mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
1 Commits
webconsole
...
moduled
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ab5ac66b6 |
9
.github/workflows/docker.yml
vendored
9
.github/workflows/docker.yml
vendored
@@ -1,11 +1,6 @@
|
|||||||
name: Build containers
|
name: Build containers
|
||||||
|
|
||||||
on:
|
on: [push]
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- openssl
|
|
||||||
tags:
|
|
||||||
- '*'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
@@ -63,8 +58,6 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
purplei2p/i2pd:latest
|
purplei2p/i2pd:latest
|
||||||
purplei2p/i2pd:latest-release
|
|
||||||
purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||||
ghcr.io/purplei2p/i2pd:latest
|
ghcr.io/purplei2p/i2pd:latest
|
||||||
ghcr.io/purplei2p/i2pd:latest-release
|
|
||||||
ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -8,15 +8,12 @@ netDb
|
|||||||
/libi2pd.a
|
/libi2pd.a
|
||||||
/libi2pdclient.a
|
/libi2pdclient.a
|
||||||
/libi2pdlang.a
|
/libi2pdlang.a
|
||||||
/libi2pdwebconsole.a
|
|
||||||
/libi2pd.so
|
/libi2pd.so
|
||||||
/libi2pdclient.so
|
/libi2pdclient.so
|
||||||
/libi2pdlang.so
|
/libi2pdlang.so
|
||||||
/libi2pdwebconsole.so
|
|
||||||
/libi2pd.dll
|
/libi2pd.dll
|
||||||
/libi2pdclient.dll
|
/libi2pdclient.dll
|
||||||
/libi2pdlang.dll
|
/libi2pdlang.dll
|
||||||
/libi2pdwebconsole.dll
|
|
||||||
*.exe
|
*.exe
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
34
ChangeLog
34
ChangeLog
@@ -1,40 +1,6 @@
|
|||||||
# for this file format description,
|
# for this file format description,
|
||||||
# see https://github.com/olivierlacan/keep-a-changelog
|
# see https://github.com/olivierlacan/keep-a-changelog
|
||||||
|
|
||||||
## [2.42.1] - 2022-05-24
|
|
||||||
### Fixed
|
|
||||||
- Incorrect jump link in HTTP Proxy
|
|
||||||
|
|
||||||
## [2.42.0] - 2022-05-22
|
|
||||||
### Added
|
|
||||||
- Preliminary SSU2 implementation
|
|
||||||
- Tunnel length variance
|
|
||||||
- Localization to French
|
|
||||||
- Daily cleanup of obsolete peer profiles
|
|
||||||
- Ordered jump services list in HTTP proxy
|
|
||||||
- Win32 service
|
|
||||||
- Show port for local non-published SSU addresses in web console
|
|
||||||
### Changed
|
|
||||||
- Maximum RouterInfo length increased to 3K
|
|
||||||
- Skip unknown addresses in RouterInfo
|
|
||||||
- Don't pick own router for peer test
|
|
||||||
- Reseeds list
|
|
||||||
- Internal numeric id for families
|
|
||||||
- Use ipv6 preference only when netinet headers not used
|
|
||||||
- Close stream if delete requested
|
|
||||||
- Remove version from title in web console
|
|
||||||
- Drop MESHNET build option
|
|
||||||
- Set data path before initialization
|
|
||||||
- Don't show registration block in web console if token is not provided
|
|
||||||
### Fixed
|
|
||||||
- Encrypted LeaseSet for EdDSA signature
|
|
||||||
- Clients tunnels are not built if clock is not synced on start
|
|
||||||
- Incorrect processing of i2cp.dontPublishLeaseSet param
|
|
||||||
- UDP tunnels reload
|
|
||||||
- Build for LibreSSL 3.5.2
|
|
||||||
- Race condition in short tunnel build message
|
|
||||||
- Race condition in local RouterInfo buffer allocation
|
|
||||||
|
|
||||||
## [2.41.0] - 2022-02-20
|
## [2.41.0] - 2022-02-20
|
||||||
### Added
|
### Added
|
||||||
- Clock syncronization through SSU
|
- Clock syncronization through SSU
|
||||||
|
|||||||
75
Makefile
75
Makefile
@@ -4,7 +4,7 @@ SYS := $(shell $(CXX) -dumpmachine)
|
|||||||
|
|
||||||
ifneq (, $(findstring darwin, $(SYS)))
|
ifneq (, $(findstring darwin, $(SYS)))
|
||||||
SHARED_SUFFIX = dylib
|
SHARED_SUFFIX = dylib
|
||||||
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS)))
|
||||||
SHARED_SUFFIX = dll
|
SHARED_SUFFIX = dll
|
||||||
else
|
else
|
||||||
SHARED_SUFFIX = so
|
SHARED_SUFFIX = so
|
||||||
@@ -12,22 +12,19 @@ endif
|
|||||||
|
|
||||||
SHLIB := libi2pd.$(SHARED_SUFFIX)
|
SHLIB := libi2pd.$(SHARED_SUFFIX)
|
||||||
ARLIB := libi2pd.a
|
ARLIB := libi2pd.a
|
||||||
SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX)
|
|
||||||
ARLIB_CLIENT := libi2pdclient.a
|
|
||||||
SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX)
|
SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX)
|
||||||
ARLIB_LANG := libi2pdlang.a
|
ARLIB_LANG := libi2pdlang.a
|
||||||
SHLIB_WEBCONSOLE := libi2pdwebconsole.$(SHARED_SUFFIX)
|
SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX)
|
||||||
ARLIB_WEBCONSOLE := libi2pdwebconsole.a
|
ARLIB_CLIENT := libi2pdclient.a
|
||||||
SHLIB_WRAP := libi2pdwrapper.$(SHARED_SUFFIX)
|
SHLIB_WRAP := libi2pdwrapper.$(SHARED_SUFFIX)
|
||||||
ARLIB_WRAP := libi2pdwrapper.a
|
ARLIB_WRAP := libi2pdwrapper.a
|
||||||
I2PD := i2pd
|
I2PD := i2pd
|
||||||
|
|
||||||
LIB_SRC_DIR := libi2pd
|
LIB_SRC_DIR := libi2pd
|
||||||
LIB_CLIENT_SRC_DIR := libi2pd_client
|
LIB_CLIENT_SRC_DIR := libi2pd_client
|
||||||
WEBCONSOLE_SRC_DIR := libi2pd_webconsole
|
WRAP_SRC_DIR := libi2pd_wrapper
|
||||||
LANG_SRC_DIR := i18n
|
LANG_SRC_DIR := i18n
|
||||||
DAEMON_SRC_DIR := daemon
|
DAEMON_SRC_DIR := daemon
|
||||||
WRAP_SRC_DIR := libi2pd_wrapper
|
|
||||||
|
|
||||||
# import source files lists
|
# import source files lists
|
||||||
include filelist.mk
|
include filelist.mk
|
||||||
@@ -43,6 +40,12 @@ USE_GIT_VERSION := $(or $(USE_GIT_VERSION),no)
|
|||||||
# for MacOS only, waiting for "1", not "yes"
|
# for MacOS only, waiting for "1", not "yes"
|
||||||
HOMEBREW := $(or $(HOMEBREW),0)
|
HOMEBREW := $(or $(HOMEBREW),0)
|
||||||
|
|
||||||
|
# Client protocols
|
||||||
|
USE_I2PC := $(or $(USE_I2PC),yes)
|
||||||
|
USE_I2CP := $(or $(USE_I2CP),yes)
|
||||||
|
USE_SAM := $(or $(USE_SAM),yes)
|
||||||
|
USE_BOB := $(or $(USE_BOB),yes)
|
||||||
|
|
||||||
ifeq ($(DEBUG),yes)
|
ifeq ($(DEBUG),yes)
|
||||||
CXX_DEBUG = -g
|
CXX_DEBUG = -g
|
||||||
else
|
else
|
||||||
@@ -50,21 +53,34 @@ else
|
|||||||
LD_DEBUG = -s
|
LD_DEBUG = -s
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(USE_I2PC),yes)
|
||||||
|
NEEDED_CXXFLAGS += -DWITH_I2PC
|
||||||
|
endif
|
||||||
|
ifeq ($(USE_I2CP),yes)
|
||||||
|
NEEDED_CXXFLAGS += -DWITH_I2CP
|
||||||
|
endif
|
||||||
|
ifeq ($(USE_SAM),yes)
|
||||||
|
NEEDED_CXXFLAGS += -DWITH_SAM
|
||||||
|
endif
|
||||||
|
ifeq ($(USE_BOB),yes)
|
||||||
|
NEEDED_CXXFLAGS += -DWITH_BOB
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (, $(findstring darwin, $(SYS)))
|
ifneq (, $(findstring darwin, $(SYS)))
|
||||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonUnix.cpp
|
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||||
ifeq ($(HOMEBREW),1)
|
ifeq ($(HOMEBREW),1)
|
||||||
include Makefile.homebrew
|
include Makefile.homebrew
|
||||||
else
|
else
|
||||||
include Makefile.osx
|
include Makefile.osx
|
||||||
endif
|
endif
|
||||||
else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS)))
|
else ifneq (, $(findstring linux, $(SYS))$(findstring gnu, $(SYS)))
|
||||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonUnix.cpp
|
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||||
include Makefile.linux
|
include Makefile.linux
|
||||||
else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
||||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonUnix.cpp
|
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||||
include Makefile.bsd
|
include Makefile.bsd
|
||||||
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS)))
|
||||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp
|
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp
|
||||||
include Makefile.mingw
|
include Makefile.mingw
|
||||||
else # not supported
|
else # not supported
|
||||||
$(error Not supported platform)
|
$(error Not supported platform)
|
||||||
@@ -75,24 +91,22 @@ ifeq ($(USE_GIT_VERSION),yes)
|
|||||||
NEEDED_CXXFLAGS += -DGITVER=\"$(GIT_VERSION)\"
|
NEEDED_CXXFLAGS += -DGITVER=\"$(GIT_VERSION)\"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR) -I$(WEBCONSOLE_SRC_DIR) -DOPENSSL_SUPPRESS_DEPRECATED
|
NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR)
|
||||||
|
|
||||||
LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||||
LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||||
LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC))
|
LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC))
|
||||||
WEBCONSOLE_OBJS += $(patsubst %.cpp,obj/%.o,$(WEBCONSOLE_SRC))
|
|
||||||
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
||||||
WRAP_LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(WRAP_LIB_SRC))
|
WRAP_LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(WRAP_LIB_SRC))
|
||||||
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(WEBCONSOLE_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) $(WRAP_LIB_OBJS:.o=.d)
|
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) $(WRAP_LIB_OBJS:.o=.d)
|
||||||
|
|
||||||
## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary
|
## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary
|
||||||
all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(ARLIB_WEBCONSOLE) $(I2PD)
|
all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD)
|
||||||
|
|
||||||
mk_obj_dir:
|
mk_obj_dir:
|
||||||
@mkdir -p obj/$(LIB_SRC_DIR)
|
@mkdir -p obj/$(LIB_SRC_DIR)
|
||||||
@mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
|
@mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
|
||||||
@mkdir -p obj/$(LANG_SRC_DIR)
|
@mkdir -p obj/$(LANG_SRC_DIR)
|
||||||
@mkdir -p obj/$(WEBCONSOLE_SRC_DIR)
|
|
||||||
@mkdir -p obj/$(DAEMON_SRC_DIR)
|
@mkdir -p obj/$(DAEMON_SRC_DIR)
|
||||||
@mkdir -p obj/$(WRAP_SRC_DIR)
|
@mkdir -p obj/$(WRAP_SRC_DIR)
|
||||||
@mkdir -p obj/Win32
|
@mkdir -p obj/Win32
|
||||||
@@ -100,8 +114,7 @@ mk_obj_dir:
|
|||||||
api: $(SHLIB) $(ARLIB)
|
api: $(SHLIB) $(ARLIB)
|
||||||
client: $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
client: $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||||
lang: $(SHLIB_LANG) $(ARLIB_LANG)
|
lang: $(SHLIB_LANG) $(ARLIB_LANG)
|
||||||
webconsole: $(SHLIB_WEBCONSOLE) $(ARLIB_WEBCONSOLE)
|
api_client: api client lang
|
||||||
api_client: api client lang webconsole
|
|
||||||
wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
|
wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
|
||||||
|
|
||||||
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
|
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
|
||||||
@@ -117,7 +130,7 @@ obj/%.o: %.cpp | mk_obj_dir
|
|||||||
# '-' is 'ignore if missing' on first run
|
# '-' is 'ignore if missing' on first run
|
||||||
-include $(DEPS)
|
-include $(DEPS)
|
||||||
|
|
||||||
$(I2PD): $(DAEMON_OBJS) $(ARLIB_WEBCONSOLE) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
|
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
|
||||||
$(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
|
$(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
|
||||||
|
|
||||||
$(SHLIB): $(LIB_OBJS) $(SHLIB_LANG)
|
$(SHLIB): $(LIB_OBJS) $(SHLIB_LANG)
|
||||||
@@ -130,17 +143,12 @@ ifneq ($(USE_STATIC),yes)
|
|||||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG)
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(SHLIB_LANG): $(LANG_OBJS)
|
$(SHLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||||
ifneq ($(USE_STATIC),yes)
|
ifneq ($(USE_STATIC),yes)
|
||||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(SHLIB_WEBCONSOLE): $(WEBCONSOLE_OBJS) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
|
$(SHLIB_LANG): $(LANG_OBJS)
|
||||||
ifneq ($(USE_STATIC),yes)
|
|
||||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
|
|
||||||
endif
|
|
||||||
|
|
||||||
$(SHLIB_WRAP): $(WRAP_LIB_OBJS)
|
|
||||||
ifneq ($(USE_STATIC),yes)
|
ifneq ($(USE_STATIC),yes)
|
||||||
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||||
endif
|
endif
|
||||||
@@ -151,21 +159,18 @@ $(ARLIB): $(LIB_OBJS)
|
|||||||
$(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
|
$(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
|
||||||
$(AR) -r $@ $^
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
$(ARLIB_LANG): $(LANG_OBJS)
|
|
||||||
$(AR) -r $@ $^
|
|
||||||
|
|
||||||
$(ARLIB_WEBCONSOLE): $(WEBCONSOLE_OBJS)
|
|
||||||
$(AR) -r $@ $^
|
|
||||||
|
|
||||||
$(ARLIB_WRAP): $(WRAP_LIB_OBJS)
|
$(ARLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||||
$(AR) -r $@ $^
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
|
$(ARLIB_LANG): $(LANG_OBJS)
|
||||||
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(RM) -r obj
|
$(RM) -r obj
|
||||||
$(RM) -r docs/generated
|
$(RM) -r docs/generated
|
||||||
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WEBCONSOLE) $(ARLIB_WEBCONSOLE) $(SHLIB_WRAP) $(ARLIB_WRAP)
|
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WRAP) $(ARLIB_WRAP)
|
||||||
|
|
||||||
strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG) $(SHLIB_WEBCONSOLE)
|
strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
|
||||||
strip $^
|
strip $^
|
||||||
|
|
||||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
||||||
|
|||||||
@@ -3,11 +3,19 @@ USE_WIN32_APP := yes
|
|||||||
|
|
||||||
WINDRES = windres
|
WINDRES = windres
|
||||||
|
|
||||||
CXXFLAGS := $(CXX_DEBUG) -fPIC -msse
|
CXXFLAGS := $(CXX_DEBUG) -DWIN32_LEAN_AND_MEAN -fPIC -msse
|
||||||
INCFLAGS = -IWin32
|
INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32
|
||||||
LDFLAGS := ${LD_DEBUG} -static
|
LDFLAGS := ${LD_DEBUG} -static
|
||||||
|
|
||||||
NEEDED_CXXFLAGS += -std=c++17 -DWIN32_LEAN_AND_MEAN
|
# detect proper flag for c++11 support by compilers
|
||||||
|
CXXVER := $(shell $(CXX) -dumpversion)
|
||||||
|
ifeq ($(shell expr match ${CXXVER} "[4]\.[7-9]\|4\.1[0-9]\|[5-6]"),4) # gcc 4.7 - 6
|
||||||
|
NEEDED_CXXFLAGS += -std=c++11
|
||||||
|
else ifeq ($(shell expr match ${CXXVER} "[1,7-9]"),1) # gcc >= 7
|
||||||
|
NEEDED_CXXFLAGS += -std=c++17
|
||||||
|
else # not supported
|
||||||
|
$(error Compiler too old)
|
||||||
|
endif
|
||||||
|
|
||||||
# Boost libraries suffix
|
# Boost libraries suffix
|
||||||
BOOST_SUFFIX = -mt
|
BOOST_SUFFIX = -mt
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
* See full license text in LICENSE file at top of project tree
|
* See full license text in LICENSE file at top of project tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <clocale>
|
#include <clocale>
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
@@ -15,6 +13,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
#include "Win32Service.h"
|
#include "Win32Service.h"
|
||||||
#ifdef WIN32_APP
|
#ifdef WIN32_APP
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@@ -39,9 +38,6 @@ namespace util
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
i2p::win32::SetIsGraceful (std::bind (&DaemonWin32::SetIsGraceful, this, std::placeholders::_1));
|
|
||||||
i2p::win32::GetIsGraceful (std::bind (&DaemonWin32::GetIsGraceful, this));
|
|
||||||
|
|
||||||
if (!Daemon_Singleton::init(argc, argv))
|
if (!Daemon_Singleton::init(argc, argv))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -49,8 +45,6 @@ namespace util
|
|||||||
{
|
{
|
||||||
LogPrint(eLogDebug, "Daemon: running as service");
|
LogPrint(eLogDebug, "Daemon: running as service");
|
||||||
I2PService service((PSTR)SERVICE_NAME);
|
I2PService service((PSTR)SERVICE_NAME);
|
||||||
service.SetDaemonStart (std::bind (&DaemonWin32::start, this));
|
|
||||||
service.SetDaemonStop (std::bind (&DaemonWin32::stop, this));
|
|
||||||
if (!I2PService::Run(service))
|
if (!I2PService::Run(service))
|
||||||
{
|
{
|
||||||
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
|
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
#include "Tunnel.h"
|
#include "Tunnel.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
#include "Daemon.h"
|
||||||
#include "Win32App.h"
|
#include "Win32App.h"
|
||||||
#include "Win32NetState.h"
|
#include "Win32NetState.h"
|
||||||
|
|
||||||
@@ -55,15 +55,13 @@ namespace win32
|
|||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
|
||||||
if(!i2p::context.AcceptsTunnels())
|
if(!i2p::context.AcceptsTunnels())
|
||||||
if(m_getIsGraceful)
|
InsertMenu (hPopup, -1,
|
||||||
if(m_getIsGraceful())
|
i2p::util::DaemonWin32::Instance ().isGraceful ? MF_BYPOSITION | MF_STRING | MF_GRAYED : MF_BYPOSITION | MF_STRING,
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING | MF_GRAYED, ID_ACCEPT_TRANSIT, "Accept &transit");
|
ID_ACCEPT_TRANSIT, "Accept &transit");
|
||||||
else
|
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ACCEPT_TRANSIT, "Accept &transit");
|
|
||||||
else
|
else
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit");
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_DECLINE_TRANSIT, "Decline &transit");
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config");
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload tunnels config");
|
||||||
if (!m_getIsGraceful)
|
if (!i2p::util::DaemonWin32::Instance ().isGraceful)
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
|
||||||
else
|
else
|
||||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "Stop &graceful shutdown");
|
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "Stop &graceful shutdown");
|
||||||
@@ -272,7 +270,7 @@ namespace win32
|
|||||||
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
|
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
|
||||||
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second
|
SetTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER, 1000, nullptr); // check tunnels every second
|
||||||
g_GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
|
g_GracefulShutdownEndtime = GetTickCount() + 10*60*1000;
|
||||||
if (m_setIsGraceful) m_setIsGraceful(true);
|
i2p::util::DaemonWin32::Instance ().isGraceful = true;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case ID_STOP_GRACEFUL_SHUTDOWN:
|
case ID_STOP_GRACEFUL_SHUTDOWN:
|
||||||
@@ -281,7 +279,7 @@ namespace win32
|
|||||||
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
|
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
|
||||||
KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER);
|
KillTimer (hWnd, IDT_GRACEFUL_TUNNELCHECK_TIMER);
|
||||||
g_GracefulShutdownEndtime = 0;
|
g_GracefulShutdownEndtime = 0;
|
||||||
if (m_setIsGraceful) m_setIsGraceful(false);
|
i2p::util::DaemonWin32::Instance ().isGraceful = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
case ID_RELOAD:
|
case ID_RELOAD:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
@@ -22,15 +22,6 @@ namespace win32
|
|||||||
int RunWin32App ();
|
int RunWin32App ();
|
||||||
bool GracefulShutdown ();
|
bool GracefulShutdown ();
|
||||||
bool StopGracefulShutdown ();
|
bool StopGracefulShutdown ();
|
||||||
|
|
||||||
inline typedef std::function<void (bool)> DaemonSetIsGraceful;
|
|
||||||
inline DaemonSetIsGraceful m_setIsGraceful;
|
|
||||||
inline void SetIsGraceful (const DaemonSetIsGraceful& f) { m_setIsGraceful = f; };
|
|
||||||
|
|
||||||
inline typedef std::function<bool ()> DaemonGetIsGraceful;
|
|
||||||
inline DaemonGetIsGraceful m_getIsGraceful;
|
|
||||||
inline void GetIsGraceful (const DaemonGetIsGraceful& f) { m_getIsGraceful = f; };
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // WIN32APP_H__
|
#endif // WIN32APP_H__
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "Daemon.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
|
||||||
I2PService *I2PService::s_service = NULL;
|
I2PService *I2PService::s_service = NULL;
|
||||||
@@ -131,13 +132,7 @@ void I2PService::Start(DWORD dwArgc, PSTR *pszArgv)
|
|||||||
void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv)
|
void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv)
|
||||||
{
|
{
|
||||||
LogPrint(eLogInfo, "Win32Service: in OnStart (", EVENTLOG_INFORMATION_TYPE, ")");
|
LogPrint(eLogInfo, "Win32Service: in OnStart (", EVENTLOG_INFORMATION_TYPE, ")");
|
||||||
if(m_daemonStart)
|
Daemon.start();
|
||||||
m_daemonStart();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint(eLogError, "Win32Service: failed to start: Unable to call callback");
|
|
||||||
SetServiceStatus(SERVICE_STOPPED);
|
|
||||||
}
|
|
||||||
_worker = new std::thread(std::bind(&I2PService::WorkerThread, this));
|
_worker = new std::thread(std::bind(&I2PService::WorkerThread, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,11 +171,7 @@ void I2PService::OnStop()
|
|||||||
{
|
{
|
||||||
// Log a service stop message to the Application log.
|
// Log a service stop message to the Application log.
|
||||||
LogPrint(eLogInfo, "Win32Service: in OnStop (", EVENTLOG_INFORMATION_TYPE, ")");
|
LogPrint(eLogInfo, "Win32Service: in OnStop (", EVENTLOG_INFORMATION_TYPE, ")");
|
||||||
if(m_daemonStop)
|
Daemon.stop();
|
||||||
m_daemonStop();
|
|
||||||
else
|
|
||||||
LogPrint(eLogError, "Win32Service: failed to stop: Unable to call callback");
|
|
||||||
|
|
||||||
m_fStopping = TRUE;
|
m_fStopping = TRUE;
|
||||||
if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
|
if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,10 +6,9 @@
|
|||||||
* See full license text in LICENSE file at top of project tree
|
* See full license text in LICENSE file at top of project tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef WIN32SERVICE_H__
|
#ifndef WIN_32_SERVICE_H__
|
||||||
#define WIN32SERVICE_H__
|
#define WIN_32_SERVICE_H__
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
@@ -30,13 +29,6 @@ class I2PService
|
|||||||
static BOOL Run(I2PService &service);
|
static BOOL Run(I2PService &service);
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
typedef std::function<bool ()> DaemonStart;
|
|
||||||
void SetDaemonStart (const DaemonStart& f) { m_daemonStart = f; };
|
|
||||||
|
|
||||||
typedef std::function<bool ()> DaemonStop;
|
|
||||||
void SetDaemonStop (const DaemonStop& f) { m_daemonStop = f; };
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void OnStart(DWORD dwArgc, PSTR *pszArgv);
|
virtual void OnStart(DWORD dwArgc, PSTR *pszArgv);
|
||||||
@@ -66,11 +58,6 @@ class I2PService
|
|||||||
HANDLE m_hStoppedEvent;
|
HANDLE m_hStoppedEvent;
|
||||||
|
|
||||||
std::thread* _worker;
|
std::thread* _worker;
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
DaemonStart m_daemonStart;
|
|
||||||
DaemonStop m_daemonStop;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WIN32SERVICE_H__
|
#endif // WIN_32_SERVICE_H__
|
||||||
|
|||||||
@@ -33,13 +33,11 @@ target_architecture(ARCHITECTURE)
|
|||||||
|
|
||||||
set(LIBI2PD_SRC_DIR ../libi2pd)
|
set(LIBI2PD_SRC_DIR ../libi2pd)
|
||||||
set(LIBI2PD_CLIENT_SRC_DIR ../libi2pd_client)
|
set(LIBI2PD_CLIENT_SRC_DIR ../libi2pd_client)
|
||||||
set(WEBCONSOLE_SRC_DIR ../libi2pd_webconsole)
|
|
||||||
set(LANG_SRC_DIR ../i18n)
|
set(LANG_SRC_DIR ../i18n)
|
||||||
set(DAEMON_SRC_DIR ../daemon)
|
set(DAEMON_SRC_DIR ../daemon)
|
||||||
|
|
||||||
include_directories(${LIBI2PD_SRC_DIR})
|
include_directories(${LIBI2PD_SRC_DIR})
|
||||||
include_directories(${LIBI2PD_CLIENT_SRC_DIR})
|
include_directories(${LIBI2PD_CLIENT_SRC_DIR})
|
||||||
include_directories(${WEBCONSOLE_SRC_DIR})
|
|
||||||
include_directories(${LANG_SRC_DIR})
|
include_directories(${LANG_SRC_DIR})
|
||||||
include_directories(${DAEMON_SRC_DIR})
|
include_directories(${DAEMON_SRC_DIR})
|
||||||
|
|
||||||
@@ -70,18 +68,6 @@ if(WITH_LIBRARY)
|
|||||||
COMPONENT Libraries)
|
COMPONENT Libraries)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
FILE(GLOB WEBCONSOLE_SRC ${WEBCONSOLE_SRC_DIR}/*.cpp)
|
|
||||||
add_library(libi2pdwebconsole ${WEBCONSOLE_SRC})
|
|
||||||
set_target_properties(libi2pdwebconsole PROPERTIES PREFIX "")
|
|
||||||
|
|
||||||
if(WITH_LIBRARY)
|
|
||||||
install(TARGETS libi2pdwebconsole
|
|
||||||
EXPORT libi2pdwebconsole
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
COMPONENT Libraries)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
FILE(GLOB LANG_SRC ${LANG_SRC_DIR}/*.cpp)
|
FILE(GLOB LANG_SRC ${LANG_SRC_DIR}/*.cpp)
|
||||||
add_library(libi2pdlang ${LANG_SRC})
|
add_library(libi2pdlang ${LANG_SRC})
|
||||||
set_target_properties(libi2pdlang PROPERTIES PREFIX "")
|
set_target_properties(libi2pdlang PROPERTIES PREFIX "")
|
||||||
@@ -96,6 +82,7 @@ endif()
|
|||||||
|
|
||||||
set(DAEMON_SRC
|
set(DAEMON_SRC
|
||||||
"${DAEMON_SRC_DIR}/Daemon.cpp"
|
"${DAEMON_SRC_DIR}/Daemon.cpp"
|
||||||
|
"${DAEMON_SRC_DIR}/HTTPServer.cpp"
|
||||||
"${DAEMON_SRC_DIR}/I2PControl.cpp"
|
"${DAEMON_SRC_DIR}/I2PControl.cpp"
|
||||||
"${DAEMON_SRC_DIR}/i2pd.cpp"
|
"${DAEMON_SRC_DIR}/i2pd.cpp"
|
||||||
"${DAEMON_SRC_DIR}/UPnP.cpp"
|
"${DAEMON_SRC_DIR}/UPnP.cpp"
|
||||||
@@ -148,7 +135,7 @@ endif()
|
|||||||
|
|
||||||
# compiler flags customization(by system)
|
# compiler flags customization(by system)
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
list(APPEND DAEMON_SRC "${DAEMON_SRC_DIR}/DaemonUnix.cpp")
|
list(APPEND DAEMON_SRC "${DAEMON_SRC_DIR}/UnixDaemon.cpp")
|
||||||
if(NOT(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR APPLE))
|
if(NOT(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR APPLE))
|
||||||
# "'sleep_for' is not a member of 'std::this_thread'" in gcc 4.7/4.8
|
# "'sleep_for' is not a member of 'std::this_thread'" in gcc 4.7/4.8
|
||||||
add_definitions("-D_GLIBCXX_USE_NANOSLEEP=1")
|
add_definitions("-D_GLIBCXX_USE_NANOSLEEP=1")
|
||||||
@@ -178,8 +165,20 @@ endif()
|
|||||||
|
|
||||||
|
|
||||||
# libraries
|
# libraries
|
||||||
|
# TODO: once CMake 3.1+ becomes mainstream, see e.g. http://stackoverflow.com/a/29871891/673826
|
||||||
|
# use imported Threads::Threads instead
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
find_package(Threads REQUIRED)
|
if(IOS)
|
||||||
|
set(CMAKE_THREAD_LIBS_INIT "-lpthread")
|
||||||
|
set(CMAKE_HAVE_THREADS_LIBRARY 1)
|
||||||
|
set(CMAKE_USE_WIN32_THREADS_INIT 0)
|
||||||
|
set(CMAKE_USE_PTHREADS_INIT 1)
|
||||||
|
else()
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
endif()
|
||||||
|
if(THREADS_HAVE_PTHREAD_ARG) # compile time flag
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WITH_STATIC)
|
if(WITH_STATIC)
|
||||||
set(Boost_USE_STATIC_LIBS ON)
|
set(Boost_USE_STATIC_LIBS ON)
|
||||||
@@ -198,7 +197,7 @@ else()
|
|||||||
add_definitions(-DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_REGEX_DYN_LINK)
|
add_definitions(-DBOOST_SYSTEM_DYN_LINK -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_DATE_TIME_DYN_LINK -DBOOST_REGEX_DYN_LINK)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(libi2pdwebconsole libi2pdclient libi2pd libi2pdlang)
|
target_link_libraries(libi2pdclient libi2pd libi2pdlang)
|
||||||
|
|
||||||
find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED)
|
find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED)
|
||||||
if(NOT DEFINED Boost_INCLUDE_DIRS)
|
if(NOT DEFINED Boost_INCLUDE_DIRS)
|
||||||
@@ -211,7 +210,7 @@ if(NOT DEFINED OPENSSL_INCLUDE_DIR)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(OPENSSL_VERSION VERSION_GREATER_EQUAL "3.0.0")
|
if(OPENSSL_VERSION VERSION_GREATER_EQUAL "3.0.0")
|
||||||
add_definitions(-DOPENSSL_SUPPRESS_DEPRECATED)
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_UPNP)
|
if(WITH_UPNP)
|
||||||
@@ -279,7 +278,7 @@ if(WITH_BINARY)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(libi2pd ${Boost_LIBRARIES} ${ZLIB_LIBRARY})
|
target_link_libraries(libi2pd ${Boost_LIBRARIES} ${ZLIB_LIBRARY})
|
||||||
target_link_libraries("${PROJECT_NAME}" libi2pdwebconsole libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${UPNP_LIB} ${ZLIB_LIBRARY} Threads::Threads ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES})
|
target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${UPNP_LIB} ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES})
|
||||||
|
|
||||||
install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime)
|
install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime)
|
||||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
|
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
|
||||||
|
|||||||
@@ -52,8 +52,9 @@ REM converting configuration files to DOS format (make usable in Windows Notepad
|
|||||||
%xSH% "unix2dos contrib/i2pd.conf contrib/tunnels.conf contrib/tunnels.d/* contrib/webconsole/style.css" >> build\build.log 2>&1
|
%xSH% "unix2dos contrib/i2pd.conf contrib/tunnels.conf contrib/tunnels.d/* contrib/webconsole/style.css" >> build\build.log 2>&1
|
||||||
|
|
||||||
REM Prepare binary signing command if signing key and password provided
|
REM Prepare binary signing command if signing key and password provided
|
||||||
if defined SIGN (
|
if defined SIGNKEY (
|
||||||
echo Signing enabled
|
if defined SIGNPASS (
|
||||||
|
echo Signing options found
|
||||||
|
|
||||||
for %%X in (signtool.exe) do (set xSIGNTOOL=%%~$PATH:X)
|
for %%X in (signtool.exe) do (set xSIGNTOOL=%%~$PATH:X)
|
||||||
if not defined xSIGNTOOL (
|
if not defined xSIGNTOOL (
|
||||||
@@ -65,15 +66,8 @@ if defined SIGN (
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if defined SIGNKEY (
|
set "xSIGNOPTS=sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 /f ^"%SIGNKEY%^" /p ^"%SIGNPASS%^""
|
||||||
set "xSIGNKEYOPTS=/f ^"%SIGNKEY%^""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if defined SIGNPASS (
|
|
||||||
set "xSIGNPASSOPTS=/p ^"%SIGNPASS%^""
|
|
||||||
)
|
|
||||||
|
|
||||||
set "xSIGNOPTS=sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 %xSIGNKEYOPTS% %xSIGNPASSOPTS%"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
REM starting building
|
REM starting building
|
||||||
|
|||||||
@@ -110,8 +110,8 @@ port = 7070
|
|||||||
# user = i2pd
|
# user = i2pd
|
||||||
# pass = changeme
|
# pass = changeme
|
||||||
## Select webconsole language
|
## Select webconsole language
|
||||||
## Currently supported english (default), afrikaans, armenian, french, german,
|
## Currently supported english (default), afrikaans, armenian, german, russian,
|
||||||
## russian, turkmen, ukrainian and uzbek languages
|
## turkmen, ukrainian and uzbek languages
|
||||||
# lang = english
|
# lang = english
|
||||||
|
|
||||||
[httpproxy]
|
[httpproxy]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
%define git_hash %(git rev-parse HEAD | cut -c -7)
|
%define git_hash %(git rev-parse HEAD | cut -c -7)
|
||||||
|
|
||||||
Name: i2pd-git
|
Name: i2pd-git
|
||||||
Version: 2.42.1
|
Version: 2.41.0
|
||||||
Release: git%{git_hash}%{?dist}
|
Release: git%{git_hash}%{?dist}
|
||||||
Summary: I2P router written in C++
|
Summary: I2P router written in C++
|
||||||
Conflicts: i2pd
|
Conflicts: i2pd
|
||||||
@@ -164,12 +164,6 @@ getent passwd i2pd >/dev/null || \
|
|||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Tue May 24 2022 r4sas <r4sas@i2pmail.org> - 2.42.1
|
|
||||||
- update to 2.42.1
|
|
||||||
|
|
||||||
* Sun May 22 2022 orignal <orignal@i2pmail.org> - 2.42.0
|
|
||||||
- update to 2.42.0
|
|
||||||
|
|
||||||
* Sun Feb 20 2022 r4sas <r4sas@i2pmail.org> - 2.41.0
|
* Sun Feb 20 2022 r4sas <r4sas@i2pmail.org> - 2.41.0
|
||||||
- update to 2.41.0
|
- update to 2.41.0
|
||||||
- fixed build on Fedora Copr over openssl trunk code
|
- fixed build on Fedora Copr over openssl trunk code
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Name: i2pd
|
Name: i2pd
|
||||||
Version: 2.42.1
|
Version: 2.41.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: I2P router written in C++
|
Summary: I2P router written in C++
|
||||||
Conflicts: i2pd-git
|
Conflicts: i2pd-git
|
||||||
@@ -161,12 +161,6 @@ getent passwd i2pd >/dev/null || \
|
|||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Tue May 24 2022 r4sas <r4sas@i2pmail.org> - 2.42.1
|
|
||||||
- update to 2.42.1
|
|
||||||
|
|
||||||
* Sun May 22 2022 orignal <orignal@i2pmail.org> - 2.42.0
|
|
||||||
- update to 2.42.0
|
|
||||||
|
|
||||||
* Sun Feb 20 2022 r4sas <r4sas@i2pmail.org> - 2.41.0
|
* Sun Feb 20 2022 r4sas <r4sas@i2pmail.org> - 2.41.0
|
||||||
- update to 2.41.0
|
- update to 2.41.0
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,9 @@
|
|||||||
#include "Streaming.h"
|
#include "Streaming.h"
|
||||||
#include "Destination.h"
|
#include "Destination.h"
|
||||||
#include "HTTPServer.h"
|
#include "HTTPServer.h"
|
||||||
|
#ifdef WITH_I2PC
|
||||||
#include "I2PControl.h"
|
#include "I2PControl.h"
|
||||||
|
#endif
|
||||||
#include "ClientContext.h"
|
#include "ClientContext.h"
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
#include "UPnP.h"
|
#include "UPnP.h"
|
||||||
@@ -45,7 +47,9 @@ namespace util
|
|||||||
~Daemon_Singleton_Private() {};
|
~Daemon_Singleton_Private() {};
|
||||||
|
|
||||||
std::unique_ptr<i2p::http::HTTPServer> httpServer;
|
std::unique_ptr<i2p::http::HTTPServer> httpServer;
|
||||||
|
#ifdef WITH_I2PC
|
||||||
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
|
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
|
||||||
|
#endif
|
||||||
std::unique_ptr<i2p::transport::UPnP> UPnP;
|
std::unique_ptr<i2p::transport::UPnP> UPnP;
|
||||||
std::unique_ptr<i2p::util::NTPTimeSync> m_NTPSync;
|
std::unique_ptr<i2p::util::NTPTimeSync> m_NTPSync;
|
||||||
};
|
};
|
||||||
@@ -58,7 +62,9 @@ namespace util
|
|||||||
bool Daemon_Singleton::IsService () const
|
bool Daemon_Singleton::IsService () const
|
||||||
{
|
{
|
||||||
bool service = false;
|
bool service = false;
|
||||||
|
#ifndef _WIN32
|
||||||
i2p::config::GetOption("service", service);
|
i2p::config::GetOption("service", service);
|
||||||
|
#endif
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,10 +429,6 @@ namespace util
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
d.httpServer = std::unique_ptr<i2p::http::HTTPServer>(new i2p::http::HTTPServer(httpAddr, httpPort));
|
d.httpServer = std::unique_ptr<i2p::http::HTTPServer>(new i2p::http::HTTPServer(httpAddr, httpPort));
|
||||||
d.httpServer->SetDaemonStop (std::bind (Daemon_Singleton::stop, this));
|
|
||||||
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
|
||||||
d.httpServer->SetDaemonGracefulTimer (std::bind (gracefulShutdownInterval, this));
|
|
||||||
#endif
|
|
||||||
d.httpServer->Start();
|
d.httpServer->Start();
|
||||||
}
|
}
|
||||||
catch (std::exception& ex)
|
catch (std::exception& ex)
|
||||||
@@ -443,6 +445,7 @@ namespace util
|
|||||||
LogPrint(eLogInfo, "Daemon: Starting Client");
|
LogPrint(eLogInfo, "Daemon: Starting Client");
|
||||||
i2p::client::context.Start ();
|
i2p::client::context.Start ();
|
||||||
|
|
||||||
|
#ifdef WITH_I2PC
|
||||||
// I2P Control Protocol
|
// I2P Control Protocol
|
||||||
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
|
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
|
||||||
if (i2pcontrol) {
|
if (i2pcontrol) {
|
||||||
@@ -460,6 +463,7 @@ namespace util
|
|||||||
ThrowFatal ("Unable to start I2PControl service at ", i2pcpAddr, ":", i2pcpPort, ": ", ex.what ());
|
ThrowFatal ("Unable to start I2PControl service at ", i2pcpAddr, ":", i2pcpPort, ": ", ex.what ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,12 +496,14 @@ namespace util
|
|||||||
d.httpServer->Stop();
|
d.httpServer->Stop();
|
||||||
d.httpServer = nullptr;
|
d.httpServer = nullptr;
|
||||||
}
|
}
|
||||||
|
#ifdef WITH_I2PC
|
||||||
if (d.m_I2PControlService)
|
if (d.m_I2PControlService)
|
||||||
{
|
{
|
||||||
LogPrint(eLogInfo, "Daemon: Stopping I2PControl");
|
LogPrint(eLogInfo, "Daemon: Stopping I2PControl");
|
||||||
d.m_I2PControlService->Stop ();
|
d.m_I2PControlService->Stop ();
|
||||||
d.m_I2PControlService = nullptr;
|
d.m_I2PControlService = nullptr;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
i2p::crypto::TerminateCrypto ();
|
i2p::crypto::TerminateCrypto ();
|
||||||
i2p::log::Logger().Stop();
|
i2p::log::Logger().Stop();
|
||||||
|
|
||||||
|
|||||||
@@ -75,17 +75,14 @@ namespace util
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init (int argc, char* argv[]);
|
bool init(int argc, char* argv[]);
|
||||||
bool start ();
|
bool start();
|
||||||
bool stop ();
|
bool stop();
|
||||||
void run ();
|
void run ();
|
||||||
|
|
||||||
void SetIsGraceful (bool state) { m_isGraceful = state; };
|
bool isGraceful;
|
||||||
const bool GetIsGraceful () { return m_isGraceful; };
|
|
||||||
|
|
||||||
private:
|
DaemonWin32 ():isGraceful(false) {}
|
||||||
|
|
||||||
bool m_isGraceful;
|
|
||||||
};
|
};
|
||||||
#elif (defined(ANDROID) && !defined(ANDROID_BINARY))
|
#elif (defined(ANDROID) && !defined(ANDROID_BINARY))
|
||||||
#define Daemon i2p::util::DaemonAndroid::Instance()
|
#define Daemon i2p::util::DaemonAndroid::Instance()
|
||||||
@@ -112,8 +109,8 @@ namespace util
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool start ();
|
bool start();
|
||||||
bool stop ();
|
bool stop();
|
||||||
void run ();
|
void run ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -27,16 +27,14 @@
|
|||||||
#include "RouterContext.h"
|
#include "RouterContext.h"
|
||||||
#include "ClientContext.h"
|
#include "ClientContext.h"
|
||||||
#include "HTTPServer.h"
|
#include "HTTPServer.h"
|
||||||
|
#include "Daemon.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "ECIESX25519AEADRatchetSession.h"
|
#include "ECIESX25519AEADRatchetSession.h"
|
||||||
#include "I18N.h"
|
#include "I18N.h"
|
||||||
|
|
||||||
//#ifdef WIN32_APP
|
#ifdef WIN32_APP
|
||||||
//#include "Win32App.h"
|
#include "Win32App.h"
|
||||||
//#endif
|
#endif
|
||||||
|
|
||||||
// Inja template engine
|
|
||||||
#include "inja/inja.hpp"
|
|
||||||
|
|
||||||
// For image, style and info
|
// For image, style and info
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
@@ -60,7 +58,7 @@ namespace http {
|
|||||||
static void GetStyles (std::stringstream& s)
|
static void GetStyles (std::stringstream& s)
|
||||||
{
|
{
|
||||||
if (externalCSS.length() != 0)
|
if (externalCSS.length() != 0)
|
||||||
s << externalCSS;
|
s << "<style>\r\n" << externalCSS << "</style>\r\n";
|
||||||
else
|
else
|
||||||
s << internalCSS;
|
s << internalCSS;
|
||||||
}
|
}
|
||||||
@@ -70,9 +68,13 @@ namespace http {
|
|||||||
const char HTTP_PAGE_TRANSPORTS[] = "transports";
|
const char HTTP_PAGE_TRANSPORTS[] = "transports";
|
||||||
const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations";
|
const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations";
|
||||||
const char HTTP_PAGE_LOCAL_DESTINATION[] = "local_destination";
|
const char HTTP_PAGE_LOCAL_DESTINATION[] = "local_destination";
|
||||||
|
#ifdef WITH_I2CP
|
||||||
const char HTTP_PAGE_I2CP_LOCAL_DESTINATION[] = "i2cp_local_destination";
|
const char HTTP_PAGE_I2CP_LOCAL_DESTINATION[] = "i2cp_local_destination";
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_SAM
|
||||||
const char HTTP_PAGE_SAM_SESSIONS[] = "sam_sessions";
|
const char HTTP_PAGE_SAM_SESSIONS[] = "sam_sessions";
|
||||||
const char HTTP_PAGE_SAM_SESSION[] = "sam_session";
|
const char HTTP_PAGE_SAM_SESSION[] = "sam_session";
|
||||||
|
#endif
|
||||||
const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels";
|
const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels";
|
||||||
const char HTTP_PAGE_COMMANDS[] = "commands";
|
const char HTTP_PAGE_COMMANDS[] = "commands";
|
||||||
const char HTTP_PAGE_LEASESETS[] = "leasesets";
|
const char HTTP_PAGE_LEASESETS[] = "leasesets";
|
||||||
@@ -89,7 +91,9 @@ namespace http {
|
|||||||
const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string";
|
const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string";
|
||||||
const char HTTP_COMMAND_SETLANGUAGE[] = "setlanguage";
|
const char HTTP_COMMAND_SETLANGUAGE[] = "setlanguage";
|
||||||
const char HTTP_COMMAND_RELOAD_CSS[] = "reload_css";
|
const char HTTP_COMMAND_RELOAD_CSS[] = "reload_css";
|
||||||
|
#ifdef WITH_SAM
|
||||||
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
|
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
|
||||||
|
#endif
|
||||||
const char HTTP_PARAM_ADDRESS[] = "address";
|
const char HTTP_PARAM_ADDRESS[] = "address";
|
||||||
|
|
||||||
static std::string ConvertTime (uint64_t time)
|
static std::string ConvertTime (uint64_t time)
|
||||||
@@ -184,11 +188,9 @@ namespace http {
|
|||||||
" <meta charset=\"UTF-8\">\r\n"
|
" <meta charset=\"UTF-8\">\r\n"
|
||||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n"
|
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n"
|
||||||
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
|
" <link rel=\"shortcut icon\" href=\"" << itoopieFavicon << "\">\r\n"
|
||||||
" <title>Purple I2P Webconsole</title>\r\n"
|
" <title>Purple I2P Webconsole</title>\r\n";
|
||||||
" <style>";
|
|
||||||
GetStyles(s);
|
GetStyles(s);
|
||||||
s <<
|
s <<
|
||||||
"\r\n</style>\r\n"
|
|
||||||
"</head>\r\n"
|
"</head>\r\n"
|
||||||
"<body>\r\n"
|
"<body>\r\n"
|
||||||
"<div class=\"header\">" << tr("<b>i2pd</b> webconsole") << "</div>\r\n"
|
"<div class=\"header\">" << tr("<b>i2pd</b> webconsole") << "</div>\r\n"
|
||||||
@@ -206,8 +208,10 @@ namespace http {
|
|||||||
s <<
|
s <<
|
||||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a><br>\r\n"
|
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a><br>\r\n"
|
||||||
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a><br>\r\n";
|
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a><br>\r\n";
|
||||||
|
#ifdef WITH_SAM
|
||||||
if (i2p::client::context.GetSAMBridge ())
|
if (i2p::client::context.GetSAMBridge ())
|
||||||
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a><br>\r\n";
|
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a><br>\r\n";
|
||||||
|
#endif
|
||||||
s <<
|
s <<
|
||||||
"</div>\r\n"
|
"</div>\r\n"
|
||||||
"<div class=\"content\">";
|
"<div class=\"content\">";
|
||||||
@@ -272,22 +276,23 @@ namespace http {
|
|||||||
ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
|
ShowNetworkStatus (s, i2p::context.GetStatusV6 ());
|
||||||
s << "<br>\r\n";
|
s << "<br>\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: rewrite timer access
|
|
||||||
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
||||||
if (auto remains = m_DaemonGracefulTimer) {
|
if (auto remains = Daemon.gracefulShutdownInterval) {
|
||||||
s << "<b>" << tr("Stopping in") << ":</b> ";
|
s << "<b>" << tr("Stopping in") << ":</b> ";
|
||||||
ShowUptime(s, remains);
|
ShowUptime(s, remains);
|
||||||
s << "<br>\r\n";
|
s << "<br>\r\n";
|
||||||
}
|
}
|
||||||
/*#elif defined(WIN32_APP)
|
#elif defined(WIN32_APP)
|
||||||
if (i2p::win32::g_GracefulShutdownEndtime != 0) {
|
if (i2p::win32::g_GracefulShutdownEndtime != 0) {
|
||||||
uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000;
|
uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000;
|
||||||
s << "<b>" << tr("Stopping in") << ":</b> ";
|
s << "<b>" << tr("Stopping in") << ":</b> ";
|
||||||
ShowUptime(s, remains);
|
ShowUptime(s, remains);
|
||||||
s << "<br>\r\n";
|
s << "<br>\r\n";
|
||||||
}*/
|
}
|
||||||
#endif
|
#endif
|
||||||
|
auto family = i2p::context.GetFamily ();
|
||||||
|
if (family.length () > 0)
|
||||||
|
s << "<b>"<< tr("Family") << ":</b> " << family << "<br>\r\n";
|
||||||
s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
|
s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
|
||||||
s << "<b>" << tr("Received") << ":</b> ";
|
s << "<b>" << tr("Received") << ":</b> ";
|
||||||
ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
|
ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
|
||||||
@@ -364,17 +369,25 @@ namespace http {
|
|||||||
if (outputFormat==OutputFormatEnum::forWebConsole) {
|
if (outputFormat==OutputFormatEnum::forWebConsole) {
|
||||||
bool httpproxy = i2p::client::context.GetHttpProxy () ? true : false;
|
bool httpproxy = i2p::client::context.GetHttpProxy () ? true : false;
|
||||||
bool socksproxy = i2p::client::context.GetSocksProxy () ? true : false;
|
bool socksproxy = i2p::client::context.GetSocksProxy () ? true : false;
|
||||||
bool bob = i2p::client::context.GetBOBCommandChannel () ? true : false;
|
|
||||||
bool sam = i2p::client::context.GetSAMBridge () ? true : false;
|
|
||||||
bool i2cp = i2p::client::context.GetI2CPServer () ? true : false;
|
|
||||||
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
|
|
||||||
s << "<table class=\"services\"><caption>" << tr("Services") << "</caption><tbody>\r\n";
|
s << "<table class=\"services\"><caption>" << tr("Services") << "</caption><tbody>\r\n";
|
||||||
s << "<tr><td>" << "HTTP " << tr("Proxy") << "</td><td class='" << (httpproxy ? "enabled" : "disabled") << "'>" << (httpproxy ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
s << "<tr><td>" << "HTTP " << tr("Proxy") << "</td><td class='" << (httpproxy ? "enabled" : "disabled") << "'>" << (httpproxy ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
||||||
s << "<tr><td>" << "SOCKS " << tr("Proxy") << "</td><td class='" << (socksproxy ? "enabled" : "disabled") << "'>" << (socksproxy ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
s << "<tr><td>" << "SOCKS " << tr("Proxy") << "</td><td class='" << (socksproxy ? "enabled" : "disabled") << "'>" << (socksproxy ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
||||||
|
#ifdef WITH_BOB
|
||||||
|
bool bob = i2p::client::context.GetBOBCommandChannel () ? true : false;
|
||||||
s << "<tr><td>" << "BOB" << "</td><td class='" << (bob ? "enabled" : "disabled") << "'>" << (bob ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
s << "<tr><td>" << "BOB" << "</td><td class='" << (bob ? "enabled" : "disabled") << "'>" << (bob ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_SAM
|
||||||
|
bool sam = i2p::client::context.GetSAMBridge () ? true : false;
|
||||||
s << "<tr><td>" << "SAM" << "</td><td class='" << (sam ? "enabled" : "disabled") << "'>" << (sam ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
s << "<tr><td>" << "SAM" << "</td><td class='" << (sam ? "enabled" : "disabled") << "'>" << (sam ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_I2CP
|
||||||
|
bool i2cp = i2p::client::context.GetI2CPServer () ? true : false;
|
||||||
s << "<tr><td>" << "I2CP" << "</td><td class='" << (i2cp ? "enabled" : "disabled") << "'>" << (i2cp ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
s << "<tr><td>" << "I2CP" << "</td><td class='" << (i2cp ? "enabled" : "disabled") << "'>" << (i2cp ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_I2PC
|
||||||
|
bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol);
|
||||||
s << "<tr><td>" << "I2PControl" << "</td><td class='" << (i2pcontrol ? "enabled" : "disabled") << "'>" << (i2pcontrol ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
s << "<tr><td>" << "I2PControl" << "</td><td class='" << (i2pcontrol ? "enabled" : "disabled") << "'>" << (i2pcontrol ? tr("Enabled") : tr("Disabled")) << "</td></tr>\r\n";
|
||||||
|
#endif
|
||||||
s << "</tbody></table>\r\n";
|
s << "</tbody></table>\r\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -391,6 +404,7 @@ namespace http {
|
|||||||
}
|
}
|
||||||
s << "</div>\r\n";
|
s << "</div>\r\n";
|
||||||
|
|
||||||
|
#ifdef WITH_I2CP
|
||||||
auto i2cpServer = i2p::client::context.GetI2CPServer ();
|
auto i2cpServer = i2p::client::context.GetI2CPServer ();
|
||||||
if (i2cpServer && !(i2cpServer->GetSessions ().empty ()))
|
if (i2cpServer && !(i2cpServer->GetSessions ().empty ()))
|
||||||
{
|
{
|
||||||
@@ -408,6 +422,7 @@ namespace http {
|
|||||||
}
|
}
|
||||||
s << "</div>\r\n";
|
s << "</div>\r\n";
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest, uint32_t token)
|
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest, uint32_t token)
|
||||||
@@ -422,7 +437,7 @@ namespace http {
|
|||||||
s << "</div>\r\n</div>\r\n";
|
s << "</div>\r\n</div>\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dest->IsPublic() && token && !dest->IsEncryptedLeaseSet ())
|
if (dest->IsPublic() && token)
|
||||||
{
|
{
|
||||||
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
|
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
|
||||||
auto base32 = dest->GetIdentHash ().ToBase32 ();
|
auto base32 = dest->GetIdentHash ().ToBase32 ();
|
||||||
@@ -575,6 +590,7 @@ namespace http {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_I2CP
|
||||||
void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
|
void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
|
||||||
{
|
{
|
||||||
auto i2cpServer = i2p::client::context.GetI2CPServer ();
|
auto i2cpServer = i2p::client::context.GetI2CPServer ();
|
||||||
@@ -590,6 +606,7 @@ namespace http {
|
|||||||
else
|
else
|
||||||
ShowError(s, tr("I2CP is not enabled"));
|
ShowError(s, tr("I2CP is not enabled"));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void ShowLeasesSets(std::stringstream& s)
|
void ShowLeasesSets(std::stringstream& s)
|
||||||
{
|
{
|
||||||
@@ -713,12 +730,11 @@ namespace http {
|
|||||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
|
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
|
||||||
else
|
else
|
||||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
|
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
|
||||||
/*#elif defined(WIN32_APP)
|
#elif defined(WIN32_APP)
|
||||||
if (i2p::util::DaemonWin32::Instance().isGraceful)
|
if (i2p::util::DaemonWin32::Instance().isGraceful)
|
||||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
|
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
|
||||||
else
|
else
|
||||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
|
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
|
||||||
*/
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">" << tr("Force shutdown") << "</a><br><br>\r\n";
|
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">" << tr("Force shutdown") << "</a><br><br>\r\n";
|
||||||
@@ -883,6 +899,7 @@ namespace http {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_SAM
|
||||||
void ShowSAMSessions (std::stringstream& s)
|
void ShowSAMSessions (std::stringstream& s)
|
||||||
{
|
{
|
||||||
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
|
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
|
||||||
@@ -945,6 +962,7 @@ namespace http {
|
|||||||
}
|
}
|
||||||
s << "</div>\r\n";
|
s << "</div>\r\n";
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void ShowI2PTunnels (std::stringstream& s)
|
void ShowI2PTunnels (std::stringstream& s)
|
||||||
{
|
{
|
||||||
@@ -1023,8 +1041,8 @@ namespace http {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket, HTTPServer& server):
|
HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
|
||||||
m_Socket (socket), m_Server (server), m_BufferLen (0), expected_host(hostname)
|
m_Socket (socket), m_BufferLen (0), expected_host(hostname)
|
||||||
{
|
{
|
||||||
/* cache options */
|
/* cache options */
|
||||||
i2p::config::GetOption("http.auth", needAuth);
|
i2p::config::GetOption("http.auth", needAuth);
|
||||||
@@ -1198,12 +1216,16 @@ namespace http {
|
|||||||
uint32_t token = CreateToken ();
|
uint32_t token = CreateToken ();
|
||||||
ShowLocalDestination (s, params["b32"], token);
|
ShowLocalDestination (s, params["b32"], token);
|
||||||
}
|
}
|
||||||
|
#ifdef WITH_I2CP
|
||||||
else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION)
|
else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION)
|
||||||
ShowI2CPLocalDestination (s, params["i2cp_id"]);
|
ShowI2CPLocalDestination (s, params["i2cp_id"]);
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_SAM
|
||||||
else if (page == HTTP_PAGE_SAM_SESSIONS)
|
else if (page == HTTP_PAGE_SAM_SESSIONS)
|
||||||
ShowSAMSessions (s);
|
ShowSAMSessions (s);
|
||||||
else if (page == HTTP_PAGE_SAM_SESSION)
|
else if (page == HTTP_PAGE_SAM_SESSION)
|
||||||
ShowSAMSession (s, params["sam_id"]);
|
ShowSAMSession (s, params["sam_id"]);
|
||||||
|
#endif
|
||||||
else if (page == HTTP_PAGE_I2P_TUNNELS)
|
else if (page == HTTP_PAGE_I2P_TUNNELS)
|
||||||
ShowI2PTunnels (s);
|
ShowI2PTunnels (s);
|
||||||
else if (page == HTTP_PAGE_LEASESETS)
|
else if (page == HTTP_PAGE_LEASESETS)
|
||||||
@@ -1245,33 +1267,28 @@ namespace http {
|
|||||||
else if (cmd == HTTP_COMMAND_SHUTDOWN_START)
|
else if (cmd == HTTP_COMMAND_SHUTDOWN_START)
|
||||||
{
|
{
|
||||||
i2p::context.SetAcceptsTunnels (false);
|
i2p::context.SetAcceptsTunnels (false);
|
||||||
// TODO: rewrite timer access
|
|
||||||
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
||||||
if (m_DaemonGracefulTimer) m_DaemonGracefulTimer = 10 * 60;
|
Daemon.gracefulShutdownInterval = 10*60;
|
||||||
/*#elif defined(WIN32_APP)
|
#elif defined(WIN32_APP)
|
||||||
i2p::win32::GracefulShutdown ();
|
i2p::win32::GracefulShutdown ();
|
||||||
*/
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL)
|
else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL)
|
||||||
{
|
{
|
||||||
i2p::context.SetAcceptsTunnels (true);
|
i2p::context.SetAcceptsTunnels (true);
|
||||||
// TODO: rewrite timer access
|
|
||||||
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
|
||||||
if (m_DaemonGracefulTimer) m_DaemonGracefulTimer = 0;
|
Daemon.gracefulShutdownInterval = 0;
|
||||||
/*#elif defined(WIN32_APP)
|
#elif defined(WIN32_APP)
|
||||||
i2p::win32::StopGracefulShutdown ();
|
i2p::win32::StopGracefulShutdown ();
|
||||||
*/
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW)
|
else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW)
|
||||||
{
|
{
|
||||||
// TODO: rewrite stop command access
|
#ifndef WIN32_APP
|
||||||
//#ifndef WIN32_APP
|
Daemon.running = false;
|
||||||
m_Server.GetDaemonStop();
|
#else
|
||||||
//#else
|
i2p::win32::StopWin32App ();
|
||||||
// i2p::win32::StopWin32App ();
|
#endif
|
||||||
//#endif
|
|
||||||
}
|
}
|
||||||
else if (cmd == HTTP_COMMAND_LOGLEVEL)
|
else if (cmd == HTTP_COMMAND_LOGLEVEL)
|
||||||
{
|
{
|
||||||
@@ -1505,7 +1522,7 @@ namespace http {
|
|||||||
|
|
||||||
void HTTPServer::CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
|
void HTTPServer::CreateConnection(std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
|
||||||
{
|
{
|
||||||
auto conn = std::make_shared<HTTPConnection> (m_Hostname, newSocket, *this);
|
auto conn = std::make_shared<HTTPConnection> (m_Hostname, newSocket);
|
||||||
conn->Receive ();
|
conn->Receive ();
|
||||||
}
|
}
|
||||||
} // http
|
} // http
|
||||||
@@ -25,13 +25,11 @@ namespace http
|
|||||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
||||||
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
|
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
|
||||||
|
|
||||||
class HTTPServer;
|
|
||||||
|
|
||||||
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
|
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
HTTPConnection (std::string serverhost, std::shared_ptr<boost::asio::ip::tcp::socket> socket, HTTPServer& server);
|
HTTPConnection (std::string serverhost, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
||||||
void Receive ();
|
void Receive ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -50,7 +48,6 @@ namespace http
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
||||||
HTTPServer& m_Server;
|
|
||||||
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
|
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
|
||||||
size_t m_BufferLen;
|
size_t m_BufferLen;
|
||||||
std::string m_SendBuffer;
|
std::string m_SendBuffer;
|
||||||
@@ -72,13 +69,6 @@ namespace http
|
|||||||
void Start ();
|
void Start ();
|
||||||
void Stop ();
|
void Stop ();
|
||||||
|
|
||||||
typedef std::function<void ()> DaemonStop;
|
|
||||||
void SetDaemonStop (const DaemonStop& f) { m_DaemonStop = f; };
|
|
||||||
DaemonStop GetDaemonStop () { return m_DaemonStop; };
|
|
||||||
|
|
||||||
void SetDaemonGracefulTimer (const int& f) { m_DaemonGracefulTimer = f; };
|
|
||||||
int GetDaemonGracefulTimer () { return m_DaemonGracefulTimer; };
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void Run ();
|
void Run ();
|
||||||
@@ -95,11 +85,6 @@ namespace http
|
|||||||
boost::asio::io_service::work m_Work;
|
boost::asio::io_service::work m_Work;
|
||||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||||
std::string m_Hostname;
|
std::string m_Hostname;
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
DaemonStop m_DaemonStop;
|
|
||||||
int m_DaemonGracefulTimer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//all the below functions are also used by Qt GUI, see mainwindow.cpp -> getStatusPageHtml
|
//all the below functions are also used by Qt GUI, see mainwindow.cpp -> getStatusPageHtml
|
||||||
@@ -110,11 +95,15 @@ namespace http
|
|||||||
void ShowTunnels (std::stringstream& s);
|
void ShowTunnels (std::stringstream& s);
|
||||||
void ShowTransitTunnels (std::stringstream& s);
|
void ShowTransitTunnels (std::stringstream& s);
|
||||||
void ShowTransports (std::stringstream& s);
|
void ShowTransports (std::stringstream& s);
|
||||||
void ShowSAMSessions (std::stringstream& s);
|
|
||||||
void ShowI2PTunnels (std::stringstream& s);
|
void ShowI2PTunnels (std::stringstream& s);
|
||||||
void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token);
|
void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token);
|
||||||
|
#ifdef WITH_SAM
|
||||||
|
void ShowSAMSessions (std::stringstream& s);
|
||||||
void ShowSAMSession (std::stringstream& s, const std::string& id);
|
void ShowSAMSession (std::stringstream& s, const std::string& id);
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_I2CP
|
||||||
void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id);
|
void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id);
|
||||||
|
#endif
|
||||||
} // http
|
} // http
|
||||||
} // i2p
|
} // i2p
|
||||||
|
|
||||||
@@ -33,6 +33,7 @@ namespace http
|
|||||||
|
|
||||||
// bundled style sheet
|
// bundled style sheet
|
||||||
const std::string internalCSS =
|
const std::string internalCSS =
|
||||||
|
"<style>\r\n"
|
||||||
":root { --main-bg-color: #fafafa; --main-text-color: #103456; --main-link-color: #894c84; --main-link-hover-color: #fafafa; }\r\n"
|
":root { --main-bg-color: #fafafa; --main-text-color: #103456; --main-link-color: #894c84; --main-link-hover-color: #fafafa; }\r\n"
|
||||||
"@media (prefers-color-scheme: dark) { :root { --main-bg-color: #242424; --main-text-color: #17ab5c; --main-link-color: #bf64b7; --main-link-hover-color: #000000; } }\r\n"
|
"@media (prefers-color-scheme: dark) { :root { --main-bg-color: #242424; --main-text-color: #17ab5c; --main-link-color: #bf64b7; --main-link-hover-color: #000000; } }\r\n"
|
||||||
"body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: var(--main-bg-color); color: var(--main-text-color); }\r\n"
|
"body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: var(--main-bg-color); color: var(--main-text-color); }\r\n"
|
||||||
@@ -84,127 +85,12 @@ namespace http
|
|||||||
" border-radius: 5px; font-size: 12px; }\r\n"
|
" border-radius: 5px; font-size: 12px; }\r\n"
|
||||||
" button[type=submit] { padding: 5px 15px; background: transparent; border: 2px solid var(--main-link-color); cursor: pointer;\r\n"
|
" button[type=submit] { padding: 5px 15px; background: transparent; border: 2px solid var(--main-link-color); cursor: pointer;\r\n"
|
||||||
" border-radius: 5px; position: relative; height: 36px; display: -webkit-inline-box; margin-top: 10px; }\r\n"
|
" border-radius: 5px; position: relative; height: 36px; display: -webkit-inline-box; margin-top: 10px; }\r\n"
|
||||||
"}\r\n";
|
"}\r\n"
|
||||||
|
"</style>\r\n";
|
||||||
|
|
||||||
// for external style sheet
|
// for external style sheet
|
||||||
std::string externalCSS;
|
std::string externalCSS;
|
||||||
|
|
||||||
const std::string pageBase =
|
|
||||||
"<!DOCTYPE html> \
|
|
||||||
<html lang=\"{{ langCode }}\"> \
|
|
||||||
<head> \
|
|
||||||
{% block head %} \
|
|
||||||
<meta charset=\"UTF-8\"> \
|
|
||||||
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> \
|
|
||||||
<link rel=\"shortcut icon\" href=\"{% getFavicon() %}\"> \
|
|
||||||
<title>{% block title %}{% endblock %} - Purple I2P Webconsole</title> \
|
|
||||||
<style>{% getStyles() %}</style> \
|
|
||||||
{% endblock %} \
|
|
||||||
</head> \
|
|
||||||
<body> \
|
|
||||||
<div class=\"header\">{% tr(\"<b>i2pd</b> webconsole\") %}</div> \
|
|
||||||
<div class=\"wrapper\"> \
|
|
||||||
<div class=\"menu\"> \
|
|
||||||
<a href=\"{{ webroot }}\">{% tr(\"Main page\") %}</a><br><br> \
|
|
||||||
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_COMMANDS\") %}\">{% tr(\"Router commands\") %}</a><br> \
|
|
||||||
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_LOCAL_DESTINATIONS\") %}\">{% tr(\"Local Destinations\") %}</a><br> \
|
|
||||||
{% if isFloodfill %} \
|
|
||||||
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_LEASESETS\") %}\">{% tr(\"LeaseSets\") %}</a><br> \
|
|
||||||
{% endif %} \
|
|
||||||
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_TUNNELS\") %}\">{% tr(\"Tunnels\") %}</a><br> \
|
|
||||||
{% if (acceptingTunnels || transitTunnels) %} \
|
|
||||||
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_TRANSIT_TUNNELS\") %}\">{% tr(\"Transit Tunnels\") %}</a><br> \
|
|
||||||
{% endif %} \
|
|
||||||
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_TRANSPORTS\") %}\">{% tr(\"Transports\") %}</a><br> \
|
|
||||||
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_I2P_TUNNELS\") %}\">{% tr(\"I2P tunnels\") %}</a><br> \
|
|
||||||
{% if samEnabled %} \
|
|
||||||
<a href=\"{{ webroot }}?page={% getCommand(\"HTTP_PAGE_SAM_SESSIONS\") %}\">{% tr(\"SAM sessions\") %}</a><br> \
|
|
||||||
{% endif %} \
|
|
||||||
</div> \
|
|
||||||
<div class=\"content\">{% block content %}{% endblock %}</div> \
|
|
||||||
</div> \
|
|
||||||
</body> \
|
|
||||||
</html>";
|
|
||||||
|
|
||||||
const std::string pageMain =
|
|
||||||
"{% extends \"base.html\" %} \
|
|
||||||
{% block title %}Main{% endblock %} \
|
|
||||||
{% block content %} \
|
|
||||||
<b>{% tr(\"Uptime\") %}:</b> {% getUptime() %}<br> \
|
|
||||||
<b>{% tr(\"Network status\") %}:</b> {% getNetworkStatus(false) %}<br> \
|
|
||||||
{% if supportsV6 %} \
|
|
||||||
<b>{% tr(\"Network status v6\") %}:</b> {% getNetworkStatus(true) %}<br> \
|
|
||||||
{% endif %} \
|
|
||||||
{% if gracefulShutdown %} \
|
|
||||||
<b>{% tr(\"Stopping in\") %}:</b> {% getShutdownTimer() %}<br> \
|
|
||||||
{% endif %} \
|
|
||||||
<b>{% tr(\"Tunnel creation success rate\") %}:</b> {% getSuccessRate() %}%<br> \
|
|
||||||
<b>{% tr(\"Received\") %}:</b> {% getInBytes() %} ({% getInBW() %} {% tr(/* tr: Kibibit/s */ \"KiB/s\") %})<br> \
|
|
||||||
<b>{% tr(\"Sent\") %}:</b> {% getOutBytes() %} ({% getOutBW() %} {% tr(/* tr: Kibibit/s */ \"KiB/s\") %})<br> \
|
|
||||||
<b>{% tr(\"Transit\") %}:</b> {% getTransitBytes() %} ({% getTransitBW() %} {% tr(/* tr: Kibibit/s */ \"KiB/s\") %})<br> \
|
|
||||||
<b>{% tr(\"Data path\") %}:</b> {% getDataPath() %}<br> \
|
|
||||||
{% if notQt || withHiddenContent %} \
|
|
||||||
<div class=\"slide\"> \
|
|
||||||
<label for=\"slide-info\">{% tr(\"Hidden content. Press on text to see.\") %}</label> \
|
|
||||||
<input type=\"checkbox\" id=\"slide-info\" /> \
|
|
||||||
<div class=\"slidecontent\"> \
|
|
||||||
<b>{% tr(\"Router Ident\") %}:</b> {% getRI() %}<br> \
|
|
||||||
{% if lenght(family) %} \
|
|
||||||
<b>{% tr(\"Family\") %}:</b> {{ family }}<br> \
|
|
||||||
{% endif %} \
|
|
||||||
<b>{% tr(\"Router Caps\") %}:</b> {% getCaps() %}<br> \
|
|
||||||
<b>{% tr(\"Version\") %}:</b> {{ version }}<br> \
|
|
||||||
<b>{% tr(\"Our external address\") %}:</b><br> \
|
|
||||||
<table class=\"extaddr\"> \
|
|
||||||
<tbody> \
|
|
||||||
{% for type, address in addresses %} \
|
|
||||||
<tr><td>{{ type }}</td><td>{{ address }}</td></tr> \
|
|
||||||
{% endfor %} \
|
|
||||||
</tbody> \
|
|
||||||
</table> \
|
|
||||||
</div> \
|
|
||||||
</div> \
|
|
||||||
{% else %} \
|
|
||||||
<br> \
|
|
||||||
{% endif %} \
|
|
||||||
<b>{% tr(\"Routers\") %}:</b> {% getNumRouter() %} \
|
|
||||||
<b>{% tr(\"Floodfills\") %}:</b> {% getNumFloodfills() %} \
|
|
||||||
<b>{% tr(\"LeaseSets\") %}:</b> {% getNumLeaseSets() %}<br> \
|
|
||||||
<b>{% tr(\"Client Tunnels\") %}:</b> {% getClientTunnelsCount() %} \
|
|
||||||
<b>{% tr(\"Transit Tunnels\") %}:</b> {% getTransitTunnelsCount() %}<br> \
|
|
||||||
<br> \
|
|
||||||
{% if notQt == false %} \
|
|
||||||
<table class=\"services\"> \
|
|
||||||
<caption>{% tr(\"Services\") %}</caption> \
|
|
||||||
<tbody> \
|
|
||||||
<tr> \
|
|
||||||
<td>HTTP {% tr(\"Proxy\") %}</td> \
|
|
||||||
<td class=\"{% if httpproxy %}enabled{% else %}disabled{% endif %}\">{% if httpproxy %}{% tr(\"Enabled\") %}{% else %}{% tr(\"Disabled\") %}{% endif %}</td> \
|
|
||||||
</tr> \
|
|
||||||
<tr> \
|
|
||||||
<td>SOCKS {% tr(\"Proxy\") %}</td> \
|
|
||||||
<td class=\"{% if socksproxy %}enabled{% else %}disabled{% endif %}\">{% if socksproxy %}{% tr(\"Enabled\") %}{% else %}{% tr(\"Disabled\") %}{% endif %}</td> \
|
|
||||||
</tr> \
|
|
||||||
<tr> \
|
|
||||||
<td>BOB</td> \
|
|
||||||
<td class=\"{% if bob %}enabled{% else %}disabled{% endif %}\">{% if bob %}{% tr(\"Enabled\") %}{% else %}{% tr(\"Disabled\") %}{% endif %}</td> \
|
|
||||||
</tr> \
|
|
||||||
<tr> \
|
|
||||||
<td>SAM</td> \
|
|
||||||
<td class=\"{% if sam %}enabled{% else %}disabled{% endif %}\">{% if sam %}{% tr(\"Enabled\") %}{% else %}{% tr(\"Disabled\") %}{% endif %}</td> \
|
|
||||||
</tr> \
|
|
||||||
<tr> \
|
|
||||||
<td>I2CP</td> \
|
|
||||||
<td class=\"{% if i2cp %}enabled{% else %}disabled{% endif %}\">{% if i2cp %}{% tr(\"Enabled\") %}{% else %}{% tr(\"Disabled\") %}{% endif %}</td> \
|
|
||||||
</tr> \
|
|
||||||
<tr> \
|
|
||||||
<td>I2PControl</td> \
|
|
||||||
<td class=\"{% if i2pcontrol %}enabled{% else %}disabled{% endif %}\">{% if i2pcontrol %}{% tr(\"Enabled\") %}{% else %}{% tr(\"Disabled\") %}{% endif %}</td> \
|
|
||||||
</tr> \
|
|
||||||
</tbody> \
|
|
||||||
</table> \
|
|
||||||
{% endblock %}";
|
|
||||||
|
|
||||||
} // http
|
} // http
|
||||||
} // i2p
|
} // i2p
|
||||||
|
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
* See full license text in LICENSE file at top of project tree
|
* See full license text in LICENSE file at top of project tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef WITH_I2PC
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
@@ -104,9 +106,15 @@ namespace client
|
|||||||
m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlService::I2PTunnelInfoHandler;
|
m_ClientServicesInfoHandlers["I2PTunnel"] = &I2PControlService::I2PTunnelInfoHandler;
|
||||||
m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlService::HTTPProxyInfoHandler;
|
m_ClientServicesInfoHandlers["HTTPProxy"] = &I2PControlService::HTTPProxyInfoHandler;
|
||||||
m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlService::SOCKSInfoHandler;
|
m_ClientServicesInfoHandlers["SOCKS"] = &I2PControlService::SOCKSInfoHandler;
|
||||||
|
#ifdef WITH_SAM
|
||||||
m_ClientServicesInfoHandlers["SAM"] = &I2PControlService::SAMInfoHandler;
|
m_ClientServicesInfoHandlers["SAM"] = &I2PControlService::SAMInfoHandler;
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_BOB
|
||||||
m_ClientServicesInfoHandlers["BOB"] = &I2PControlService::BOBInfoHandler;
|
m_ClientServicesInfoHandlers["BOB"] = &I2PControlService::BOBInfoHandler;
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_I2CP
|
||||||
m_ClientServicesInfoHandlers["I2CP"] = &I2PControlService::I2CPInfoHandler;
|
m_ClientServicesInfoHandlers["I2CP"] = &I2PControlService::I2CPInfoHandler;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
I2PControlService::~I2PControlService ()
|
I2PControlService::~I2PControlService ()
|
||||||
@@ -345,8 +353,7 @@ namespace client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handlers
|
// handlers
|
||||||
|
|
||||||
void I2PControlService::AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
void I2PControlService::AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||||
{
|
{
|
||||||
int api = params.get<int> ("API");
|
int api = params.get<int> ("API");
|
||||||
@@ -371,8 +378,7 @@ namespace client
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// I2PControl
|
// I2PControl
|
||||||
|
|
||||||
void I2PControlService::I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
void I2PControlService::I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||||
{
|
{
|
||||||
for (auto& it: params)
|
for (auto& it: params)
|
||||||
@@ -532,7 +538,7 @@ namespace client
|
|||||||
i2p::data::netdb.Reseed ();
|
i2p::data::netdb.Reseed ();
|
||||||
}
|
}
|
||||||
|
|
||||||
// network setting
|
// network setting
|
||||||
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||||
{
|
{
|
||||||
for (auto it = params.begin (); it != params.end (); it++)
|
for (auto it = params.begin (); it != params.end (); it++)
|
||||||
@@ -612,8 +618,7 @@ namespace client
|
|||||||
EVP_PKEY_free (pkey);
|
EVP_PKEY_free (pkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientServicesInfo
|
// ClientServicesInfo
|
||||||
|
|
||||||
void I2PControlService::ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
void I2PControlService::ClientServicesInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||||
{
|
{
|
||||||
for (auto it = params.begin (); it != params.end (); it++)
|
for (auto it = params.begin (); it != params.end (); it++)
|
||||||
@@ -719,6 +724,7 @@ namespace client
|
|||||||
InsertParam (results, "SOCKS", pt);
|
InsertParam (results, "SOCKS", pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_SAM
|
||||||
void I2PControlService::SAMInfoHandler (std::ostringstream& results)
|
void I2PControlService::SAMInfoHandler (std::ostringstream& results)
|
||||||
{
|
{
|
||||||
boost::property_tree::ptree pt;
|
boost::property_tree::ptree pt;
|
||||||
@@ -754,7 +760,9 @@ namespace client
|
|||||||
|
|
||||||
InsertParam (results, "SAM", pt);
|
InsertParam (results, "SAM", pt);
|
||||||
}
|
}
|
||||||
|
#endif // WITH_SAM
|
||||||
|
|
||||||
|
#ifdef WITH_BOB
|
||||||
void I2PControlService::BOBInfoHandler (std::ostringstream& results)
|
void I2PControlService::BOBInfoHandler (std::ostringstream& results)
|
||||||
{
|
{
|
||||||
boost::property_tree::ptree pt;
|
boost::property_tree::ptree pt;
|
||||||
@@ -769,7 +777,9 @@ namespace client
|
|||||||
|
|
||||||
InsertParam (results, "BOB", pt);
|
InsertParam (results, "BOB", pt);
|
||||||
}
|
}
|
||||||
|
#endif // WITH_BOB
|
||||||
|
|
||||||
|
#ifdef WITH_I2CP
|
||||||
void I2PControlService::I2CPInfoHandler (std::ostringstream& results)
|
void I2PControlService::I2CPInfoHandler (std::ostringstream& results)
|
||||||
{
|
{
|
||||||
boost::property_tree::ptree pt;
|
boost::property_tree::ptree pt;
|
||||||
@@ -784,5 +794,7 @@ namespace client
|
|||||||
|
|
||||||
InsertParam (results, "I2CP", pt);
|
InsertParam (results, "I2CP", pt);
|
||||||
}
|
}
|
||||||
|
#endif // WITH_I2CP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // WITH_I2PC
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
* See full license text in LICENSE file at top of project tree
|
* See full license text in LICENSE file at top of project tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef WITH_I2PC
|
||||||
|
|
||||||
#ifndef I2P_CONTROL_H__
|
#ifndef I2P_CONTROL_H__
|
||||||
#define I2P_CONTROL_H__
|
#define I2P_CONTROL_H__
|
||||||
|
|
||||||
@@ -114,9 +116,15 @@ namespace client
|
|||||||
void I2PTunnelInfoHandler (std::ostringstream& results);
|
void I2PTunnelInfoHandler (std::ostringstream& results);
|
||||||
void HTTPProxyInfoHandler (std::ostringstream& results);
|
void HTTPProxyInfoHandler (std::ostringstream& results);
|
||||||
void SOCKSInfoHandler (std::ostringstream& results);
|
void SOCKSInfoHandler (std::ostringstream& results);
|
||||||
|
#ifdef WITH_SAM
|
||||||
void SAMInfoHandler (std::ostringstream& results);
|
void SAMInfoHandler (std::ostringstream& results);
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_BOB
|
||||||
void BOBInfoHandler (std::ostringstream& results);
|
void BOBInfoHandler (std::ostringstream& results);
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_I2CP
|
||||||
void I2CPInfoHandler (std::ostringstream& results);
|
void I2CPInfoHandler (std::ostringstream& results);
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -141,3 +149,4 @@ namespace client
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
#endif // WITH_I2PC
|
||||||
13
debian/changelog
vendored
13
debian/changelog
vendored
@@ -1,16 +1,3 @@
|
|||||||
i2pd (2.42.1-1) unstable; urgency=medium
|
|
||||||
|
|
||||||
* updated to version 2.42.1/0.9.54
|
|
||||||
* remove -O3 optimization flag
|
|
||||||
|
|
||||||
-- r4sas <r4sas@i2pmail.org> Tue, 24 May 2022 12:00:00 +0000
|
|
||||||
|
|
||||||
i2pd (2.42.0-1) unstable; urgency=medium
|
|
||||||
|
|
||||||
* updated to version 2.42.0/0.9.54
|
|
||||||
|
|
||||||
-- orignal <orignal@i2pmail.org> Sun, 22 May 2022 16:00:00 +0000
|
|
||||||
|
|
||||||
i2pd (2.41.0-1) unstable; urgency=medium
|
i2pd (2.41.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
* updated to version 2.41.0/0.9.53
|
* updated to version 2.41.0/0.9.53
|
||||||
|
|||||||
7
debian/rules
vendored
7
debian/rules
vendored
@@ -1,12 +1,17 @@
|
|||||||
#!/usr/bin/make -f
|
#!/usr/bin/make -f
|
||||||
#export DH_VERBOSE=1
|
#export DH_VERBOSE=1
|
||||||
|
|
||||||
|
|
||||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||||
|
|
||||||
|
|
||||||
include /usr/share/dpkg/architecture.mk
|
include /usr/share/dpkg/architecture.mk
|
||||||
|
|
||||||
export DEB_CXXFLAGS_MAINT_APPEND = -Wall -pedantic
|
export DEB_CXXFLAGS_MAINT_APPEND = -Wall -pedantic -O3
|
||||||
|
|
||||||
export DEB_LDFLAGS_MAINT_APPEND =
|
export DEB_LDFLAGS_MAINT_APPEND =
|
||||||
|
|
||||||
|
|
||||||
%:
|
%:
|
||||||
dh $@ --parallel
|
dh $@ --parallel
|
||||||
|
|
||||||
|
|||||||
23
filelist.mk
23
filelist.mk
@@ -1,7 +1,26 @@
|
|||||||
|
#LIB_SRC = \
|
||||||
|
# BloomFilter.cpp Gzip.cpp Crypto.cpp Datagram.cpp Garlic.cpp I2NPProtocol.cpp LeaseSet.cpp \
|
||||||
|
# Log.cpp NTCPSession.cpp NetDb.cpp NetDbRequests.cpp Profiling.cpp \
|
||||||
|
# Reseed.cpp RouterContext.cpp RouterInfo.cpp Signature.cpp SSU.cpp \
|
||||||
|
# SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \
|
||||||
|
# Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp TunnelGateway.cpp \
|
||||||
|
# Destination.cpp Base.cpp I2PEndian.cpp FS.cpp Config.cpp Family.cpp \
|
||||||
|
# Config.cpp HTTP.cpp Timestamp.cpp util.cpp api.cpp Gost.cpp
|
||||||
|
|
||||||
LIB_SRC = $(wildcard $(LIB_SRC_DIR)/*.cpp)
|
LIB_SRC = $(wildcard $(LIB_SRC_DIR)/*.cpp)
|
||||||
|
|
||||||
|
#LIB_CLIENT_SRC = \
|
||||||
|
# AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp MatchedDestination.cpp \
|
||||||
|
# SAM.cpp SOCKS.cpp HTTPProxy.cpp I2CP.cpp
|
||||||
|
|
||||||
LIB_CLIENT_SRC = $(wildcard $(LIB_CLIENT_SRC_DIR)/*.cpp)
|
LIB_CLIENT_SRC = $(wildcard $(LIB_CLIENT_SRC_DIR)/*.cpp)
|
||||||
|
|
||||||
|
# also: Daemon{Linux,Win32}.cpp will be added later
|
||||||
|
#DAEMON_SRC = \
|
||||||
|
# HTTPServer.cpp I2PControl.cpp UPnP.cpp Daemon.cpp i2pd.cpp
|
||||||
|
|
||||||
LANG_SRC = $(wildcard $(LANG_SRC_DIR)/*.cpp)
|
LANG_SRC = $(wildcard $(LANG_SRC_DIR)/*.cpp)
|
||||||
WEBCONSOLE_SRC = $(wildcard $(WEBCONSOLE_SRC_DIR)/*.cpp)
|
|
||||||
|
|
||||||
WRAP_LIB_SRC = $(wildcard $(WRAP_SRC_DIR)/*.cpp)
|
WRAP_LIB_SRC = $(wildcard $(WRAP_SRC_DIR)/*.cpp)
|
||||||
DAEMON_SRC = $(DAEMON_SRC_DIR)/Daemon.cpp $(DAEMON_SRC_DIR)/I2PControl.cpp $(DAEMON_SRC_DIR)/i2pd.cpp $(DAEMON_SRC_DIR)/UPnP.cpp
|
|
||||||
|
DAEMON_SRC = $(wildcard $(DAEMON_SRC_DIR)/*.cpp)
|
||||||
|
|||||||
102
i18n/French.cpp
102
i18n/French.cpp
@@ -1,102 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2022, The PurpleI2P Project
|
|
||||||
*
|
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
|
||||||
*
|
|
||||||
* See full license text in LICENSE file at top of project tree
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include "I18N.h"
|
|
||||||
|
|
||||||
// French localization file
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace i18n
|
|
||||||
{
|
|
||||||
namespace french // language namespace
|
|
||||||
{
|
|
||||||
// language name in lowercase
|
|
||||||
static std::string language = "french";
|
|
||||||
|
|
||||||
// See for language plural forms here:
|
|
||||||
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
|
|
||||||
static int plural (int n) {
|
|
||||||
return n != 1 ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::map<std::string, std::string> strings
|
|
||||||
{
|
|
||||||
{"KiB", "Kio"},
|
|
||||||
{"MiB", "Mio"},
|
|
||||||
{"GiB", "Gio"},
|
|
||||||
{"building", "En construction"},
|
|
||||||
{"failed", "echoué"},
|
|
||||||
{"expiring", "expiré"},
|
|
||||||
{"established", "établi"},
|
|
||||||
{"unknown", "inconnu"},
|
|
||||||
{"exploratory", "exploratoire"},
|
|
||||||
{"<b>i2pd</b> webconsole", "Console web <b>i2pd</b>"},
|
|
||||||
{"Main page", "Page principale"},
|
|
||||||
{"Router commands", "Commandes du routeur"},
|
|
||||||
{"Local Destinations", "Destinations locales"},
|
|
||||||
{"Tunnels", "Tunnels"},
|
|
||||||
{"Transit Tunnels", "Tunnels transitoires"},
|
|
||||||
{"I2P tunnels", "Tunnels I2P"},
|
|
||||||
{"SAM sessions", "Sessions SAM"},
|
|
||||||
{"ERROR", "ERREUR"},
|
|
||||||
{"OK", "OK"},
|
|
||||||
{"Firewalled", "Derrière un pare-feu"},
|
|
||||||
{"Error", "Erreur"},
|
|
||||||
{"Offline", "Hors ligne"},
|
|
||||||
{"Uptime", "Temps de fonctionnement"},
|
|
||||||
{"Network status", "État du réseau"},
|
|
||||||
{"Network status v6", "État du réseau v6"},
|
|
||||||
{"Stopping in", "Arrêt dans"},
|
|
||||||
{"Family", "Famille"},
|
|
||||||
{"Tunnel creation success rate", "Taux de succès de création de tunnels"},
|
|
||||||
{"Received", "Reçu"},
|
|
||||||
{"KiB/s", "kio/s"},
|
|
||||||
{"Sent", "Envoyé"},
|
|
||||||
{"Transit", "Transit"},
|
|
||||||
{"Hidden content. Press on text to see.", "Contenu caché. Cliquez sur le texte pour regarder."},
|
|
||||||
{"Router Ident", "Identifiant du routeur"},
|
|
||||||
{"Router Family", "Famille du routeur"},
|
|
||||||
{"Version", "Version"},
|
|
||||||
{"Our external address", "Notre adresse externe"},
|
|
||||||
{"Client Tunnels", "Tunnels clients"},
|
|
||||||
{"Services", "Services"},
|
|
||||||
{"Enabled", "Activé"},
|
|
||||||
{"Disabled", "Désactivé"},
|
|
||||||
{"Encrypted B33 address", "Adresse B33 chiffrée"},
|
|
||||||
{"Domain", "Domaine"},
|
|
||||||
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Note:</b> La chaîne résultante peut seulement être utilisée pour enregistrer les domaines 2LD (exemple.i2p). Pour enregistrer des sous-domaines, veuillez utiliser i2pd-tools."},
|
|
||||||
{"Address", "Adresse"},
|
|
||||||
{"ms", "ms"},
|
|
||||||
{"Outbound tunnels", "Tunnels sortants"},
|
|
||||||
{"Destination", "Destination"},
|
|
||||||
{"Local Destination", "Destination locale"},
|
|
||||||
{"", ""},
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::map<std::string, std::vector<std::string>> plurals
|
|
||||||
{
|
|
||||||
{"days", {"jour", "jours"}},
|
|
||||||
{"hours", {"heure", "heures"}},
|
|
||||||
{"minutes", {"minute", "minutes"}},
|
|
||||||
{"seconds", {"seconde", "secondes"}},
|
|
||||||
{"", {"", ""}},
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
|
|
||||||
{
|
|
||||||
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
|
|
||||||
}
|
|
||||||
|
|
||||||
} // language
|
|
||||||
} // i18n
|
|
||||||
} // i2p
|
|
||||||
@@ -74,7 +74,6 @@ namespace i18n
|
|||||||
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
namespace armenian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
namespace french { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
|
||||||
namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
namespace german { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
|
||||||
@@ -89,7 +88,6 @@ namespace i18n
|
|||||||
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
|
{ "afrikaans", {"Afrikaans", "af", i2p::i18n::afrikaans::GetLocale} },
|
||||||
{ "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} },
|
{ "armenian", {"հայերէն", "hy", i2p::i18n::armenian::GetLocale} },
|
||||||
{ "english", {"English", "en", i2p::i18n::english::GetLocale} },
|
{ "english", {"English", "en", i2p::i18n::english::GetLocale} },
|
||||||
{ "french", {"Français", "fr", i2p::i18n::french::GetLocale} },
|
|
||||||
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
|
{ "german", {"Deutsch", "de", i2p::i18n::german::GetLocale} },
|
||||||
{ "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} },
|
{ "russian", {"русский язык", "ru", i2p::i18n::russian::GetLocale} },
|
||||||
{ "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
|
{ "turkmen", {"türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace data {
|
|||||||
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
|
size_t ByteStreamToBase32 (const uint8_t * InBuf, size_t len, char * outBuf, size_t outLen);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes
|
Compute the size for a buffer to contain encoded base64 given that the size of the input is input_size bytes
|
||||||
*/
|
*/
|
||||||
size_t Base64EncodingBufferSize(const size_t input_size);
|
size_t Base64EncodingBufferSize(const size_t input_size);
|
||||||
|
|
||||||
|
|||||||
@@ -78,9 +78,9 @@ namespace config {
|
|||||||
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
|
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
|
||||||
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
|
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
|
||||||
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
|
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
|
||||||
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Ignored")
|
("limits.ntcpsoft", value<uint16_t>()->default_value(0), "Threshold to start probabilistic backoff with ntcp sessions (default: use system limit)")
|
||||||
("limits.ntcphard", value<uint16_t>()->default_value(0), "Ignored")
|
("limits.ntcphard", value<uint16_t>()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)")
|
||||||
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Ignored")
|
("limits.ntcpthreads", value<uint16_t>()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)")
|
||||||
;
|
;
|
||||||
|
|
||||||
options_description httpserver("HTTP Server options");
|
options_description httpserver("HTTP Server options");
|
||||||
@@ -230,7 +230,6 @@ namespace config {
|
|||||||
|
|
||||||
options_description addressbook("AddressBook options");
|
options_description addressbook("AddressBook options");
|
||||||
addressbook.add_options()
|
addressbook.add_options()
|
||||||
("addressbook.enabled", value<bool>()->default_value(true), "Enable address book lookups and subscritions (default: enabled)")
|
|
||||||
("addressbook.defaulturl", value<std::string>()->default_value(
|
("addressbook.defaulturl", value<std::string>()->default_value(
|
||||||
"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt"
|
"http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt"
|
||||||
), "AddressBook subscription URL for initial setup")
|
), "AddressBook subscription URL for initial setup")
|
||||||
@@ -273,7 +272,7 @@ namespace config {
|
|||||||
;
|
;
|
||||||
|
|
||||||
options_description ssu2("SSU2 Options");
|
options_description ssu2("SSU2 Options");
|
||||||
ssu2.add_options()
|
ntcp2.add_options()
|
||||||
("ssu2.enabled", value<bool>()->default_value(false), "Enable SSU2 (default: disabled)")
|
("ssu2.enabled", value<bool>()->default_value(false), "Enable SSU2 (default: disabled)")
|
||||||
("ssu2.published", value<bool>()->default_value(false), "Publish SSU2 (default: disabled)")
|
("ssu2.published", value<bool>()->default_value(false), "Publish SSU2 (default: disabled)")
|
||||||
("ssu2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming SSU2 packets (default: auto)")
|
("ssu2.port", value<uint16_t>()->default_value(0), "Port to listen for incoming SSU2 packets (default: auto)")
|
||||||
|
|||||||
@@ -29,9 +29,7 @@
|
|||||||
#include "CPU.h"
|
#include "CPU.h"
|
||||||
|
|
||||||
// recognize openssl version and features
|
// recognize openssl version and features
|
||||||
#if (defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x3050200fL)) // LibreSSL 3.5.2 and above
|
#if ((OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER)) // 1.0.2 and below or LibreSSL
|
||||||
# define LEGACY_OPENSSL 0
|
|
||||||
#elif ((OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER)) // 1.0.2 and below or LibreSSL
|
|
||||||
# define LEGACY_OPENSSL 1
|
# define LEGACY_OPENSSL 1
|
||||||
# define X509_getm_notBefore X509_get_notBefore
|
# define X509_getm_notBefore X509_get_notBefore
|
||||||
# define X509_getm_notAfter X509_get_notAfter
|
# define X509_getm_notAfter X509_get_notAfter
|
||||||
@@ -41,7 +39,7 @@
|
|||||||
# define OPENSSL_HKDF 1
|
# define OPENSSL_HKDF 1
|
||||||
# define OPENSSL_EDDSA 1
|
# define OPENSSL_EDDSA 1
|
||||||
# define OPENSSL_X25519 1
|
# define OPENSSL_X25519 1
|
||||||
# if (OPENSSL_VERSION_NUMBER != 0x030000000) // 3.0.0, regression in SipHash
|
# if (OPENSSL_VERSION_NUMBER < 0x030000000) // 3.0.0, regression in SipHash
|
||||||
# define OPENSSL_SIPHASH 1
|
# define OPENSSL_SIPHASH 1
|
||||||
# endif
|
# endif
|
||||||
# endif
|
# endif
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
|
#include "Config.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "FS.h"
|
#include "FS.h"
|
||||||
#include "Timestamp.h"
|
#include "Timestamp.h"
|
||||||
@@ -93,7 +94,9 @@ namespace client
|
|||||||
if (it != params->end ())
|
if (it != params->end ())
|
||||||
{
|
{
|
||||||
// oveeride isPublic
|
// oveeride isPublic
|
||||||
m_IsPublic = (it->second != "true");
|
bool dontpublish = false;
|
||||||
|
i2p::config::GetOption (it->second, dontpublish);
|
||||||
|
m_IsPublic = !dontpublish;
|
||||||
}
|
}
|
||||||
it = params->find (I2CP_PARAM_LEASESET_TYPE);
|
it = params->find (I2CP_PARAM_LEASESET_TYPE);
|
||||||
if (it != params->end ())
|
if (it != params->end ())
|
||||||
@@ -951,7 +954,7 @@ namespace client
|
|||||||
for (auto& it: encryptionKeyTypes)
|
for (auto& it: encryptionKeyTypes)
|
||||||
{
|
{
|
||||||
auto encryptionKey = new EncryptionKey (it);
|
auto encryptionKey = new EncryptionKey (it);
|
||||||
if (IsPublic ())
|
if (isPublic)
|
||||||
PersistTemporaryKeys (encryptionKey, isSingleKey);
|
PersistTemporaryKeys (encryptionKey, isSingleKey);
|
||||||
else
|
else
|
||||||
encryptionKey->GenerateKeys ();
|
encryptionKey->GenerateKeys ();
|
||||||
@@ -966,7 +969,7 @@ namespace client
|
|||||||
m_StandardEncryptionKey.reset (encryptionKey);
|
m_StandardEncryptionKey.reset (encryptionKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPublic ())
|
if (isPublic)
|
||||||
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
|
LogPrint (eLogInfo, "Destination: Local address ", GetIdentHash().ToBase32 (), " created");
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -979,7 +982,7 @@ namespace client
|
|||||||
m_StreamingAckDelay = std::stoi(it->second);
|
m_StreamingAckDelay = std::stoi(it->second);
|
||||||
it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS);
|
it = params->find (I2CP_PARAM_STREAMING_ANSWER_PINGS);
|
||||||
if (it != params->end ())
|
if (it != params->end ())
|
||||||
m_IsStreamingAnswerPings = (it->second == "true");
|
i2p::config::GetOption (it->second, m_IsStreamingAnswerPings);
|
||||||
|
|
||||||
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
if (GetLeaseSetType () == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -319,5 +319,4 @@ namespace client
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
@@ -60,38 +60,10 @@ namespace fs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DetectDataDir(const std::string & cmdline_param, bool isService) {
|
void DetectDataDir(const std::string & cmdline_param, bool isService) {
|
||||||
// with 'datadir' option
|
|
||||||
if (cmdline_param != "") {
|
if (cmdline_param != "") {
|
||||||
dataDir = cmdline_param;
|
dataDir = cmdline_param;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(MAC_OSX) && !defined(ANDROID)
|
|
||||||
// with 'service' option
|
|
||||||
if (isService) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
wchar_t commonAppData[MAX_PATH];
|
|
||||||
if(SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, commonAppData) != S_OK)
|
|
||||||
{
|
|
||||||
#ifdef WIN32_APP
|
|
||||||
MessageBox(NULL, TEXT("Unable to get common AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK);
|
|
||||||
#else
|
|
||||||
fprintf(stderr, "Error: Unable to get common AppData path!");
|
|
||||||
#endif
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dataDir = boost::filesystem::wpath(commonAppData).string() + "\\" + appName;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
dataDir = "/var/lib/" + appName;
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// detect directory as usual
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
wchar_t localAppData[MAX_PATH];
|
wchar_t localAppData[MAX_PATH];
|
||||||
|
|
||||||
@@ -145,10 +117,12 @@ namespace fs {
|
|||||||
dataDir = std::string (ext) + "/" + appName;
|
dataDir = std::string (ext) + "/" + appName;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif // ANDROID
|
#endif
|
||||||
// use /home/user/.i2pd or /tmp/i2pd
|
// otherwise use /data/files
|
||||||
char *home = getenv("HOME");
|
char *home = getenv("HOME");
|
||||||
if (home != NULL && strlen(home) > 0) {
|
if (isService) {
|
||||||
|
dataDir = "/var/lib/" + appName;
|
||||||
|
} else if (home != NULL && strlen(home) > 0) {
|
||||||
dataDir = std::string(home) + "/." + appName;
|
dataDir = std::string(home) + "/." + appName;
|
||||||
} else {
|
} else {
|
||||||
dataDir = "/tmp/" + appName;
|
dataDir = "/tmp/" + appName;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
@@ -802,8 +802,13 @@ namespace i2p
|
|||||||
break;
|
break;
|
||||||
case eI2NPGarlic:
|
case eI2NPGarlic:
|
||||||
{
|
{
|
||||||
if (msg->from && msg->from->GetTunnelPool ())
|
if (msg->from)
|
||||||
|
{
|
||||||
|
if (msg->from->GetTunnelPool ())
|
||||||
msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg);
|
msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg);
|
||||||
|
else
|
||||||
|
LogPrint (eLogInfo, "I2NP: Local destination for garlic doesn't exist anymore");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
i2p::context.ProcessGarlicMessage (msg);
|
i2p::context.ProcessGarlicMessage (msg);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -128,8 +128,8 @@ namespace data
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* validate lease set buffer signature and extract expiration timestamp
|
validate lease set buffer signature and extract expiration timestamp
|
||||||
* @returns true if the leaseset is well formed and signature is valid
|
@returns true if the leaseset is well formed and signature is valid
|
||||||
*/
|
*/
|
||||||
bool LeaseSetBufferValidate(const uint8_t * ptr, size_t sz, uint64_t & expires);
|
bool LeaseSetBufferValidate(const uint8_t * ptr, size_t sz, uint64_t & expires);
|
||||||
|
|
||||||
|
|||||||
@@ -107,10 +107,7 @@ namespace data
|
|||||||
{
|
{
|
||||||
i2p::util::SetThreadName("NetDB");
|
i2p::util::SetThreadName("NetDB");
|
||||||
|
|
||||||
uint64_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0;
|
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0;
|
||||||
uint64_t lastProfilesCleanup = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
int16_t profilesCleanupVariance = 0;
|
|
||||||
|
|
||||||
while (m_IsRunning)
|
while (m_IsRunning)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -158,7 +155,6 @@ namespace data
|
|||||||
m_Requests.ManageRequests ();
|
m_Requests.ManageRequests ();
|
||||||
lastManageRequest = ts;
|
lastManageRequest = ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute
|
if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute
|
||||||
{
|
{
|
||||||
if (lastSave)
|
if (lastSave)
|
||||||
@@ -168,20 +164,12 @@ namespace data
|
|||||||
}
|
}
|
||||||
lastSave = ts;
|
lastSave = ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
if (ts - lastDestinationCleanup >= i2p::garlic::INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
||||||
{
|
{
|
||||||
i2p::context.CleanupDestination ();
|
i2p::context.CleanupDestination ();
|
||||||
lastDestinationCleanup = ts;
|
lastDestinationCleanup = ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ts - lastProfilesCleanup >= (uint64_t)(i2p::data::PEER_PROFILE_AUTOCLEAN_TIMEOUT + profilesCleanupVariance))
|
|
||||||
{
|
|
||||||
DeleteObsoleteProfiles ();
|
|
||||||
lastProfilesCleanup = ts;
|
|
||||||
profilesCleanupVariance = (rand () % (2 * i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE) - i2p::data::PEER_PROFILE_AUTOCLEAN_VARIANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// publish
|
// publish
|
||||||
if (!m_HiddenMode && i2p::transport::transports.IsOnline ())
|
if (!m_HiddenMode && i2p::transport::transports.IsOnline ())
|
||||||
{
|
{
|
||||||
@@ -207,7 +195,6 @@ namespace data
|
|||||||
lastPublish = ts;
|
lastPublish = ts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ts - lastExploratory >= 30) // exploratory every 30 seconds
|
if (ts - lastExploratory >= 30) // exploratory every 30 seconds
|
||||||
{
|
{
|
||||||
auto numRouters = m_RouterInfos.size ();
|
auto numRouters = m_RouterInfos.size ();
|
||||||
@@ -1203,16 +1190,6 @@ namespace data
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const
|
|
||||||
{
|
|
||||||
return GetRandomRouter (
|
|
||||||
[v4, &excluded](std::shared_ptr<const RouterInfo> router)->bool
|
|
||||||
{
|
|
||||||
return !router->IsHidden () && router->IsECIES () &&
|
|
||||||
router->IsSSU2PeerTesting (v4) && !excluded.count (router->GetIdentHash ());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSUV6Router () const
|
std::shared_ptr<const RouterInfo> NetDb::GetRandomSSUV6Router () const
|
||||||
{
|
{
|
||||||
return GetRandomRouter (
|
return GetRandomRouter (
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ namespace data
|
|||||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
||||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith, bool reverse) const;
|
||||||
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
|
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
|
||||||
std::shared_ptr<const RouterInfo> GetRandomSSU2PeerTestRouter (bool v4, const std::set<IdentHash>& excluded) const;
|
|
||||||
std::shared_ptr<const RouterInfo> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later
|
std::shared_ptr<const RouterInfo> GetRandomSSUV6Router () const; // TODO: change to v6 peer test later
|
||||||
std::shared_ptr<const RouterInfo> GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const;
|
std::shared_ptr<const RouterInfo> GetRandomIntroducer (bool v4, const std::set<IdentHash>& excluded) const;
|
||||||
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
/**
|
|
||||||
* This code is licensed under the MCGSI Public License
|
|
||||||
* Copyright 2018 Jeff Becker
|
|
||||||
*
|
|
||||||
*Kovri go write your own code
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Poly1305.h"
|
#include "Poly1305.h"
|
||||||
|
/**
|
||||||
|
This code is licensed under the MCGSI Public License
|
||||||
|
Copyright 2018 Jeff Becker
|
||||||
|
|
||||||
|
Kovri go write your own code
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
#if !OPENSSL_AEAD_CHACHA20_POLY1305
|
#if !OPENSSL_AEAD_CHACHA20_POLY1305
|
||||||
namespace i2p
|
namespace i2p
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
* Kovri go write your own code
|
* Kovri go write your own code
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LIBI2PD_POLY1305_H
|
#ifndef LIBI2PD_POLY1305_H
|
||||||
#define LIBI2PD_POLY1305_H
|
#define LIBI2PD_POLY1305_H
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
@@ -29,8 +29,6 @@ namespace data
|
|||||||
const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
|
const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
|
||||||
|
|
||||||
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days)
|
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days)
|
||||||
const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 24 * 3600; // in seconds (1 day)
|
|
||||||
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3 * 3600; // in seconds (3 hours)
|
|
||||||
|
|
||||||
class RouterProfile
|
class RouterProfile
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1000,17 +1000,6 @@ namespace data
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RouterInfo::IsSSU2PeerTesting (bool v4) const
|
|
||||||
{
|
|
||||||
if (!(m_SupportedTransports & (v4 ? eSSU2V4 : eSSU2V6))) return false;
|
|
||||||
return (bool)GetAddress (
|
|
||||||
[v4](std::shared_ptr<const RouterInfo::Address> address)->bool
|
|
||||||
{
|
|
||||||
return (address->IsSSU2 ()) && address->IsPeerTesting () &&
|
|
||||||
((v4 && address->IsV4 ()) || (!v4 && address->IsV6 ())) && address->IsReachableSSU ();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::IsIntroducer (bool v4) const
|
bool RouterInfo::IsIntroducer (bool v4) const
|
||||||
{
|
{
|
||||||
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
|
if (!(m_SupportedTransports & (v4 ? eSSUV4 : eSSUV6))) return false;
|
||||||
|
|||||||
@@ -232,7 +232,6 @@ namespace data
|
|||||||
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
|
bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; };
|
||||||
bool IsEligibleFloodfill () const;
|
bool IsEligibleFloodfill () const;
|
||||||
bool IsPeerTesting (bool v4) const;
|
bool IsPeerTesting (bool v4) const;
|
||||||
bool IsSSU2PeerTesting (bool v4) const;
|
|
||||||
bool IsIntroducer (bool v4) const;
|
bool IsIntroducer (bool v4) const;
|
||||||
|
|
||||||
uint8_t GetCaps () const { return m_Caps; };
|
uint8_t GetCaps () const { return m_Caps; };
|
||||||
|
|||||||
1521
libi2pd/SSU2.cpp
1521
libi2pd/SSU2.cpp
File diff suppressed because it is too large
Load Diff
218
libi2pd/SSU2.h
218
libi2pd/SSU2.h
@@ -9,17 +9,228 @@
|
|||||||
#ifndef SSU2_H__
|
#ifndef SSU2_H__
|
||||||
#define SSU2_H__
|
#define SSU2_H__
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "util.h"
|
#include <boost/asio.hpp>
|
||||||
#include "SSU2Session.h"
|
#include "Crypto.h"
|
||||||
|
#include "RouterInfo.h"
|
||||||
|
#include "TransportSession.h"
|
||||||
|
|
||||||
namespace i2p
|
namespace i2p
|
||||||
{
|
{
|
||||||
namespace transport
|
namespace transport
|
||||||
{
|
{
|
||||||
|
const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||||
|
const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes
|
||||||
const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
||||||
|
const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // in seconds
|
||||||
|
const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds
|
||||||
const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
|
const size_t SSU2_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
|
||||||
const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
|
const size_t SSU2_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
|
||||||
|
const size_t SSU2_MTU = 1488;
|
||||||
|
const size_t SSU2_MAX_PAYLOAD_SIZE = SSU2_MTU - 32;
|
||||||
|
const int SSU2_RESEND_INTERVAL = 3; // in seconds
|
||||||
|
const int SSU2_MAX_NUM_RESENDS = 5;
|
||||||
|
const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
|
||||||
|
const size_t SSU2_MAX_WINDOW_SIZE = 128; // in packets
|
||||||
|
|
||||||
|
enum SSU2MessageType
|
||||||
|
{
|
||||||
|
eSSU2SessionRequest = 0,
|
||||||
|
eSSU2SessionCreated = 1,
|
||||||
|
eSSU2SessionConfirmed = 2,
|
||||||
|
eSSU2Data = 6,
|
||||||
|
eSSU2Retry = 9,
|
||||||
|
eSSU2TokenRequest = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SSU2BlockType
|
||||||
|
{
|
||||||
|
eSSU2BlkDateTime = 0,
|
||||||
|
eSSU2BlkOptions, // 1
|
||||||
|
eSSU2BlkRouterInfo, // 2
|
||||||
|
eSSU2BlkI2NPMessage, // 3
|
||||||
|
eSSU2BlkFirstFragment, // 4
|
||||||
|
eSSU2BlkFollowOnFragment, // 5
|
||||||
|
eSSU2BlkTermination, // 6
|
||||||
|
eSSU2BlkRelayRequest, // 7
|
||||||
|
eSSU2BlkRelayResponse, // 8
|
||||||
|
eSSU2BlkRelayIntro, // 9
|
||||||
|
eSSU2BlkPeerTest, // 10
|
||||||
|
eSSU2BlkNextNonce, // 11
|
||||||
|
eSSU2BlkAck, // 12
|
||||||
|
eSSU2BlkAddress, // 13
|
||||||
|
eSSU2BlkIntroKey, // 14
|
||||||
|
eSSU2BlkRelayTagRequest, // 15
|
||||||
|
eSSU2BlkRelayTag, // 16
|
||||||
|
eSSU2BlkNewToken, // 17
|
||||||
|
eSSU2BlkPathChallenge, // 18
|
||||||
|
eSSU2BlkPathResponse, // 19
|
||||||
|
eSSU2BlkFirstPacketNumber, // 20
|
||||||
|
eSSU2BlkPadding = 254
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SSU2SessionState
|
||||||
|
{
|
||||||
|
eSSU2SessionStateUnknown,
|
||||||
|
eSSU2SessionStateIntroduced,
|
||||||
|
eSSU2SessionStateEstablished,
|
||||||
|
eSSU2SessionStateTerminated,
|
||||||
|
eSSU2SessionStateFailed
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SSU2IncompleteMessage
|
||||||
|
{
|
||||||
|
struct Fragment
|
||||||
|
{
|
||||||
|
uint8_t buf[SSU2_MTU];
|
||||||
|
size_t len;
|
||||||
|
bool isLast;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<I2NPMessage> msg;
|
||||||
|
int nextFragmentNum;
|
||||||
|
uint32_t lastFragmentInsertTime; // in seconds
|
||||||
|
std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments;
|
||||||
|
};
|
||||||
|
|
||||||
|
// RouterInfo flags
|
||||||
|
const uint8_t SSU2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
|
||||||
|
const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02;
|
||||||
|
|
||||||
|
class SSU2Server;
|
||||||
|
class SSU2Session: public TransportSession, public std::enable_shared_from_this<SSU2Session>
|
||||||
|
{
|
||||||
|
union Header
|
||||||
|
{
|
||||||
|
uint64_t ll[2];
|
||||||
|
uint8_t buf[16];
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint64_t connID;
|
||||||
|
uint32_t packetNum;
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t flags[3];
|
||||||
|
} h;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SentPacket
|
||||||
|
{
|
||||||
|
uint8_t payload[SSU2_MAX_PAYLOAD_SIZE];
|
||||||
|
size_t payloadSize = 0;
|
||||||
|
uint32_t nextResendTime; // in seconds
|
||||||
|
int numResends = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SessionConfirmedFragment
|
||||||
|
{
|
||||||
|
Header header;
|
||||||
|
uint8_t payload[SSU2_MAX_PAYLOAD_SIZE];
|
||||||
|
size_t payloadSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::function<void ()> OnEstablished;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr,
|
||||||
|
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr);
|
||||||
|
~SSU2Session ();
|
||||||
|
|
||||||
|
void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; };
|
||||||
|
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; };
|
||||||
|
void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; };
|
||||||
|
|
||||||
|
void Connect ();
|
||||||
|
bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag);
|
||||||
|
void Terminate ();
|
||||||
|
void TerminateByTimeout ();
|
||||||
|
void CleanUp (uint64_t ts);
|
||||||
|
void FlushData ();
|
||||||
|
void Done () override;
|
||||||
|
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
|
||||||
|
void Resend (uint64_t ts);
|
||||||
|
bool IsEstablished () const { return m_State == eSSU2SessionStateEstablished; };
|
||||||
|
uint64_t GetConnID () const { return m_SourceConnID; };
|
||||||
|
SSU2SessionState GetState () const { return m_State; };
|
||||||
|
void SetState (SSU2SessionState state) { m_State = state; };
|
||||||
|
|
||||||
|
bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len);
|
||||||
|
bool ProcessSessionCreated (uint8_t * buf, size_t len);
|
||||||
|
bool ProcessSessionConfirmed (uint8_t * buf, size_t len);
|
||||||
|
bool ProcessRetry (uint8_t * buf, size_t len);
|
||||||
|
void ProcessData (uint8_t * buf, size_t len);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void Established ();
|
||||||
|
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
||||||
|
bool SendQueue ();
|
||||||
|
void SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
|
||||||
|
|
||||||
|
void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len);
|
||||||
|
void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len);
|
||||||
|
|
||||||
|
void SendSessionRequest (uint64_t token = 0);
|
||||||
|
void SendSessionCreated (const uint8_t * X);
|
||||||
|
void SendSessionConfirmed (const uint8_t * Y);
|
||||||
|
void KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba);
|
||||||
|
void SendTokenRequest ();
|
||||||
|
void SendRetry ();
|
||||||
|
uint32_t SendData (const uint8_t * buf, size_t len); // returns packet num
|
||||||
|
void SendQuickAck ();
|
||||||
|
void SendTermination ();
|
||||||
|
|
||||||
|
void HandlePayload (const uint8_t * buf, size_t len);
|
||||||
|
void HandleAck (const uint8_t * buf, size_t len);
|
||||||
|
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum);
|
||||||
|
bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep);
|
||||||
|
size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
|
||||||
|
std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo (const uint8_t * buf, size_t size);
|
||||||
|
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
||||||
|
bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate
|
||||||
|
void HandleFirstFragment (const uint8_t * buf, size_t len);
|
||||||
|
void HandleFollowOnFragment (const uint8_t * buf, size_t len);
|
||||||
|
bool ConcatOutOfSequenceFragments (std::shared_ptr<SSU2IncompleteMessage> m); // true if message complete
|
||||||
|
void HandleRelayRequest (const uint8_t * buf, size_t len);
|
||||||
|
void HandleRelayIntro (const uint8_t * buf, size_t len);
|
||||||
|
void HandleRelayResponse (const uint8_t * buf, size_t len);
|
||||||
|
|
||||||
|
size_t CreateAddressBlock (const boost::asio::ip::udp::endpoint& ep, uint8_t * buf, size_t len);
|
||||||
|
size_t CreateAckBlock (uint8_t * buf, size_t len);
|
||||||
|
size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0);
|
||||||
|
size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage>&& msg);
|
||||||
|
size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg);
|
||||||
|
size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg, uint8_t& fragmentNum, uint32_t msgID);
|
||||||
|
size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen);
|
||||||
|
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, uint32_t nonce); // Charlie
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
SSU2Server& m_Server;
|
||||||
|
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
||||||
|
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState;
|
||||||
|
std::unique_ptr<SessionConfirmedFragment> m_SessionConfirmedFragment1; // for Bob if applicable
|
||||||
|
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
|
||||||
|
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
|
||||||
|
uint64_t m_DestConnID, m_SourceConnID;
|
||||||
|
SSU2SessionState m_State;
|
||||||
|
uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];
|
||||||
|
uint32_t m_SendPacketNum, m_ReceivePacketNum;
|
||||||
|
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
|
||||||
|
std::map<uint32_t, std::shared_ptr<SentPacket> > m_SentPackets; // packetNum -> packet
|
||||||
|
std::map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // I2NP
|
||||||
|
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
|
||||||
|
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||||
|
i2p::I2NPMessagesHandler m_Handler;
|
||||||
|
bool m_IsDataReceived;
|
||||||
|
size_t m_WindowSize;
|
||||||
|
uint32_t m_RelayTag; // between Bob and Charlie
|
||||||
|
OnEstablished m_OnEstablished; // callback from Established
|
||||||
|
};
|
||||||
|
|
||||||
class SSU2Server: private i2p::util::RunnableServiceWithWork
|
class SSU2Server: private i2p::util::RunnableServiceWithWork
|
||||||
{
|
{
|
||||||
@@ -53,7 +264,6 @@ namespace transport
|
|||||||
void RemoveSession (uint64_t connID);
|
void RemoveSession (uint64_t connID);
|
||||||
void AddSessionByRouterHash (std::shared_ptr<SSU2Session> session);
|
void AddSessionByRouterHash (std::shared_ptr<SSU2Session> session);
|
||||||
void AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session);
|
void AddPendingOutgoingSession (std::shared_ptr<SSU2Session> session);
|
||||||
std::shared_ptr<SSU2Session> FindSession (const i2p::data::IdentHash& ident) const;
|
|
||||||
|
|
||||||
void AddRelay (uint32_t tag, std::shared_ptr<SSU2Session> relay);
|
void AddRelay (uint32_t tag, std::shared_ptr<SSU2Session> relay);
|
||||||
void RemoveRelay (uint32_t tag);
|
void RemoveRelay (uint32_t tag);
|
||||||
@@ -63,10 +273,10 @@ namespace transport
|
|||||||
const boost::asio::ip::udp::endpoint& to);
|
const boost::asio::ip::udp::endpoint& to);
|
||||||
void Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen,
|
void Send (const uint8_t * header, size_t headerLen, const uint8_t * headerX, size_t headerXLen,
|
||||||
const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to);
|
const uint8_t * payload, size_t payloadLen, const boost::asio::ip::udp::endpoint& to);
|
||||||
|
void SendHolePunch (const boost::asio::ip::udp::endpoint& to);
|
||||||
|
|
||||||
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
bool CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||||
std::shared_ptr<const i2p::data::RouterInfo::Address> address);
|
std::shared_ptr<const i2p::data::RouterInfo::Address> address);
|
||||||
bool StartPeerTest (std::shared_ptr<const i2p::data::RouterInfo> router, bool v4);
|
|
||||||
|
|
||||||
void UpdateOutgoingToken (const boost::asio::ip::udp::endpoint& ep, uint64_t token, uint32_t exp);
|
void UpdateOutgoingToken (const boost::asio::ip::udp::endpoint& ep, uint64_t token, uint32_t exp);
|
||||||
uint64_t FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep) const;
|
uint64_t FindOutgoingToken (const boost::asio::ip::udp::endpoint& ep) const;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,273 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2022, The PurpleI2P Project
|
|
||||||
*
|
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
|
||||||
*
|
|
||||||
* See full license text in LICENSE file at top of project tree
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SSU2_SESSION_H__
|
|
||||||
#define SSU2_SESSION_H__
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <list>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Crypto.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "TransportSession.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
const int SSU2_CONNECT_TIMEOUT = 5; // 5 seconds
|
|
||||||
const int SSU2_TERMINATION_TIMEOUT = 330; // 5.5 minutes
|
|
||||||
const int SSU2_TOKEN_EXPIRATION_TIMEOUT = 9; // in seconds
|
|
||||||
const int SSU2_RELAY_NONCE_EXPIRATION_TIMEOUT = 10; // in seconds
|
|
||||||
const int SSU2_PEER_TEST_EXPIRATION_TIMEOUT = 60; // 60 seconds
|
|
||||||
const size_t SSU2_MTU = 1488;
|
|
||||||
const size_t SSU2_MAX_PAYLOAD_SIZE = SSU2_MTU - 32;
|
|
||||||
const int SSU2_RESEND_INTERVAL = 3; // in seconds
|
|
||||||
const int SSU2_MAX_NUM_RESENDS = 5;
|
|
||||||
const int SSU2_INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
|
|
||||||
const size_t SSU2_MAX_WINDOW_SIZE = 128; // in packets
|
|
||||||
|
|
||||||
enum SSU2MessageType
|
|
||||||
{
|
|
||||||
eSSU2SessionRequest = 0,
|
|
||||||
eSSU2SessionCreated = 1,
|
|
||||||
eSSU2SessionConfirmed = 2,
|
|
||||||
eSSU2Data = 6,
|
|
||||||
eSSU2PeerTest = 7,
|
|
||||||
eSSU2Retry = 9,
|
|
||||||
eSSU2TokenRequest = 10,
|
|
||||||
eSSU2HolePunch = 11
|
|
||||||
};
|
|
||||||
|
|
||||||
enum SSU2BlockType
|
|
||||||
{
|
|
||||||
eSSU2BlkDateTime = 0,
|
|
||||||
eSSU2BlkOptions, // 1
|
|
||||||
eSSU2BlkRouterInfo, // 2
|
|
||||||
eSSU2BlkI2NPMessage, // 3
|
|
||||||
eSSU2BlkFirstFragment, // 4
|
|
||||||
eSSU2BlkFollowOnFragment, // 5
|
|
||||||
eSSU2BlkTermination, // 6
|
|
||||||
eSSU2BlkRelayRequest, // 7
|
|
||||||
eSSU2BlkRelayResponse, // 8
|
|
||||||
eSSU2BlkRelayIntro, // 9
|
|
||||||
eSSU2BlkPeerTest, // 10
|
|
||||||
eSSU2BlkNextNonce, // 11
|
|
||||||
eSSU2BlkAck, // 12
|
|
||||||
eSSU2BlkAddress, // 13
|
|
||||||
eSSU2BlkIntroKey, // 14
|
|
||||||
eSSU2BlkRelayTagRequest, // 15
|
|
||||||
eSSU2BlkRelayTag, // 16
|
|
||||||
eSSU2BlkNewToken, // 17
|
|
||||||
eSSU2BlkPathChallenge, // 18
|
|
||||||
eSSU2BlkPathResponse, // 19
|
|
||||||
eSSU2BlkFirstPacketNumber, // 20
|
|
||||||
eSSU2BlkPadding = 254
|
|
||||||
};
|
|
||||||
|
|
||||||
enum SSU2SessionState
|
|
||||||
{
|
|
||||||
eSSU2SessionStateUnknown,
|
|
||||||
eSSU2SessionStateIntroduced,
|
|
||||||
eSSU2SessionStatePeerTest,
|
|
||||||
eSSU2SessionStateEstablished,
|
|
||||||
eSSU2SessionStateTerminated,
|
|
||||||
eSSU2SessionStateFailed
|
|
||||||
};
|
|
||||||
|
|
||||||
enum SSU2PeerTestCode
|
|
||||||
{
|
|
||||||
eSSU2PeerTestCodeAccept = 0,
|
|
||||||
eSSU2PeerTestCodeBobReasonUnspecified = 1,
|
|
||||||
eSSU2PeerTestCodeBobNoCharlieAvailable = 2,
|
|
||||||
eSSU2PeerTestCodeBobLimitExceeded = 3,
|
|
||||||
eSSU2PeerTestCodeBobSignatureFailure = 4,
|
|
||||||
eSSU2PeerTestCodeCharlieReasonUnspecified = 64,
|
|
||||||
eSSU2PeerTestCodeCharlieUnsupportedAddress = 65,
|
|
||||||
eSSU2PeerTestCodeCharlieLimitExceeded = 66,
|
|
||||||
eSSU2PeerTestCodeCharlieSignatureFailure = 67,
|
|
||||||
eSSU2PeerTestCodeCharlieAliceIsAlreadyConnected = 68,
|
|
||||||
eSSU2PeerTestCodeCharlieAliceIsBanned = 69,
|
|
||||||
eSSU2PeerTestCodeCharlieAliceIsUnknown = 70,
|
|
||||||
eSSU2PeerTestCodeUnspecified = 128
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SSU2IncompleteMessage
|
|
||||||
{
|
|
||||||
struct Fragment
|
|
||||||
{
|
|
||||||
uint8_t buf[SSU2_MTU];
|
|
||||||
size_t len;
|
|
||||||
bool isLast;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> msg;
|
|
||||||
int nextFragmentNum;
|
|
||||||
uint32_t lastFragmentInsertTime; // in seconds
|
|
||||||
std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments;
|
|
||||||
};
|
|
||||||
|
|
||||||
// RouterInfo flags
|
|
||||||
const uint8_t SSU2_ROUTER_INFO_FLAG_REQUEST_FLOOD = 0x01;
|
|
||||||
const uint8_t SSU2_ROUTER_INFO_FLAG_GZIP = 0x02;
|
|
||||||
|
|
||||||
class SSU2Server;
|
|
||||||
class SSU2Session: public TransportSession, public std::enable_shared_from_this<SSU2Session>
|
|
||||||
{
|
|
||||||
union Header
|
|
||||||
{
|
|
||||||
uint64_t ll[2];
|
|
||||||
uint8_t buf[16];
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
uint64_t connID;
|
|
||||||
uint32_t packetNum;
|
|
||||||
uint8_t type;
|
|
||||||
uint8_t flags[3];
|
|
||||||
} h;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SentPacket
|
|
||||||
{
|
|
||||||
uint8_t payload[SSU2_MAX_PAYLOAD_SIZE];
|
|
||||||
size_t payloadSize = 0;
|
|
||||||
uint32_t nextResendTime; // in seconds
|
|
||||||
int numResends = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SessionConfirmedFragment
|
|
||||||
{
|
|
||||||
Header header;
|
|
||||||
uint8_t payload[SSU2_MAX_PAYLOAD_SIZE];
|
|
||||||
size_t payloadSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::function<void ()> OnEstablished;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr,
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo::Address> addr = nullptr);
|
|
||||||
~SSU2Session ();
|
|
||||||
|
|
||||||
void SetRemoteEndpoint (const boost::asio::ip::udp::endpoint& ep) { m_RemoteEndpoint = ep; };
|
|
||||||
const boost::asio::ip::udp::endpoint& GetRemoteEndpoint () const { return m_RemoteEndpoint; };
|
|
||||||
void SetOnEstablished (OnEstablished e) { m_OnEstablished = e; };
|
|
||||||
|
|
||||||
void Connect ();
|
|
||||||
bool Introduce (std::shared_ptr<SSU2Session> session, uint32_t relayTag);
|
|
||||||
void SendPeerTest (); // Alice, Data message
|
|
||||||
void Terminate ();
|
|
||||||
void TerminateByTimeout ();
|
|
||||||
void CleanUp (uint64_t ts);
|
|
||||||
void FlushData ();
|
|
||||||
void Done () override;
|
|
||||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) override;
|
|
||||||
void Resend (uint64_t ts);
|
|
||||||
bool IsEstablished () const { return m_State == eSSU2SessionStateEstablished; };
|
|
||||||
uint64_t GetConnID () const { return m_SourceConnID; };
|
|
||||||
SSU2SessionState GetState () const { return m_State; };
|
|
||||||
void SetState (SSU2SessionState state) { m_State = state; };
|
|
||||||
|
|
||||||
bool ProcessFirstIncomingMessage (uint64_t connID, uint8_t * buf, size_t len);
|
|
||||||
bool ProcessSessionCreated (uint8_t * buf, size_t len);
|
|
||||||
bool ProcessSessionConfirmed (uint8_t * buf, size_t len);
|
|
||||||
bool ProcessRetry (uint8_t * buf, size_t len);
|
|
||||||
bool ProcessHolePunch (uint8_t * buf, size_t len);
|
|
||||||
bool ProcessPeerTest (uint8_t * buf, size_t len);
|
|
||||||
void ProcessData (uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Established ();
|
|
||||||
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
|
||||||
bool SendQueue ();
|
|
||||||
void SendFragmentedMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
|
|
||||||
void ProcessSessionRequest (Header& header, uint8_t * buf, size_t len);
|
|
||||||
void ProcessTokenRequest (Header& header, uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
void SendSessionRequest (uint64_t token = 0);
|
|
||||||
void SendSessionCreated (const uint8_t * X);
|
|
||||||
void SendSessionConfirmed (const uint8_t * Y);
|
|
||||||
void KDFDataPhase (uint8_t * keydata_ab, uint8_t * keydata_ba);
|
|
||||||
void SendTokenRequest ();
|
|
||||||
void SendRetry ();
|
|
||||||
uint32_t SendData (const uint8_t * buf, size_t len); // returns packet num
|
|
||||||
void SendQuickAck ();
|
|
||||||
void SendTermination ();
|
|
||||||
void SendHolePunch (uint32_t nonce, const boost::asio::ip::udp::endpoint& ep, const uint8_t * introKey);
|
|
||||||
void SendPeerTest (uint8_t msg, const uint8_t * signedData, size_t signedDataLen, const uint8_t * introKey); // PeerTest message
|
|
||||||
|
|
||||||
void HandlePayload (const uint8_t * buf, size_t len);
|
|
||||||
void HandleAck (const uint8_t * buf, size_t len);
|
|
||||||
void HandleAckRange (uint32_t firstPacketNum, uint32_t lastPacketNum);
|
|
||||||
bool ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep);
|
|
||||||
size_t CreateEndpoint (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo::Address> FindLocalAddress () const;
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> ExtractRouterInfo (const uint8_t * buf, size_t size);
|
|
||||||
void CreateNonce (uint64_t seqn, uint8_t * nonce);
|
|
||||||
bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate
|
|
||||||
void HandleFirstFragment (const uint8_t * buf, size_t len);
|
|
||||||
void HandleFollowOnFragment (const uint8_t * buf, size_t len);
|
|
||||||
bool ConcatOutOfSequenceFragments (std::shared_ptr<SSU2IncompleteMessage> m); // true if message complete
|
|
||||||
void HandleRelayRequest (const uint8_t * buf, size_t len);
|
|
||||||
void HandleRelayIntro (const uint8_t * buf, size_t len);
|
|
||||||
void HandleRelayResponse (const uint8_t * buf, size_t len);
|
|
||||||
void HandlePeerTest (const uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
size_t CreateAddressBlock (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& ep);
|
|
||||||
size_t CreateRouterInfoBlock (uint8_t * buf, size_t len, std::shared_ptr<const i2p::data::RouterInfo> r);
|
|
||||||
size_t CreateAckBlock (uint8_t * buf, size_t len);
|
|
||||||
size_t CreatePaddingBlock (uint8_t * buf, size_t len, size_t minSize = 0);
|
|
||||||
size_t CreateI2NPBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage>&& msg);
|
|
||||||
size_t CreateFirstFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg);
|
|
||||||
size_t CreateFollowOnFragmentBlock (uint8_t * buf, size_t len, std::shared_ptr<I2NPMessage> msg, uint8_t& fragmentNum, uint32_t msgID);
|
|
||||||
size_t CreateRelayIntroBlock (uint8_t * buf, size_t len, const uint8_t * introData, size_t introDataLen);
|
|
||||||
size_t CreateRelayResponseBlock (uint8_t * buf, size_t len, uint32_t nonce); // Charlie
|
|
||||||
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint8_t msg, SSU2PeerTestCode code, const uint8_t * routerHash, const uint8_t * signedData, size_t signedDataLen);
|
|
||||||
size_t CreatePeerTestBlock (uint8_t * buf, size_t len, uint32_t nonce); // Alice
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
SSU2Server& m_Server;
|
|
||||||
std::shared_ptr<i2p::crypto::X25519Keys> m_EphemeralKeys;
|
|
||||||
std::unique_ptr<i2p::crypto::NoiseSymmetricState> m_NoiseState;
|
|
||||||
std::unique_ptr<SessionConfirmedFragment> m_SessionConfirmedFragment1; // for Bob if applicable
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo::Address> m_Address;
|
|
||||||
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
|
|
||||||
uint64_t m_DestConnID, m_SourceConnID;
|
|
||||||
SSU2SessionState m_State;
|
|
||||||
uint8_t m_KeyDataSend[64], m_KeyDataReceive[64];
|
|
||||||
uint32_t m_SendPacketNum, m_ReceivePacketNum;
|
|
||||||
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
|
|
||||||
std::map<uint32_t, std::shared_ptr<SentPacket> > m_SentPackets; // packetNum -> packet
|
|
||||||
std::map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // I2NP
|
|
||||||
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
|
|
||||||
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_PeerTests; // same as for relay sessions
|
|
||||||
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
|
||||||
i2p::I2NPMessagesHandler m_Handler;
|
|
||||||
bool m_IsDataReceived;
|
|
||||||
size_t m_WindowSize;
|
|
||||||
uint32_t m_RelayTag; // between Bob and Charlie
|
|
||||||
OnEstablished m_OnEstablished; // callback from Established
|
|
||||||
};
|
|
||||||
|
|
||||||
inline uint64_t CreateHeaderMask (const uint8_t * kh, const uint8_t * nonce)
|
|
||||||
{
|
|
||||||
uint64_t data = 0;
|
|
||||||
i2p::crypto::ChaCha20 ((uint8_t *)&data, 8, kh, nonce, (uint8_t *)&data);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1285,13 +1285,7 @@ namespace stream
|
|||||||
auto it = m_Streams.find (recvStreamID);
|
auto it = m_Streams.find (recvStreamID);
|
||||||
if (it == m_Streams.end ())
|
if (it == m_Streams.end ())
|
||||||
return false;
|
return false;
|
||||||
auto s = it->second;
|
DeleteStream (it->second);
|
||||||
m_Owner->GetService ().post ([this, s] ()
|
|
||||||
{
|
|
||||||
s->Close (); // try to send FIN
|
|
||||||
s->Terminate (false);
|
|
||||||
DeleteStream (s);
|
|
||||||
});
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -268,7 +268,6 @@ namespace transport
|
|||||||
|
|
||||||
// start servers
|
// start servers
|
||||||
if (m_NTCP2Server) m_NTCP2Server->Start ();
|
if (m_NTCP2Server) m_NTCP2Server->Start ();
|
||||||
if (m_SSU2Server) m_SSU2Server->Start ();
|
|
||||||
if (m_SSUServer)
|
if (m_SSUServer)
|
||||||
{
|
{
|
||||||
LogPrint (eLogInfo, "Transports: Start listening UDP port ", ssuPort);
|
LogPrint (eLogInfo, "Transports: Start listening UDP port ", ssuPort);
|
||||||
@@ -285,6 +284,7 @@ namespace transport
|
|||||||
}
|
}
|
||||||
if (m_SSUServer) DetectExternalIP ();
|
if (m_SSUServer) DetectExternalIP ();
|
||||||
}
|
}
|
||||||
|
if (m_SSU2Server) m_SSU2Server->Start ();
|
||||||
|
|
||||||
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
|
m_PeerCleanupTimer->expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
|
||||||
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
|
m_PeerCleanupTimer->async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
|
||||||
@@ -654,16 +654,6 @@ namespace transport
|
|||||||
}
|
}
|
||||||
if (!statusChanged)
|
if (!statusChanged)
|
||||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4");
|
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv4");
|
||||||
|
|
||||||
// SSU2
|
|
||||||
if (m_SSU2Server)
|
|
||||||
{
|
|
||||||
excluded.clear ();
|
|
||||||
excluded.insert (i2p::context.GetIdentHash ());
|
|
||||||
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (true, excluded); // v4
|
|
||||||
if (router)
|
|
||||||
m_SSU2Server->StartPeerTest (router, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (ipv6 && i2p::context.SupportsV6 ())
|
if (ipv6 && i2p::context.SupportsV6 ())
|
||||||
{
|
{
|
||||||
@@ -691,16 +681,6 @@ namespace transport
|
|||||||
}
|
}
|
||||||
if (!statusChanged)
|
if (!statusChanged)
|
||||||
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6");
|
LogPrint (eLogWarning, "Transports: Can't find routers for peer test IPv6");
|
||||||
|
|
||||||
// SSU2
|
|
||||||
if (m_SSU2Server)
|
|
||||||
{
|
|
||||||
excluded.clear ();
|
|
||||||
excluded.insert (i2p::context.GetIdentHash ());
|
|
||||||
auto router = i2p::data::netdb.GetRandomSSU2PeerTestRouter (false, excluded); // v6
|
|
||||||
if (router)
|
|
||||||
m_SSU2Server->StartPeerTest (router, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,10 @@
|
|||||||
#include <iphlpapi.h>
|
#include <iphlpapi.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma comment(lib, "IPHLPAPI.lib")
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
|
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
|
||||||
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
|
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
|
#define MAKE_VERSION_NUMBER(a,b,c) ((a*100+b)*100+c)
|
||||||
|
|
||||||
#define I2PD_VERSION_MAJOR 2
|
#define I2PD_VERSION_MAJOR 2
|
||||||
#define I2PD_VERSION_MINOR 42
|
#define I2PD_VERSION_MINOR 41
|
||||||
#define I2PD_VERSION_MICRO 1
|
#define I2PD_VERSION_MICRO 0
|
||||||
#define I2PD_VERSION_PATCH 0
|
#define I2PD_VERSION_PATCH 0
|
||||||
#ifdef GITVER
|
#ifdef GITVER
|
||||||
#define I2PD_VERSION GITVER
|
#define I2PD_VERSION GITVER
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
#define I2P_VERSION_MAJOR 0
|
#define I2P_VERSION_MAJOR 0
|
||||||
#define I2P_VERSION_MINOR 9
|
#define I2P_VERSION_MINOR 9
|
||||||
#define I2P_VERSION_MICRO 54
|
#define I2P_VERSION_MICRO 53
|
||||||
#define I2P_VERSION_PATCH 0
|
#define I2P_VERSION_PATCH 0
|
||||||
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
#define I2P_VERSION MAKE_VERSION(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
||||||
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
#define I2P_VERSION_NUMBER MAKE_VERSION_NUMBER(I2P_VERSION_MAJOR, I2P_VERSION_MINOR, I2P_VERSION_MICRO)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
@@ -299,8 +299,7 @@ namespace client
|
|||||||
}
|
}
|
||||||
|
|
||||||
AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false),
|
AddressBook::AddressBook (): m_Storage(nullptr), m_IsLoaded (false), m_IsDownloading (false),
|
||||||
m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr),
|
m_NumRetries (0), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
|
||||||
m_IsEnabled (true)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,9 +309,6 @@ namespace client
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AddressBook::Start ()
|
void AddressBook::Start ()
|
||||||
{
|
|
||||||
i2p::config::GetOption("addressbook.enabled", m_IsEnabled);
|
|
||||||
if (m_IsEnabled)
|
|
||||||
{
|
{
|
||||||
if (!m_Storage)
|
if (!m_Storage)
|
||||||
m_Storage = new AddressBookFilesystemStorage;
|
m_Storage = new AddressBookFilesystemStorage;
|
||||||
@@ -321,7 +317,6 @@ namespace client
|
|||||||
StartSubscriptions ();
|
StartSubscriptions ();
|
||||||
StartLookups ();
|
StartLookups ();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBook::StartResolvers ()
|
void AddressBook::StartResolvers ()
|
||||||
{
|
{
|
||||||
@@ -375,7 +370,6 @@ namespace client
|
|||||||
pos = address.find (".i2p");
|
pos = address.find (".i2p");
|
||||||
if (pos != std::string::npos)
|
if (pos != std::string::npos)
|
||||||
{
|
{
|
||||||
if (!m_IsEnabled) return nullptr;
|
|
||||||
auto addr = FindAddress (address);
|
auto addr = FindAddress (address);
|
||||||
if (!addr)
|
if (!addr)
|
||||||
LookupAddress (address); // TODO:
|
LookupAddress (address); // TODO:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
@@ -126,7 +126,6 @@ namespace client
|
|||||||
std::vector<std::shared_ptr<AddressBookSubscription> > m_Subscriptions;
|
std::vector<std::shared_ptr<AddressBookSubscription> > m_Subscriptions;
|
||||||
std::shared_ptr<AddressBookSubscription> m_DefaultSubscription; // in case if we don't know any addresses yet
|
std::shared_ptr<AddressBookSubscription> m_DefaultSubscription; // in case if we don't know any addresses yet
|
||||||
boost::asio::deadline_timer * m_SubscriptionsUpdateTimer;
|
boost::asio::deadline_timer * m_SubscriptionsUpdateTimer;
|
||||||
bool m_IsEnabled;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddressBookSubscription
|
class AddressBookSubscription
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
* Copyright (c) 2013-2020, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
* See full license text in LICENSE file at top of project tree
|
* See full license text in LICENSE file at top of project tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef WITH_BOB
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "ClientContext.h"
|
#include "ClientContext.h"
|
||||||
@@ -704,7 +706,7 @@ namespace client
|
|||||||
msg += operand;
|
msg += operand;
|
||||||
*(const_cast<char *>(value)) = '=';
|
*(const_cast<char *>(value)) = '=';
|
||||||
msg += " set to ";
|
msg += " set to ";
|
||||||
msg += value + 1;
|
msg += value;
|
||||||
SendReplyOK (msg.c_str ());
|
SendReplyOK (msg.c_str ());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -884,3 +886,4 @@ namespace client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // WITH_BOB
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
* See full license text in LICENSE file at top of project tree
|
* See full license text in LICENSE file at top of project tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef WITH_BOB
|
||||||
|
|
||||||
#ifndef BOB_H__
|
#ifndef BOB_H__
|
||||||
#define BOB_H__
|
#define BOB_H__
|
||||||
|
|
||||||
@@ -277,5 +279,5 @@ namespace client
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
#endif // WITH_BOB
|
||||||
|
|||||||
@@ -26,8 +26,16 @@ namespace client
|
|||||||
ClientContext context;
|
ClientContext context;
|
||||||
|
|
||||||
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
|
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
|
||||||
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr),
|
m_HttpProxy (nullptr), m_SocksProxy (nullptr)
|
||||||
m_BOBCommandChannel (nullptr), m_I2CPServer (nullptr)
|
#ifdef WITH_SAM
|
||||||
|
, m_SamBridge (nullptr)
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_BOB
|
||||||
|
, m_BOBCommandChannel (nullptr)
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_I2CP
|
||||||
|
, m_I2CPServer (nullptr)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,9 +43,15 @@ namespace client
|
|||||||
{
|
{
|
||||||
delete m_HttpProxy;
|
delete m_HttpProxy;
|
||||||
delete m_SocksProxy;
|
delete m_SocksProxy;
|
||||||
|
#ifdef WITH_SAM
|
||||||
delete m_SamBridge;
|
delete m_SamBridge;
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_BOB
|
||||||
delete m_BOBCommandChannel;
|
delete m_BOBCommandChannel;
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_I2CP
|
||||||
delete m_I2CPServer;
|
delete m_I2CPServer;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientContext::Start ()
|
void ClientContext::Start ()
|
||||||
@@ -58,6 +72,7 @@ namespace client
|
|||||||
// I2P tunnels
|
// I2P tunnels
|
||||||
ReadTunnels ();
|
ReadTunnels ();
|
||||||
|
|
||||||
|
#ifdef WITH_SAM
|
||||||
// SAM
|
// SAM
|
||||||
bool sam; i2p::config::GetOption("sam.enabled", sam);
|
bool sam; i2p::config::GetOption("sam.enabled", sam);
|
||||||
if (sam)
|
if (sam)
|
||||||
@@ -77,7 +92,9 @@ namespace client
|
|||||||
ThrowFatal ("Unable to start SAM bridge at ", samAddr, ":", samPort, ": ", e.what ());
|
ThrowFatal ("Unable to start SAM bridge at ", samAddr, ":", samPort, ": ", e.what ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_BOB
|
||||||
// BOB
|
// BOB
|
||||||
bool bob; i2p::config::GetOption("bob.enabled", bob);
|
bool bob; i2p::config::GetOption("bob.enabled", bob);
|
||||||
if (bob) {
|
if (bob) {
|
||||||
@@ -95,7 +112,9 @@ namespace client
|
|||||||
ThrowFatal ("Unable to start BOB bridge at ", bobAddr, ":", bobPort, ": ", e.what ());
|
ThrowFatal ("Unable to start BOB bridge at ", bobAddr, ":", bobPort, ": ", e.what ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_I2CP
|
||||||
// I2CP
|
// I2CP
|
||||||
bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp);
|
bool i2cp; i2p::config::GetOption("i2cp.enabled", i2cp);
|
||||||
if (i2cp)
|
if (i2cp)
|
||||||
@@ -115,6 +134,7 @@ namespace client
|
|||||||
ThrowFatal ("Unable to start I2CP at ", i2cpAddr, ":", i2cpPort, ": ", e.what ());
|
ThrowFatal ("Unable to start I2CP at ", i2cpAddr, ":", i2cpPort, ": ", e.what ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
m_AddressBook.StartResolvers ();
|
m_AddressBook.StartResolvers ();
|
||||||
|
|
||||||
@@ -158,6 +178,7 @@ namespace client
|
|||||||
}
|
}
|
||||||
m_ServerTunnels.clear ();
|
m_ServerTunnels.clear ();
|
||||||
|
|
||||||
|
#ifdef WITH_SAM
|
||||||
if (m_SamBridge)
|
if (m_SamBridge)
|
||||||
{
|
{
|
||||||
LogPrint(eLogInfo, "Clients: Stopping SAM bridge");
|
LogPrint(eLogInfo, "Clients: Stopping SAM bridge");
|
||||||
@@ -165,7 +186,9 @@ namespace client
|
|||||||
delete m_SamBridge;
|
delete m_SamBridge;
|
||||||
m_SamBridge = nullptr;
|
m_SamBridge = nullptr;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_BOB
|
||||||
if (m_BOBCommandChannel)
|
if (m_BOBCommandChannel)
|
||||||
{
|
{
|
||||||
LogPrint(eLogInfo, "Clients: Stopping BOB command channel");
|
LogPrint(eLogInfo, "Clients: Stopping BOB command channel");
|
||||||
@@ -173,7 +196,9 @@ namespace client
|
|||||||
delete m_BOBCommandChannel;
|
delete m_BOBCommandChannel;
|
||||||
m_BOBCommandChannel = nullptr;
|
m_BOBCommandChannel = nullptr;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_I2CP
|
||||||
if (m_I2CPServer)
|
if (m_I2CPServer)
|
||||||
{
|
{
|
||||||
LogPrint(eLogInfo, "Clients: Stopping I2CP");
|
LogPrint(eLogInfo, "Clients: Stopping I2CP");
|
||||||
@@ -181,6 +206,7 @@ namespace client
|
|||||||
delete m_I2CPServer;
|
delete m_I2CPServer;
|
||||||
m_I2CPServer = nullptr;
|
m_I2CPServer = nullptr;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
LogPrint(eLogInfo, "Clients: Stopping AddressBook");
|
LogPrint(eLogInfo, "Clients: Stopping AddressBook");
|
||||||
m_AddressBook.Stop ();
|
m_AddressBook.Stop ();
|
||||||
@@ -608,29 +634,21 @@ namespace client
|
|||||||
if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) {
|
if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) {
|
||||||
// udp client
|
// udp client
|
||||||
// TODO: hostnames
|
// TODO: hostnames
|
||||||
boost::asio::ip::udp::endpoint end (boost::asio::ip::address::from_string(address), port);
|
boost::asio::ip::udp::endpoint end(boost::asio::ip::address::from_string(address), port);
|
||||||
if (!localDestination)
|
if (!localDestination)
|
||||||
localDestination = m_SharedLocalDestination;
|
localDestination = m_SharedLocalDestination;
|
||||||
|
|
||||||
bool gzip = section.second.get (I2P_CLIENT_TUNNEL_GZIP, true);
|
bool gzip = section.second.get (I2P_CLIENT_TUNNEL_GZIP, true);
|
||||||
auto clientTunnel = std::make_shared<I2PUDPClientTunnel> (name, dest, end, localDestination, destinationPort, gzip);
|
auto clientTunnel = std::make_shared<I2PUDPClientTunnel>(name, dest, end, localDestination, destinationPort, gzip);
|
||||||
|
|
||||||
auto ins = m_ClientForwards.insert (std::make_pair (end, clientTunnel));
|
auto ins = m_ClientForwards.insert(std::make_pair(end, clientTunnel));
|
||||||
if (ins.second)
|
if (ins.second)
|
||||||
{
|
{
|
||||||
clientTunnel->Start ();
|
clientTunnel->Start();
|
||||||
numClientTunnels++;
|
numClientTunnels++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: update
|
|
||||||
if (ins.first->second->GetLocalDestination () != clientTunnel->GetLocalDestination ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Clients: I2P UDP client tunnel destination updated");
|
|
||||||
ins.first->second->Stop ();
|
|
||||||
ins.first->second->SetLocalDestination (clientTunnel->GetLocalDestination ());
|
|
||||||
ins.first->second->Start ();
|
|
||||||
}
|
|
||||||
ins.first->second->isUpdated = true;
|
ins.first->second->isUpdated = true;
|
||||||
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
|
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
|
||||||
}
|
}
|
||||||
@@ -864,8 +882,6 @@ namespace client
|
|||||||
uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort);
|
uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort);
|
||||||
std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL);
|
std::string httpOutProxyURL; i2p::config::GetOption("httpproxy.outproxy", httpOutProxyURL);
|
||||||
bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper);
|
bool httpAddresshelper; i2p::config::GetOption("httpproxy.addresshelper", httpAddresshelper);
|
||||||
if (httpAddresshelper)
|
|
||||||
i2p::config::GetOption("addressbook.enabled", httpAddresshelper); // addresshelper is not supported without address book
|
|
||||||
i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType);
|
i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType);
|
||||||
LogPrint(eLogInfo, "Clients: Starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
|
LogPrint(eLogInfo, "Clients: Starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
|
||||||
if (httpProxyKeys.length () > 0)
|
if (httpProxyKeys.length () > 0)
|
||||||
@@ -986,11 +1002,11 @@ namespace client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write correct UDP tunnels stop
|
/* // TODO: Write correct UDP tunnels stop
|
||||||
for (auto it = m_ClientForwards.begin (); it != m_ClientForwards.end ();)
|
for (auto it = m_ClientForwards.begin (); it != m_ClientForwards.end ();)
|
||||||
{
|
{
|
||||||
if(clean && !it->second->isUpdated) {
|
if(clean && !it->second->isUpdated) {
|
||||||
it->second->Stop ();
|
it->second = nullptr;
|
||||||
it = m_ClientForwards.erase(it);
|
it = m_ClientForwards.erase(it);
|
||||||
} else {
|
} else {
|
||||||
it->second->isUpdated = false;
|
it->second->isUpdated = false;
|
||||||
@@ -1001,13 +1017,13 @@ namespace client
|
|||||||
for (auto it = m_ServerForwards.begin (); it != m_ServerForwards.end ();)
|
for (auto it = m_ServerForwards.begin (); it != m_ServerForwards.end ();)
|
||||||
{
|
{
|
||||||
if(clean && !it->second->isUpdated) {
|
if(clean && !it->second->isUpdated) {
|
||||||
it->second->Stop ();
|
it->second = nullptr;
|
||||||
it = m_ServerForwards.erase(it);
|
it = m_ServerForwards.erase(it);
|
||||||
} else {
|
} else {
|
||||||
it->second->isUpdated = false;
|
it->second->isUpdated = false;
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
@@ -18,9 +18,19 @@
|
|||||||
#include "HTTPProxy.h"
|
#include "HTTPProxy.h"
|
||||||
#include "SOCKS.h"
|
#include "SOCKS.h"
|
||||||
#include "I2PTunnel.h"
|
#include "I2PTunnel.h"
|
||||||
|
|
||||||
|
#ifdef WITH_SAM
|
||||||
#include "SAM.h"
|
#include "SAM.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_BOB
|
||||||
#include "BOB.h"
|
#include "BOB.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_I2CP
|
||||||
#include "I2CP.h"
|
#include "I2CP.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "AddressBook.h"
|
#include "AddressBook.h"
|
||||||
#include "I18N_langs.h"
|
#include "I18N_langs.h"
|
||||||
|
|
||||||
@@ -76,31 +86,45 @@ namespace client
|
|||||||
void ReloadConfig ();
|
void ReloadConfig ();
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> GetSharedLocalDestination () const { return m_SharedLocalDestination; };
|
std::shared_ptr<ClientDestination> GetSharedLocalDestination () const { return m_SharedLocalDestination; };
|
||||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (bool isPublic = false, // transient
|
|
||||||
|
std::shared_ptr<ClientDestination> CreateNewLocalDestination (
|
||||||
|
bool isPublic = false, // transient
|
||||||
i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
||||||
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
|
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
|
||||||
const std::map<std::string, std::string> * params = nullptr); // used by SAM only
|
const std::map<std::string, std::string> * params = nullptr); // used by SAM only
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_service& service,
|
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_service& service,
|
||||||
bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
||||||
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
|
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL,
|
||||||
const std::map<std::string, std::string> * params = nullptr); // same as previous but on external io_service
|
const std::map<std::string, std::string> * params = nullptr); // same as previous but on external io_service
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
std::shared_ptr<ClientDestination> CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
||||||
const std::map<std::string, std::string> * params = nullptr);
|
const std::map<std::string, std::string> * params = nullptr);
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_service& service,
|
std::shared_ptr<ClientDestination> CreateNewLocalDestination (boost::asio::io_service& service,
|
||||||
const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
||||||
const std::map<std::string, std::string> * params = nullptr); // same as previous but on external io_service
|
const std::map<std::string, std::string> * params = nullptr); // same as previous but on external io_service
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys,
|
std::shared_ptr<ClientDestination> CreateNewMatchedTunnelDestination(const i2p::data::PrivateKeys &keys,
|
||||||
const std::string & name, const std::map<std::string, std::string> * params = nullptr);
|
const std::string & name, const std::map<std::string, std::string> * params = nullptr);
|
||||||
|
|
||||||
void DeleteLocalDestination (std::shared_ptr<ClientDestination> destination);
|
void DeleteLocalDestination (std::shared_ptr<ClientDestination> destination);
|
||||||
std::shared_ptr<ClientDestination> FindLocalDestination (const i2p::data::IdentHash& destination) const;
|
std::shared_ptr<ClientDestination> FindLocalDestination (const i2p::data::IdentHash& destination) const;
|
||||||
|
|
||||||
bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename,
|
bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename,
|
||||||
i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
|
||||||
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL);
|
i2p::data::CryptoKeyType cryptoType = i2p::data::CRYPTO_KEY_TYPE_ELGAMAL);
|
||||||
|
|
||||||
AddressBook& GetAddressBook () { return m_AddressBook; };
|
AddressBook& GetAddressBook () { return m_AddressBook; };
|
||||||
|
#ifdef WITH_BOB
|
||||||
const BOBCommandChannel * GetBOBCommandChannel () const { return m_BOBCommandChannel; };
|
const BOBCommandChannel * GetBOBCommandChannel () const { return m_BOBCommandChannel; };
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_SAM
|
||||||
const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
|
const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_I2CP
|
||||||
const I2CPServer * GetI2CPServer () const { return m_I2CPServer; };
|
const I2CPServer * GetI2CPServer () const { return m_I2CPServer; };
|
||||||
|
#endif
|
||||||
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination);
|
std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination);
|
||||||
|
|
||||||
@@ -142,16 +166,22 @@ namespace client
|
|||||||
|
|
||||||
i2p::proxy::HTTPProxy * m_HttpProxy;
|
i2p::proxy::HTTPProxy * m_HttpProxy;
|
||||||
i2p::proxy::SOCKSProxy * m_SocksProxy;
|
i2p::proxy::SOCKSProxy * m_SocksProxy;
|
||||||
std::map<boost::asio::ip::tcp::endpoint, std::shared_ptr<I2PService> > m_ClientTunnels; // local endpoint -> tunnel
|
std::map<boost::asio::ip::tcp::endpoint, std::shared_ptr<I2PService> > m_ClientTunnels; // local endpoint->tunnel
|
||||||
std::map<std::pair<i2p::data::IdentHash, int>, std::shared_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port> -> tunnel
|
std::map<std::pair<i2p::data::IdentHash, int>, std::shared_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->tunnel
|
||||||
|
|
||||||
std::mutex m_ForwardsMutex;
|
std::mutex m_ForwardsMutex;
|
||||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<I2PUDPClientTunnel> > m_ClientForwards; // local endpoint -> udp tunnel
|
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<I2PUDPClientTunnel> > m_ClientForwards; // local endpoint -> udp tunnel
|
||||||
std::map<std::pair<i2p::data::IdentHash, int>, std::shared_ptr<I2PUDPServerTunnel> > m_ServerForwards; // <destination,port> -> udp tunnel
|
std::map<std::pair<i2p::data::IdentHash, int>, std::shared_ptr<I2PUDPServerTunnel> > m_ServerForwards; // <destination,port> -> udp tunnel
|
||||||
|
|
||||||
|
#ifdef WITH_SAM
|
||||||
SAMBridge * m_SamBridge;
|
SAMBridge * m_SamBridge;
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_BOB
|
||||||
BOBCommandChannel * m_BOBCommandChannel;
|
BOBCommandChannel * m_BOBCommandChannel;
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_I2CP
|
||||||
I2CPServer * m_I2CPServer;
|
I2CPServer * m_I2CPServer;
|
||||||
|
#endif
|
||||||
|
|
||||||
std::unique_ptr<boost::asio::deadline_timer> m_CleanupUDPTimer;
|
std::unique_ptr<boost::asio::deadline_timer> m_CleanupUDPTimer;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
@@ -32,13 +32,7 @@
|
|||||||
|
|
||||||
namespace i2p {
|
namespace i2p {
|
||||||
namespace proxy {
|
namespace proxy {
|
||||||
static const std::vector<std::string> jumporder = {
|
std::map<std::string, std::string> jumpservices = {
|
||||||
"reg.i2p",
|
|
||||||
"stats.i2p",
|
|
||||||
"identiguy.i2p",
|
|
||||||
};
|
|
||||||
|
|
||||||
static const std::map<std::string, std::string> jumpservices = {
|
|
||||||
{ "reg.i2p", "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/jump/" },
|
{ "reg.i2p", "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/jump/" },
|
||||||
{ "identiguy.i2p", "http://3mzmrus2oron5fxptw7hw2puho3bnqmw2hqy7nw64dsrrjwdilva.b32.i2p/cgi-bin/query?hostname=" },
|
{ "identiguy.i2p", "http://3mzmrus2oron5fxptw7hw2puho3bnqmw2hqy7nw64dsrrjwdilva.b32.i2p/cgi-bin/query?hostname=" },
|
||||||
{ "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" },
|
{ "stats.i2p", "http://7tbay5p4kzeekxvyvbf6v7eauazemsnnl2aoyqhg5jzpr5eke7tq.b32.i2p/cgi-bin/jump.cgi?a=" },
|
||||||
@@ -180,11 +174,8 @@ namespace proxy {
|
|||||||
<< "<p>" << tr("Remote host not found in router's addressbook") << "</p>\r\n"
|
<< "<p>" << tr("Remote host not found in router's addressbook") << "</p>\r\n"
|
||||||
<< "<p>" << tr("You may try to find this host on jump services below") << ":</p>\r\n"
|
<< "<p>" << tr("You may try to find this host on jump services below") << ":</p>\r\n"
|
||||||
<< "<ul>\r\n";
|
<< "<ul>\r\n";
|
||||||
for (const auto& jump : jumporder)
|
for (const auto& js : jumpservices) {
|
||||||
{
|
ss << " <li><a href=\"" << js.second << host << "\">" << js.first << "</a></li>\r\n";
|
||||||
auto js = jumpservices.find (jump);
|
|
||||||
if (js != jumpservices.end())
|
|
||||||
ss << " <li><a href=\"" << js->second << host << "\">" << js->first << "</a></li>\r\n";
|
|
||||||
}
|
}
|
||||||
ss << "</ul>\r\n";
|
ss << "</ul>\r\n";
|
||||||
std::string content = ss.str();
|
std::string content = ss.str();
|
||||||
@@ -246,14 +237,14 @@ namespace proxy {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* according to i2p ticket #1862:
|
* according to i2p ticket #1862:
|
||||||
* leave Referer if requested URL with same schema, host and port,
|
* leave Referrer if requested URL with same schema, host and port,
|
||||||
* otherwise, drop it.
|
* otherwise, drop it.
|
||||||
*/
|
*/
|
||||||
if(req.GetHeader("Referer") != "") {
|
if(req.GetHeader("Referrer") != "") {
|
||||||
i2p::http::URL reqURL; reqURL.parse(req.uri);
|
i2p::http::URL reqURL; reqURL.parse(req.uri);
|
||||||
i2p::http::URL refURL; refURL.parse(req.GetHeader("Referer"));
|
i2p::http::URL refURL; refURL.parse(req.GetHeader("Referrer"));
|
||||||
if(!boost::iequals(reqURL.schema, refURL.schema) || !boost::iequals(reqURL.host, refURL.host) || reqURL.port != refURL.port)
|
if(!boost::iequals(reqURL.schema, refURL.schema) || !boost::iequals(reqURL.host, refURL.host) || reqURL.port != refURL.port)
|
||||||
req.RemoveHeader("Referer");
|
req.RemoveHeader("Referrer");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add headers */
|
/* add headers */
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
* See full license text in LICENSE file at top of project tree
|
* See full license text in LICENSE file at top of project tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef WITH_I2CP
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
@@ -524,21 +526,31 @@ namespace client
|
|||||||
|
|
||||||
void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len)
|
void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
|
if (m_Destination || !m_Owner.InsertSession (shared_from_this ()))
|
||||||
|
{
|
||||||
|
LogPrint (eLogError, "I2CP: Session already exists");
|
||||||
|
SendSessionStatusMessage (eI2CPSessionStatusRefused); // refused
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
RAND_bytes ((uint8_t *)&m_SessionID, 2);
|
RAND_bytes ((uint8_t *)&m_SessionID, 2);
|
||||||
auto identity = std::make_shared<i2p::data::IdentityEx>();
|
auto identity = std::make_shared<i2p::data::IdentityEx>();
|
||||||
size_t offset = identity->FromBuffer (buf, len);
|
size_t offset = identity->FromBuffer (buf, len);
|
||||||
|
|
||||||
if (!offset)
|
if (!offset)
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "I2CP: Create session malformed identity");
|
LogPrint (eLogError, "I2CP: Create session malformed identity");
|
||||||
SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
|
SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Owner.FindSessionByIdentHash (identity->GetIdentHash ()))
|
if (m_Owner.FindSessionByIdentHash (identity->GetIdentHash ()))
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "I2CP: Create session duplicate address ", identity->GetIdentHash ().ToBase32 ());
|
LogPrint (eLogError, "I2CP: Create session duplicate address ", identity->GetIdentHash ().ToBase32 ());
|
||||||
SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
|
SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t optionsSize = bufbe16toh (buf + offset);
|
uint16_t optionsSize = bufbe16toh (buf + offset);
|
||||||
offset += 2;
|
offset += 2;
|
||||||
if (optionsSize > len - offset)
|
if (optionsSize > len - offset)
|
||||||
@@ -547,42 +559,27 @@ namespace client
|
|||||||
SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
|
SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> params;
|
std::map<std::string, std::string> params;
|
||||||
ExtractMapping (buf + offset, optionsSize, params);
|
ExtractMapping (buf + offset, optionsSize, params);
|
||||||
offset += optionsSize; // options
|
offset += optionsSize; // options
|
||||||
if (params[I2CP_PARAM_MESSAGE_RELIABILITY] == "none") m_IsSendAccepted = false;
|
if (params[I2CP_PARAM_MESSAGE_RELIABILITY] == "none") m_IsSendAccepted = false;
|
||||||
|
|
||||||
offset += 8; // date
|
offset += 8; // date
|
||||||
if (identity->Verify (buf, offset, buf + offset)) // signature
|
if (!identity->Verify (buf, offset, buf + offset)) // signature
|
||||||
{
|
|
||||||
if (!m_Destination)
|
|
||||||
{
|
|
||||||
m_Destination = m_Owner.IsSingleThread () ?
|
|
||||||
std::make_shared<I2CPDestination>(m_Owner.GetService (), shared_from_this (), identity, true, params):
|
|
||||||
std::make_shared<RunnableI2CPDestination>(shared_from_this (), identity, true, params);
|
|
||||||
if (m_Owner.InsertSession (shared_from_this ()))
|
|
||||||
{
|
|
||||||
SendSessionStatusMessage (eI2CPSessionStatusCreated); // created
|
|
||||||
LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " created");
|
|
||||||
m_Destination->Start ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2CP: Session already exists");
|
|
||||||
SendSessionStatusMessage (eI2CPSessionStatusRefused);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2CP: Session already exists");
|
|
||||||
SendSessionStatusMessage (eI2CPSessionStatusRefused); // refused
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "I2CP: Create session signature verification failed");
|
LogPrint (eLogError, "I2CP: Create session signature verification failed");
|
||||||
SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
|
SendSessionStatusMessage (eI2CPSessionStatusInvalid); // invalid
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_Destination = m_Owner.IsSingleThread () ?
|
||||||
|
std::make_shared<I2CPDestination>(m_Owner.GetService (), shared_from_this (), identity, true, params):
|
||||||
|
std::make_shared<RunnableI2CPDestination>(shared_from_this (), identity, true, params);
|
||||||
|
|
||||||
|
SendSessionStatusMessage (eI2CPSessionStatusCreated); // created
|
||||||
|
LogPrint (eLogDebug, "I2CP: Session ", m_SessionID, " created");
|
||||||
|
m_Destination->Start ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2CPSession::DestroySessionMessageHandler (const uint8_t * buf, size_t len)
|
void I2CPSession::DestroySessionMessageHandler (const uint8_t * buf, size_t len)
|
||||||
@@ -1040,3 +1037,4 @@ namespace client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // WITH_I2CP
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
* See full license text in LICENSE file at top of project tree
|
* See full license text in LICENSE file at top of project tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef WITH_I2CP
|
||||||
|
|
||||||
#ifndef I2CP_H__
|
#ifndef I2CP_H__
|
||||||
#define I2CP_H__
|
#define I2CP_H__
|
||||||
|
|
||||||
@@ -249,3 +251,4 @@ namespace client
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
#endif // WITH_I2CP
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <boost/algorithm/string.hpp>
|
|
||||||
#include "Base.h"
|
#include "Base.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Destination.h"
|
#include "Destination.h"
|
||||||
@@ -341,7 +340,7 @@ namespace client
|
|||||||
m_InHeader.clear ();
|
m_InHeader.clear ();
|
||||||
m_InHeader.write ((const char *)buf, len);
|
m_InHeader.write ((const char *)buf, len);
|
||||||
std::string line;
|
std::string line;
|
||||||
bool endOfHeader = false, connection = false;
|
bool endOfHeader = false;
|
||||||
while (!endOfHeader)
|
while (!endOfHeader)
|
||||||
{
|
{
|
||||||
std::getline(m_InHeader, line);
|
std::getline(m_InHeader, line);
|
||||||
@@ -350,33 +349,9 @@ namespace client
|
|||||||
if (line == "\r") endOfHeader = true;
|
if (line == "\r") endOfHeader = true;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// strip up some headers
|
if (m_Host.length () > 0 && !line.compare(0, 5, "Host:"))
|
||||||
static const std::vector<std::string> excluded // list of excluded headers
|
|
||||||
{
|
|
||||||
"Keep-Alive:", "X-I2P"
|
|
||||||
};
|
|
||||||
bool matched = false;
|
|
||||||
for (const auto& it: excluded)
|
|
||||||
if (boost::iequals (line.substr (0, it.length ()), it))
|
|
||||||
{
|
|
||||||
matched = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (matched) break;
|
|
||||||
|
|
||||||
// replace some headers
|
|
||||||
if (!m_Host.empty () && boost::iequals (line.substr (0, 5), "Host:"))
|
|
||||||
m_OutHeader << "Host: " << m_Host << "\r\n"; // override host
|
m_OutHeader << "Host: " << m_Host << "\r\n"; // override host
|
||||||
else if (boost::iequals (line.substr (0, 11), "Connection:"))
|
|
||||||
{
|
|
||||||
auto x = line.find("pgrade");
|
|
||||||
if (x != std::string::npos && x && std::tolower(line[x - 1]) != 'u') // upgrade or Upgrade
|
|
||||||
m_OutHeader << line << "\n";
|
|
||||||
else
|
else
|
||||||
m_OutHeader << "Connection: close\r\n";
|
|
||||||
connection = true;
|
|
||||||
}
|
|
||||||
else // forward as is
|
|
||||||
m_OutHeader << line << "\n";
|
m_OutHeader << line << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,9 +361,6 @@ namespace client
|
|||||||
|
|
||||||
if (endOfHeader)
|
if (endOfHeader)
|
||||||
{
|
{
|
||||||
// add Connection if not presented
|
|
||||||
if (!connection)
|
|
||||||
m_OutHeader << "Connection: close\r\n";
|
|
||||||
// add X-I2P fields
|
// add X-I2P fields
|
||||||
if (m_From)
|
if (m_From)
|
||||||
{
|
{
|
||||||
@@ -611,7 +583,7 @@ namespace client
|
|||||||
{
|
{
|
||||||
if (m_KeepAliveTimer)
|
if (m_KeepAliveTimer)
|
||||||
{
|
{
|
||||||
m_KeepAliveTimer->expires_from_now (boost::posix_time::seconds (m_KeepAliveInterval));
|
m_KeepAliveTimer->expires_from_now (boost::posix_time::seconds(m_KeepAliveInterval));
|
||||||
m_KeepAliveTimer->async_wait (std::bind (&I2PClientTunnel::HandleKeepAliveTimer,
|
m_KeepAliveTimer->async_wait (std::bind (&I2PClientTunnel::HandleKeepAliveTimer,
|
||||||
this, std::placeholders::_1));
|
this, std::placeholders::_1));
|
||||||
}
|
}
|
||||||
@@ -824,8 +796,7 @@ namespace client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta)
|
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) {
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||||
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
|
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
|
||||||
auto itr = m_Sessions.begin();
|
auto itr = m_Sessions.begin();
|
||||||
@@ -837,8 +808,7 @@ namespace client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPClientTunnel::ExpireStale(const uint64_t delta)
|
void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) {
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||||
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
|
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
|
||||||
std::vector<uint16_t> removePorts;
|
std::vector<uint16_t> removePorts;
|
||||||
@@ -894,8 +864,7 @@ namespace client
|
|||||||
Receive();
|
Receive();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UDPSession::Receive()
|
void UDPSession::Receive() {
|
||||||
{
|
|
||||||
LogPrint(eLogDebug, "UDPSession: Receive");
|
LogPrint(eLogDebug, "UDPSession: Receive");
|
||||||
IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU),
|
IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU),
|
||||||
FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2));
|
FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
@@ -932,241 +901,230 @@ namespace client
|
|||||||
LogPrint(eLogError, "UDPSession: ", ecode.message());
|
LogPrint(eLogError, "UDPSession: ", ecode.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
I2PUDPServerTunnel::I2PUDPServerTunnel (const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||||
boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip) :
|
boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip) :
|
||||||
m_IsUniqueLocal (true), m_Name (name), m_LocalAddress (localAddress),
|
m_IsUniqueLocal(true),
|
||||||
m_RemoteEndpoint (forwardTo), m_LocalDest (localDestination), m_Gzip (gzip)
|
m_Name(name),
|
||||||
|
m_LocalAddress(localAddress),
|
||||||
|
m_RemoteEndpoint(forwardTo)
|
||||||
{
|
{
|
||||||
|
m_LocalDest = localDestination;
|
||||||
|
m_LocalDest->Start();
|
||||||
|
auto dgram = m_LocalDest->CreateDatagramDestination(gzip);
|
||||||
|
dgram->SetReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
||||||
|
dgram->SetRawReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||||
}
|
}
|
||||||
|
|
||||||
I2PUDPServerTunnel::~I2PUDPServerTunnel ()
|
I2PUDPServerTunnel::~I2PUDPServerTunnel()
|
||||||
{
|
{
|
||||||
Stop ();
|
auto dgram = m_LocalDest->GetDatagramDestination();
|
||||||
|
if (dgram) dgram->ResetReceiver();
|
||||||
|
|
||||||
|
LogPrint(eLogInfo, "UDPServer: Done");
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPServerTunnel::Start ()
|
void I2PUDPServerTunnel::Start()
|
||||||
{
|
{
|
||||||
m_LocalDest->Start ();
|
m_LocalDest->Start();
|
||||||
|
|
||||||
auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip);
|
|
||||||
dgram->SetReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
|
||||||
dgram->SetRawReceiver (std::bind (&I2PUDPServerTunnel::HandleRecvFromI2PRaw, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPServerTunnel::Stop ()
|
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPServerTunnel::GetSessions()
|
||||||
{
|
|
||||||
auto dgram = m_LocalDest->GetDatagramDestination ();
|
|
||||||
if (dgram) dgram->ResetReceiver ();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPServerTunnel::GetSessions ()
|
|
||||||
{
|
{
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > sessions;
|
std::vector<std::shared_ptr<DatagramSessionInfo> > sessions;
|
||||||
std::lock_guard<std::mutex> lock (m_SessionsMutex);
|
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||||
|
|
||||||
for (UDPSessionPtr s: m_Sessions)
|
for ( UDPSessionPtr s : m_Sessions )
|
||||||
{
|
{
|
||||||
if (!s->m_Destination) continue;
|
if (!s->m_Destination) continue;
|
||||||
auto info = s->m_Destination->GetInfoForRemote (s->Identity);
|
auto info = s->m_Destination->GetInfoForRemote(s->Identity);
|
||||||
if (!info) continue;
|
if(!info) continue;
|
||||||
|
|
||||||
auto sinfo = std::make_shared<DatagramSessionInfo> ();
|
auto sinfo = std::make_shared<DatagramSessionInfo>();
|
||||||
sinfo->Name = m_Name;
|
sinfo->Name = m_Name;
|
||||||
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash> (m_LocalDest->GetIdentHash ().data ());
|
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash>(m_LocalDest->GetIdentHash().data());
|
||||||
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash> (s->Identity.data ());
|
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash>(s->Identity.data());
|
||||||
sinfo->CurrentIBGW = info->IBGW;
|
sinfo->CurrentIBGW = info->IBGW;
|
||||||
sinfo->CurrentOBEP = info->OBEP;
|
sinfo->CurrentOBEP = info->OBEP;
|
||||||
sessions.push_back (sinfo);
|
sessions.push_back(sinfo);
|
||||||
}
|
}
|
||||||
return sessions;
|
return sessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
I2PUDPClientTunnel::I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest,
|
I2PUDPClientTunnel::I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
|
||||||
boost::asio::ip::udp::endpoint localEndpoint,
|
boost::asio::ip::udp::endpoint localEndpoint,
|
||||||
std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||||
uint16_t remotePort, bool gzip) :
|
uint16_t remotePort, bool gzip) :
|
||||||
m_Name (name), m_RemoteDest (remoteDest), m_LocalDest (localDestination), m_LocalEndpoint (localEndpoint),
|
m_Name(name),
|
||||||
m_RemoteIdent (nullptr), m_ResolveThread (nullptr), m_LocalSocket (nullptr), RemotePort (remotePort),
|
m_RemoteDest(remoteDest),
|
||||||
m_LastPort (0), m_cancel_resolve (false), m_Gzip (gzip)
|
m_LocalDest(localDestination),
|
||||||
|
m_LocalEndpoint(localEndpoint),
|
||||||
|
m_RemoteIdent(nullptr),
|
||||||
|
m_ResolveThread(nullptr),
|
||||||
|
m_LocalSocket(localDestination->GetService(), localEndpoint),
|
||||||
|
RemotePort(remotePort), m_LastPort (0),
|
||||||
|
m_cancel_resolve(false)
|
||||||
{
|
{
|
||||||
}
|
m_LocalSocket.set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU));
|
||||||
|
m_LocalSocket.set_option (boost::asio::socket_base::reuse_address (true));
|
||||||
|
|
||||||
I2PUDPClientTunnel::~I2PUDPClientTunnel ()
|
auto dgram = m_LocalDest->CreateDatagramDestination(gzip);
|
||||||
{
|
dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this,
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PUDPClientTunnel::Start ()
|
|
||||||
{
|
|
||||||
// Reset flag in case of tunnel reload
|
|
||||||
if (m_cancel_resolve) m_cancel_resolve = false;
|
|
||||||
|
|
||||||
m_LocalSocket.reset (new boost::asio::ip::udp::socket (m_LocalDest->GetService (), m_LocalEndpoint));
|
|
||||||
m_LocalSocket->set_option (boost::asio::socket_base::receive_buffer_size (I2P_UDP_MAX_MTU));
|
|
||||||
m_LocalSocket->set_option (boost::asio::socket_base::reuse_address (true));
|
|
||||||
|
|
||||||
auto dgram = m_LocalDest->CreateDatagramDestination (m_Gzip);
|
|
||||||
dgram->SetReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2P, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2,
|
std::placeholders::_1, std::placeholders::_2,
|
||||||
std::placeholders::_3, std::placeholders::_4,
|
std::placeholders::_3, std::placeholders::_4,
|
||||||
std::placeholders::_5));
|
std::placeholders::_5));
|
||||||
dgram->SetRawReceiver (std::bind (&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this,
|
dgram->SetRawReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2PRaw, this,
|
||||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||||
|
}
|
||||||
|
|
||||||
m_LocalDest->Start ();
|
void I2PUDPClientTunnel::Start() {
|
||||||
|
m_LocalDest->Start();
|
||||||
if (m_ResolveThread == nullptr)
|
if (m_ResolveThread == nullptr)
|
||||||
m_ResolveThread = new std::thread (std::bind (&I2PUDPClientTunnel::TryResolving, this));
|
m_ResolveThread = new std::thread(std::bind(&I2PUDPClientTunnel::TryResolving, this));
|
||||||
RecvFromLocal ();
|
RecvFromLocal();
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPClientTunnel::Stop ()
|
void I2PUDPClientTunnel::RecvFromLocal()
|
||||||
{
|
{
|
||||||
auto dgram = m_LocalDest->GetDatagramDestination ();
|
m_LocalSocket.async_receive_from(boost::asio::buffer(m_RecvBuff, I2P_UDP_MAX_MTU),
|
||||||
if (dgram) dgram->ResetReceiver ();
|
m_RecvEndpoint, std::bind(&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
m_cancel_resolve = true;
|
|
||||||
|
|
||||||
m_Sessions.clear();
|
|
||||||
|
|
||||||
if(m_LocalSocket && m_LocalSocket->is_open ())
|
|
||||||
m_LocalSocket->close ();
|
|
||||||
|
|
||||||
if(m_ResolveThread)
|
|
||||||
{
|
|
||||||
m_ResolveThread->join ();
|
|
||||||
delete m_ResolveThread;
|
|
||||||
m_ResolveThread = nullptr;
|
|
||||||
}
|
|
||||||
if (m_RemoteIdent)
|
|
||||||
{
|
|
||||||
delete m_RemoteIdent;
|
|
||||||
m_RemoteIdent = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPClientTunnel::RecvFromLocal ()
|
void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred)
|
||||||
{
|
{
|
||||||
m_LocalSocket->async_receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU),
|
if(m_cancel_resolve) {
|
||||||
m_RecvEndpoint, std::bind (&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2));
|
LogPrint(eLogDebug, "UDP Client: Ignoring incomming data: stopping");
|
||||||
}
|
|
||||||
|
|
||||||
void I2PUDPClientTunnel::HandleRecvFromLocal (const boost::system::error_code & ec, std::size_t transferred)
|
|
||||||
{
|
|
||||||
if (m_cancel_resolve) {
|
|
||||||
LogPrint (eLogDebug, "UDP Client: Ignoring incomming data: stopping");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ec) {
|
if(ec) {
|
||||||
LogPrint (eLogError, "UDP Client: Reading from socket error: ", ec.message (), ". Restarting listener...");
|
LogPrint(eLogError, "UDP Client: Reading from socket error: ", ec.message(), ". Restarting listener...");
|
||||||
RecvFromLocal (); // Restart listener and continue work
|
RecvFromLocal(); // Restart listener and continue work
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!m_RemoteIdent) {
|
if(!m_RemoteIdent) {
|
||||||
LogPrint (eLogWarning, "UDP Client: Remote endpoint not resolved yet");
|
LogPrint(eLogWarning, "UDP Client: Remote endpoint not resolved yet");
|
||||||
RecvFromLocal ();
|
RecvFromLocal();
|
||||||
return; // drop, remote not resolved
|
return; // drop, remote not resolved
|
||||||
}
|
}
|
||||||
auto remotePort = m_RecvEndpoint.port ();
|
auto remotePort = m_RecvEndpoint.port();
|
||||||
if (!m_LastPort || m_LastPort != remotePort)
|
if (!m_LastPort || m_LastPort != remotePort)
|
||||||
{
|
{
|
||||||
auto itr = m_Sessions.find (remotePort);
|
auto itr = m_Sessions.find(remotePort);
|
||||||
if (itr != m_Sessions.end ())
|
if (itr != m_Sessions.end())
|
||||||
m_LastSession = itr->second;
|
m_LastSession = itr->second;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_LastSession = std::make_shared<UDPConvo> (boost::asio::ip::udp::endpoint (m_RecvEndpoint), 0);
|
m_LastSession = std::make_shared<UDPConvo>(boost::asio::ip::udp::endpoint(m_RecvEndpoint), 0);
|
||||||
m_Sessions.emplace (remotePort, m_LastSession);
|
m_Sessions.emplace (remotePort, m_LastSession);
|
||||||
}
|
}
|
||||||
m_LastPort = remotePort;
|
m_LastPort = remotePort;
|
||||||
}
|
}
|
||||||
// send off to remote i2p destination
|
// send off to remote i2p destination
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
auto ts = i2p::util::GetMillisecondsSinceEpoch();
|
||||||
LogPrint (eLogDebug, "UDP Client: Send ", transferred, " to ", m_RemoteIdent->ToBase32 (), ":", RemotePort);
|
LogPrint(eLogDebug, "UDP Client: Send ", transferred, " to ", m_RemoteIdent->ToBase32(), ":", RemotePort);
|
||||||
auto session = m_LocalDest->GetDatagramDestination ()->GetSession (*m_RemoteIdent);
|
auto session = m_LocalDest->GetDatagramDestination()->GetSession (*m_RemoteIdent);
|
||||||
if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
|
if (ts > m_LastSession->second + I2P_UDP_REPLIABLE_DATAGRAM_INTERVAL)
|
||||||
m_LocalDest->GetDatagramDestination ()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
|
m_LocalDest->GetDatagramDestination()->SendDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
|
||||||
else
|
else
|
||||||
m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
|
m_LocalDest->GetDatagramDestination()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
|
||||||
size_t numPackets = 0;
|
size_t numPackets = 0;
|
||||||
while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE)
|
while (numPackets < i2p::datagram::DATAGRAM_SEND_QUEUE_MAX_SIZE)
|
||||||
{
|
{
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
size_t moreBytes = m_LocalSocket->available (ec);
|
size_t moreBytes = m_LocalSocket.available(ec);
|
||||||
if (ec || !moreBytes) break;
|
if (ec || !moreBytes) break;
|
||||||
transferred = m_LocalSocket->receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), m_RecvEndpoint, 0, ec);
|
transferred = m_LocalSocket.receive_from (boost::asio::buffer (m_RecvBuff, I2P_UDP_MAX_MTU), m_RecvEndpoint, 0, ec);
|
||||||
remotePort = m_RecvEndpoint.port ();
|
remotePort = m_RecvEndpoint.port();
|
||||||
// TODO: check remotePort
|
// TODO: check remotePort
|
||||||
m_LocalDest->GetDatagramDestination ()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
|
m_LocalDest->GetDatagramDestination()->SendRawDatagram (session, m_RecvBuff, transferred, remotePort, RemotePort);
|
||||||
numPackets++;
|
numPackets++;
|
||||||
}
|
}
|
||||||
if (numPackets)
|
if (numPackets)
|
||||||
LogPrint (eLogDebug, "UDP Client: Sent ", numPackets, " more packets to ", m_RemoteIdent->ToBase32 ());
|
LogPrint(eLogDebug, "UDP Client: Sent ", numPackets, " more packets to ", m_RemoteIdent->ToBase32());
|
||||||
m_LocalDest->GetDatagramDestination ()->FlushSendQueue (session);
|
m_LocalDest->GetDatagramDestination()->FlushSendQueue (session);
|
||||||
|
|
||||||
// mark convo as active
|
// mark convo as active
|
||||||
if (m_LastSession)
|
if (m_LastSession)
|
||||||
m_LastSession->second = ts;
|
m_LastSession->second = ts;
|
||||||
RecvFromLocal ();
|
RecvFromLocal();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions ()
|
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions()
|
||||||
{
|
{
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
|
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPClientTunnel::TryResolving ()
|
void I2PUDPClientTunnel::TryResolving() {
|
||||||
{
|
i2p::util::SetThreadName("UDP Resolver");
|
||||||
i2p::util::SetThreadName ("UDP Resolver");
|
LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
|
||||||
LogPrint (eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
|
|
||||||
|
|
||||||
std::shared_ptr<const Address> addr;
|
std::shared_ptr<const Address> addr;
|
||||||
while (!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve)
|
while(!(addr = context.GetAddressBook().GetAddress(m_RemoteDest)) && !m_cancel_resolve)
|
||||||
{
|
{
|
||||||
LogPrint (eLogWarning, "UDP Tunnel: Failed to lookup ", m_RemoteDest);
|
LogPrint(eLogWarning, "UDP Tunnel: Failed to lookup ", m_RemoteDest);
|
||||||
std::this_thread::sleep_for (std::chrono::seconds (1));
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
}
|
}
|
||||||
if (m_cancel_resolve)
|
if(m_cancel_resolve)
|
||||||
{
|
{
|
||||||
LogPrint(eLogError, "UDP Tunnel: Lookup of ", m_RemoteDest, " was cancelled");
|
LogPrint(eLogError, "UDP Tunnel: Lookup of ", m_RemoteDest, " was cancelled");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!addr || !addr->IsIdentHash ())
|
if (!addr || !addr->IsIdentHash ())
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "UDP Tunnel: ", m_RemoteDest, " not found");
|
LogPrint(eLogError, "UDP Tunnel: ", m_RemoteDest, " not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_RemoteIdent = new i2p::data::IdentHash;
|
m_RemoteIdent = new i2p::data::IdentHash;
|
||||||
*m_RemoteIdent = addr->identHash;
|
*m_RemoteIdent = addr->identHash;
|
||||||
LogPrint(eLogInfo, "UDP Tunnel: Resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32 ());
|
LogPrint(eLogInfo, "UDP Tunnel: Resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32());
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPClientTunnel::HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
if (m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
|
if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
|
||||||
HandleRecvFromI2PRaw (fromPort, toPort, buf, len);
|
HandleRecvFromI2PRaw (fromPort, toPort, buf, len);
|
||||||
else
|
else
|
||||||
LogPrint(eLogWarning, "UDP Client: Unwarranted traffic from ", from.GetIdentHash().ToBase32 ());
|
LogPrint(eLogWarning, "UDP Client: Unwarranted traffic from ", from.GetIdentHash().ToBase32());
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2PUDPClientTunnel::HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
void I2PUDPClientTunnel::HandleRecvFromI2PRaw(uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||||
{
|
{
|
||||||
auto itr = m_Sessions.find (toPort);
|
auto itr = m_Sessions.find(toPort);
|
||||||
// found convo ?
|
// found convo ?
|
||||||
if (itr != m_Sessions.end ())
|
if(itr != m_Sessions.end())
|
||||||
{
|
{
|
||||||
// found convo
|
// found convo
|
||||||
if (len > 0)
|
if (len > 0)
|
||||||
{
|
{
|
||||||
LogPrint (eLogDebug, "UDP Client: Got ", len, "B from ", m_RemoteIdent ? m_RemoteIdent->ToBase32 () : "");
|
LogPrint(eLogDebug, "UDP Client: Got ", len, "B from ", m_RemoteIdent ? m_RemoteIdent->ToBase32() : "");
|
||||||
m_LocalSocket->send_to (boost::asio::buffer (buf, len), itr->second->first);
|
m_LocalSocket.send_to(boost::asio::buffer(buf, len), itr->second->first);
|
||||||
// mark convo as active
|
// mark convo as active
|
||||||
itr->second->second = i2p::util::GetMillisecondsSinceEpoch ();
|
itr->second->second = i2p::util::GetMillisecondsSinceEpoch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
LogPrint (eLogWarning, "UDP Client: Not tracking udp session using port ", (int) toPort);
|
LogPrint(eLogWarning, "UDP Client: Not tracking udp session using port ", (int) toPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
I2PUDPClientTunnel::~I2PUDPClientTunnel()
|
||||||
|
{
|
||||||
|
auto dgram = m_LocalDest->GetDatagramDestination();
|
||||||
|
if (dgram) dgram->ResetReceiver();
|
||||||
|
m_cancel_resolve = true;
|
||||||
|
|
||||||
|
m_Sessions.clear();
|
||||||
|
|
||||||
|
if(m_LocalSocket.is_open())
|
||||||
|
m_LocalSocket.close();
|
||||||
|
|
||||||
|
if(m_ResolveThread)
|
||||||
|
{
|
||||||
|
m_ResolveThread->join();
|
||||||
|
delete m_ResolveThread;
|
||||||
|
m_ResolveThread = nullptr;
|
||||||
|
}
|
||||||
|
if (m_RemoteIdent) delete m_RemoteIdent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2022, The PurpleI2P Project
|
* Copyright (c) 2013-2021, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
@@ -230,27 +230,25 @@ namespace client
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
I2PUDPServerTunnel (const std::string & name,
|
I2PUDPServerTunnel(const std::string & name,
|
||||||
std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||||
boost::asio::ip::address localAddress,
|
boost::asio::ip::address localAddress,
|
||||||
boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip);
|
boost::asio::ip::udp::endpoint forwardTo, uint16_t port, bool gzip);
|
||||||
~I2PUDPServerTunnel ();
|
~I2PUDPServerTunnel();
|
||||||
|
|
||||||
/** expire stale udp conversations */
|
/** expire stale udp conversations */
|
||||||
void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
|
void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
|
||||||
void Start ();
|
void Start();
|
||||||
void Stop ();
|
const char * GetName() const { return m_Name.c_str(); }
|
||||||
const char * GetName () const { return m_Name.c_str(); }
|
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions();
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions ();
|
|
||||||
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
|
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
|
||||||
|
|
||||||
void SetUniqueLocal (bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; }
|
void SetUniqueLocal(bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||||
void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||||
UDPSessionPtr ObtainUDPSession (const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
|
UDPSessionPtr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -262,7 +260,6 @@ namespace client
|
|||||||
std::vector<UDPSessionPtr> m_Sessions;
|
std::vector<UDPSessionPtr> m_Sessions;
|
||||||
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
|
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
|
||||||
UDPSessionPtr m_LastSession;
|
UDPSessionPtr m_LastSession;
|
||||||
bool m_Gzip;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -273,36 +270,27 @@ namespace client
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
I2PUDPClientTunnel (const std::string & name, const std::string &remoteDest,
|
I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
|
||||||
boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||||
uint16_t remotePort, bool gzip);
|
uint16_t remotePort, bool gzip);
|
||||||
~I2PUDPClientTunnel ();
|
~I2PUDPClientTunnel();
|
||||||
|
void Start();
|
||||||
|
const char * GetName() const { return m_Name.c_str(); }
|
||||||
|
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions();
|
||||||
|
|
||||||
void Start ();
|
bool IsLocalDestination(const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); }
|
||||||
void Stop ();
|
|
||||||
const char * GetName () const { return m_Name.c_str(); }
|
|
||||||
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions ();
|
|
||||||
|
|
||||||
bool IsLocalDestination (const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); }
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
|
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
|
||||||
inline void SetLocalDestination (std::shared_ptr<ClientDestination> dest)
|
void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
|
||||||
{
|
|
||||||
if (m_LocalDest) m_LocalDest->Release ();
|
|
||||||
if (dest) dest->Acquire ();
|
|
||||||
m_LocalDest = dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExpireStale (const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
typedef std::pair<boost::asio::ip::udp::endpoint, uint64_t> UDPConvo;
|
typedef std::pair<boost::asio::ip::udp::endpoint, uint64_t> UDPConvo;
|
||||||
void RecvFromLocal ();
|
void RecvFromLocal();
|
||||||
void HandleRecvFromLocal (const boost::system::error_code & e, std::size_t transferred);
|
void HandleRecvFromLocal(const boost::system::error_code & e, std::size_t transferred);
|
||||||
void HandleRecvFromI2P (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||||
void HandleRecvFromI2PRaw (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
void HandleRecvFromI2PRaw(uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||||
void TryResolving ();
|
void TryResolving();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@@ -314,12 +302,11 @@ namespace client
|
|||||||
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
|
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
|
||||||
i2p::data::IdentHash * m_RemoteIdent;
|
i2p::data::IdentHash * m_RemoteIdent;
|
||||||
std::thread * m_ResolveThread;
|
std::thread * m_ResolveThread;
|
||||||
std::unique_ptr<boost::asio::ip::udp::socket> m_LocalSocket;
|
boost::asio::ip::udp::socket m_LocalSocket;
|
||||||
boost::asio::ip::udp::endpoint m_RecvEndpoint;
|
boost::asio::ip::udp::endpoint m_RecvEndpoint;
|
||||||
uint8_t m_RecvBuff[I2P_UDP_MAX_MTU];
|
uint8_t m_RecvBuff[I2P_UDP_MAX_MTU];
|
||||||
uint16_t RemotePort, m_LastPort;
|
uint16_t RemotePort, m_LastPort;
|
||||||
bool m_cancel_resolve;
|
bool m_cancel_resolve;
|
||||||
bool m_Gzip;
|
|
||||||
std::shared_ptr<UDPConvo> m_LastSession;
|
std::shared_ptr<UDPConvo> m_LastSession;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2021, The PurpleI2P Project
|
* Copyright (c) 2013-2022, The PurpleI2P Project
|
||||||
*
|
*
|
||||||
* This file is part of Purple i2pd project and licensed under BSD3
|
* This file is part of Purple i2pd project and licensed under BSD3
|
||||||
*
|
*
|
||||||
* See full license text in LICENSE file at top of project tree
|
* See full license text in LICENSE file at top of project tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef WITH_SAM
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@@ -154,11 +156,7 @@ namespace client
|
|||||||
|
|
||||||
if (SAMVersionAcceptable(version))
|
if (SAMVersionAcceptable(version))
|
||||||
{
|
{
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
|
|
||||||
#else
|
|
||||||
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
|
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
|
||||||
#endif
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (),
|
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (),
|
||||||
std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (),
|
std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (),
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
std::placeholders::_1, std::placeholders::_2));
|
||||||
@@ -465,11 +463,7 @@ namespace client
|
|||||||
size_t l = session->GetLocalDestination ()->GetPrivateKeys ().ToBuffer (buf, 1024);
|
size_t l = session->GetLocalDestination ()->GetPrivateKeys ().ToBuffer (buf, 1024);
|
||||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024);
|
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024);
|
||||||
priv[l1] = 0;
|
priv[l1] = 0;
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
|
||||||
#else
|
|
||||||
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, l2, false);
|
SendMessageReply (m_Buffer, l2, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -710,13 +704,8 @@ namespace client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType);
|
auto keys = i2p::data::PrivateKeys::CreateRandomKeys (signatureType, cryptoType);
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
|
|
||||||
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
|
|
||||||
#else
|
|
||||||
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
|
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
|
||||||
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
|
keys.GetPublic ()->ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, l, false);
|
SendMessageReply (m_Buffer, l, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -754,11 +743,7 @@ namespace client
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "SAM: Naming failed, unknown address ", name);
|
LogPrint (eLogError, "SAM: Naming failed, unknown address ", name);
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
|
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, len, false);
|
SendMessageReply (m_Buffer, len, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -833,11 +818,7 @@ namespace client
|
|||||||
void SAMSocket::SendI2PError(const std::string & msg)
|
void SAMSocket::SendI2PError(const std::string & msg)
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "SAM: I2P error: ", msg);
|
LogPrint (eLogError, "SAM: I2P error: ", msg);
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str());
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str());
|
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str());
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, len, true);
|
SendMessageReply (m_Buffer, len, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -851,11 +832,7 @@ namespace client
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogPrint (eLogError, "SAM: Naming lookup failed. LeaseSet for ", name, " not found");
|
LogPrint (eLogError, "SAM: Naming lookup failed. LeaseSet for ", name, " not found");
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
|
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, len, false);
|
SendMessageReply (m_Buffer, len, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -863,11 +840,7 @@ namespace client
|
|||||||
void SAMSocket::SendNamingLookupReply (const std::string& name, std::shared_ptr<const i2p::data::IdentityEx> identity)
|
void SAMSocket::SendNamingLookupReply (const std::string& name, std::shared_ptr<const i2p::data::IdentityEx> identity)
|
||||||
{
|
{
|
||||||
auto base64 = identity->ToBase64 ();
|
auto base64 = identity->ToBase64 ();
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ());
|
|
||||||
#else
|
|
||||||
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ());
|
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ());
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, l, false);
|
SendMessageReply (m_Buffer, l, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1121,11 +1094,7 @@ namespace client
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len);
|
|
||||||
#else
|
|
||||||
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len);
|
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len);
|
||||||
#endif
|
|
||||||
if (len < SAM_SOCKET_BUFFER_SIZE - l)
|
if (len < SAM_SOCKET_BUFFER_SIZE - l)
|
||||||
{
|
{
|
||||||
memcpy (m_StreamBuffer + l, buf, len);
|
memcpy (m_StreamBuffer + l, buf, len);
|
||||||
@@ -1149,11 +1118,7 @@ namespace client
|
|||||||
m_Owner.SendTo({ {buf, len} }, *ep);
|
m_Owner.SendTo({ {buf, len} }, *ep);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len);
|
|
||||||
#else
|
|
||||||
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len);
|
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_RAW_RECEIVED, (long unsigned int)len);
|
||||||
#endif
|
|
||||||
if (len < SAM_SOCKET_BUFFER_SIZE - l)
|
if (len < SAM_SOCKET_BUFFER_SIZE - l)
|
||||||
{
|
{
|
||||||
memcpy (m_StreamBuffer + l, buf, len);
|
memcpy (m_StreamBuffer + l, buf, len);
|
||||||
@@ -1528,3 +1493,4 @@ namespace client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // WITH_SAM
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
* See full license text in LICENSE file at top of project tree
|
* See full license text in LICENSE file at top of project tree
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef WITH_SAM
|
||||||
|
|
||||||
#ifndef SAM_H__
|
#ifndef SAM_H__
|
||||||
#define SAM_H__
|
#define SAM_H__
|
||||||
|
|
||||||
@@ -286,3 +288,4 @@ namespace client
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
#endif // WITH_SAM
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#ifndef INCLUDE_INJA_CONFIG_HPP_
|
|
||||||
#define INCLUDE_INJA_CONFIG_HPP_
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "template.hpp"
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Class for lexer configuration.
|
|
||||||
*/
|
|
||||||
struct LexerConfig {
|
|
||||||
std::string statement_open {"{%"};
|
|
||||||
std::string statement_open_no_lstrip {"{%+"};
|
|
||||||
std::string statement_open_force_lstrip {"{%-"};
|
|
||||||
std::string statement_close {"%}"};
|
|
||||||
std::string statement_close_force_rstrip {"-%}"};
|
|
||||||
std::string line_statement {"##"};
|
|
||||||
std::string expression_open {"{{"};
|
|
||||||
std::string expression_open_force_lstrip {"{{-"};
|
|
||||||
std::string expression_close {"}}"};
|
|
||||||
std::string expression_close_force_rstrip {"-}}"};
|
|
||||||
std::string comment_open {"{#"};
|
|
||||||
std::string comment_open_force_lstrip {"{#-"};
|
|
||||||
std::string comment_close {"#}"};
|
|
||||||
std::string comment_close_force_rstrip {"-#}"};
|
|
||||||
std::string open_chars {"#{"};
|
|
||||||
|
|
||||||
bool trim_blocks {false};
|
|
||||||
bool lstrip_blocks {false};
|
|
||||||
|
|
||||||
void update_open_chars() {
|
|
||||||
open_chars = "";
|
|
||||||
if (open_chars.find(line_statement[0]) == std::string::npos) {
|
|
||||||
open_chars += line_statement[0];
|
|
||||||
}
|
|
||||||
if (open_chars.find(statement_open[0]) == std::string::npos) {
|
|
||||||
open_chars += statement_open[0];
|
|
||||||
}
|
|
||||||
if (open_chars.find(statement_open_no_lstrip[0]) == std::string::npos) {
|
|
||||||
open_chars += statement_open_no_lstrip[0];
|
|
||||||
}
|
|
||||||
if (open_chars.find(statement_open_force_lstrip[0]) == std::string::npos) {
|
|
||||||
open_chars += statement_open_force_lstrip[0];
|
|
||||||
}
|
|
||||||
if (open_chars.find(expression_open[0]) == std::string::npos) {
|
|
||||||
open_chars += expression_open[0];
|
|
||||||
}
|
|
||||||
if (open_chars.find(expression_open_force_lstrip[0]) == std::string::npos) {
|
|
||||||
open_chars += expression_open_force_lstrip[0];
|
|
||||||
}
|
|
||||||
if (open_chars.find(comment_open[0]) == std::string::npos) {
|
|
||||||
open_chars += comment_open[0];
|
|
||||||
}
|
|
||||||
if (open_chars.find(comment_open_force_lstrip[0]) == std::string::npos) {
|
|
||||||
open_chars += comment_open_force_lstrip[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Class for parser configuration.
|
|
||||||
*/
|
|
||||||
struct ParserConfig {
|
|
||||||
bool search_included_templates_in_files {true};
|
|
||||||
|
|
||||||
std::function<Template(const std::string&, const std::string&)> include_callback;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Class for render configuration.
|
|
||||||
*/
|
|
||||||
struct RenderConfig {
|
|
||||||
bool throw_at_missing_includes {true};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_CONFIG_HPP_
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
#ifndef INCLUDE_INJA_ENVIRONMENT_HPP_
|
|
||||||
#define INCLUDE_INJA_ENVIRONMENT_HPP_
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include "config.hpp"
|
|
||||||
#include "function_storage.hpp"
|
|
||||||
#include "parser.hpp"
|
|
||||||
#include "renderer.hpp"
|
|
||||||
#include "template.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Class for changing the configuration.
|
|
||||||
*/
|
|
||||||
class Environment {
|
|
||||||
LexerConfig lexer_config;
|
|
||||||
ParserConfig parser_config;
|
|
||||||
RenderConfig render_config;
|
|
||||||
|
|
||||||
FunctionStorage function_storage;
|
|
||||||
TemplateStorage template_storage;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::string input_path;
|
|
||||||
std::string output_path;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Environment(): Environment("") {}
|
|
||||||
|
|
||||||
explicit Environment(const std::string& global_path): input_path(global_path), output_path(global_path) {}
|
|
||||||
|
|
||||||
Environment(const std::string& input_path, const std::string& output_path): input_path(input_path), output_path(output_path) {}
|
|
||||||
|
|
||||||
/// Sets the opener and closer for template statements
|
|
||||||
void set_statement(const std::string& open, const std::string& close) {
|
|
||||||
lexer_config.statement_open = open;
|
|
||||||
lexer_config.statement_open_no_lstrip = open + "+";
|
|
||||||
lexer_config.statement_open_force_lstrip = open + "-";
|
|
||||||
lexer_config.statement_close = close;
|
|
||||||
lexer_config.statement_close_force_rstrip = "-" + close;
|
|
||||||
lexer_config.update_open_chars();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the opener for template line statements
|
|
||||||
void set_line_statement(const std::string& open) {
|
|
||||||
lexer_config.line_statement = open;
|
|
||||||
lexer_config.update_open_chars();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the opener and closer for template expressions
|
|
||||||
void set_expression(const std::string& open, const std::string& close) {
|
|
||||||
lexer_config.expression_open = open;
|
|
||||||
lexer_config.expression_open_force_lstrip = open + "-";
|
|
||||||
lexer_config.expression_close = close;
|
|
||||||
lexer_config.expression_close_force_rstrip = "-" + close;
|
|
||||||
lexer_config.update_open_chars();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the opener and closer for template comments
|
|
||||||
void set_comment(const std::string& open, const std::string& close) {
|
|
||||||
lexer_config.comment_open = open;
|
|
||||||
lexer_config.comment_open_force_lstrip = open + "-";
|
|
||||||
lexer_config.comment_close = close;
|
|
||||||
lexer_config.comment_close_force_rstrip = "-" + close;
|
|
||||||
lexer_config.update_open_chars();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets whether to remove the first newline after a block
|
|
||||||
void set_trim_blocks(bool trim_blocks) {
|
|
||||||
lexer_config.trim_blocks = trim_blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets whether to strip the spaces and tabs from the start of a line to a block
|
|
||||||
void set_lstrip_blocks(bool lstrip_blocks) {
|
|
||||||
lexer_config.lstrip_blocks = lstrip_blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the element notation syntax
|
|
||||||
void set_search_included_templates_in_files(bool search_in_files) {
|
|
||||||
parser_config.search_included_templates_in_files = search_in_files;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets whether a missing include will throw an error
|
|
||||||
void set_throw_at_missing_includes(bool will_throw) {
|
|
||||||
render_config.throw_at_missing_includes = will_throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
Template parse(std::string_view input) {
|
|
||||||
Parser parser(parser_config, lexer_config, template_storage, function_storage);
|
|
||||||
return parser.parse(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
Template parse_template(const std::string& filename) {
|
|
||||||
Parser parser(parser_config, lexer_config, template_storage, function_storage);
|
|
||||||
auto result = Template(parser.load_file(input_path + static_cast<std::string>(filename)));
|
|
||||||
parser.parse_into_template(result, input_path + static_cast<std::string>(filename));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Template parse_file(const std::string& filename) {
|
|
||||||
return parse_template(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string render(std::string_view input, const json& data) {
|
|
||||||
return render(parse(input), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string render(const Template& tmpl, const json& data) {
|
|
||||||
std::stringstream os;
|
|
||||||
render_to(os, tmpl, data);
|
|
||||||
return os.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string render_file(const std::string& filename, const json& data) {
|
|
||||||
return render(parse_template(filename), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string render_file_with_json_file(const std::string& filename, const std::string& filename_data) {
|
|
||||||
const json data = load_json(filename_data);
|
|
||||||
return render_file(filename, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(const std::string& filename, const json& data, const std::string& filename_out) {
|
|
||||||
std::ofstream file(output_path + filename_out);
|
|
||||||
file << render_file(filename, data);
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(const Template& temp, const json& data, const std::string& filename_out) {
|
|
||||||
std::ofstream file(output_path + filename_out);
|
|
||||||
file << render(temp, data);
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_with_json_file(const std::string& filename, const std::string& filename_data, const std::string& filename_out) {
|
|
||||||
const json data = load_json(filename_data);
|
|
||||||
write(filename, data, filename_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_with_json_file(const Template& temp, const std::string& filename_data, const std::string& filename_out) {
|
|
||||||
const json data = load_json(filename_data);
|
|
||||||
write(temp, data, filename_out);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& render_to(std::ostream& os, const Template& tmpl, const json& data) {
|
|
||||||
Renderer(render_config, template_storage, function_storage).render_to(os, tmpl, data);
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string load_file(const std::string& filename) {
|
|
||||||
Parser parser(parser_config, lexer_config, template_storage, function_storage);
|
|
||||||
return parser.load_file(input_path + filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
json load_json(const std::string& filename) {
|
|
||||||
std::ifstream file;
|
|
||||||
file.open(input_path + filename);
|
|
||||||
if (file.fail()) {
|
|
||||||
INJA_THROW(FileError("failed accessing file at '" + input_path + filename + "'"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return json::parse(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Adds a variadic callback
|
|
||||||
*/
|
|
||||||
void add_callback(const std::string& name, const CallbackFunction& callback) {
|
|
||||||
add_callback(name, -1, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Adds a variadic void callback
|
|
||||||
*/
|
|
||||||
void add_void_callback(const std::string& name, const VoidCallbackFunction& callback) {
|
|
||||||
add_void_callback(name, -1, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Adds a callback with given number or arguments
|
|
||||||
*/
|
|
||||||
void add_callback(const std::string& name, int num_args, const CallbackFunction& callback) {
|
|
||||||
function_storage.add_callback(name, num_args, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Adds a void callback with given number or arguments
|
|
||||||
*/
|
|
||||||
void add_void_callback(const std::string& name, int num_args, const VoidCallbackFunction& callback) {
|
|
||||||
function_storage.add_callback(name, num_args, [callback](Arguments& args) {
|
|
||||||
callback(args);
|
|
||||||
return json();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Includes a template with a given name into the environment.
|
|
||||||
* Then, a template can be rendered in another template using the
|
|
||||||
* include "<name>" syntax.
|
|
||||||
*/
|
|
||||||
void include_template(const std::string& name, const Template& tmpl) {
|
|
||||||
template_storage[name] = tmpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief Sets a function that is called when an included file is not found
|
|
||||||
*/
|
|
||||||
void set_include_callback(const std::function<Template(const std::string&, const std::string&)>& callback) {
|
|
||||||
parser_config.include_callback = callback;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief render with default settings to a string
|
|
||||||
*/
|
|
||||||
inline std::string render(std::string_view input, const json& data) {
|
|
||||||
return Environment().render(input, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief render with default settings to the given output stream
|
|
||||||
*/
|
|
||||||
inline void render_to(std::ostream& os, std::string_view input, const json& data) {
|
|
||||||
Environment env;
|
|
||||||
env.render_to(os, env.parse(input), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_ENVIRONMENT_HPP_
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#ifndef INCLUDE_INJA_EXCEPTIONS_HPP_
|
|
||||||
#define INCLUDE_INJA_EXCEPTIONS_HPP_
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
|
|
||||||
struct SourceLocation {
|
|
||||||
size_t line;
|
|
||||||
size_t column;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InjaError : public std::runtime_error {
|
|
||||||
const std::string type;
|
|
||||||
const std::string message;
|
|
||||||
|
|
||||||
const SourceLocation location;
|
|
||||||
|
|
||||||
explicit InjaError(const std::string& type, const std::string& message)
|
|
||||||
: std::runtime_error("[inja.exception." + type + "] " + message), type(type), message(message), location({0, 0}) {}
|
|
||||||
|
|
||||||
explicit InjaError(const std::string& type, const std::string& message, SourceLocation location)
|
|
||||||
: std::runtime_error("[inja.exception." + type + "] (at " + std::to_string(location.line) + ":" + std::to_string(location.column) + ") " + message),
|
|
||||||
type(type), message(message), location(location) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ParserError : public InjaError {
|
|
||||||
explicit ParserError(const std::string& message, SourceLocation location): InjaError("parser_error", message, location) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RenderError : public InjaError {
|
|
||||||
explicit RenderError(const std::string& message, SourceLocation location): InjaError("render_error", message, location) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FileError : public InjaError {
|
|
||||||
explicit FileError(const std::string& message): InjaError("file_error", message) {}
|
|
||||||
explicit FileError(const std::string& message, SourceLocation location): InjaError("file_error", message, location) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DataError : public InjaError {
|
|
||||||
explicit DataError(const std::string& message, SourceLocation location): InjaError("data_error", message, location) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_EXCEPTIONS_HPP_
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
#ifndef INCLUDE_INJA_FUNCTION_STORAGE_HPP_
|
|
||||||
#define INCLUDE_INJA_FUNCTION_STORAGE_HPP_
|
|
||||||
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
|
|
||||||
using Arguments = std::vector<const json*>;
|
|
||||||
using CallbackFunction = std::function<json(Arguments& args)>;
|
|
||||||
using VoidCallbackFunction = std::function<void(Arguments& args)>;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Class for builtin functions and user-defined callbacks.
|
|
||||||
*/
|
|
||||||
class FunctionStorage {
|
|
||||||
public:
|
|
||||||
enum class Operation {
|
|
||||||
Not,
|
|
||||||
And,
|
|
||||||
Or,
|
|
||||||
In,
|
|
||||||
Equal,
|
|
||||||
NotEqual,
|
|
||||||
Greater,
|
|
||||||
GreaterEqual,
|
|
||||||
Less,
|
|
||||||
LessEqual,
|
|
||||||
Add,
|
|
||||||
Subtract,
|
|
||||||
Multiplication,
|
|
||||||
Division,
|
|
||||||
Power,
|
|
||||||
Modulo,
|
|
||||||
AtId,
|
|
||||||
At,
|
|
||||||
Default,
|
|
||||||
DivisibleBy,
|
|
||||||
Even,
|
|
||||||
Exists,
|
|
||||||
ExistsInObject,
|
|
||||||
First,
|
|
||||||
Float,
|
|
||||||
Int,
|
|
||||||
IsArray,
|
|
||||||
IsBoolean,
|
|
||||||
IsFloat,
|
|
||||||
IsInteger,
|
|
||||||
IsNumber,
|
|
||||||
IsObject,
|
|
||||||
IsString,
|
|
||||||
Last,
|
|
||||||
Length,
|
|
||||||
Lower,
|
|
||||||
Max,
|
|
||||||
Min,
|
|
||||||
Odd,
|
|
||||||
Range,
|
|
||||||
Round,
|
|
||||||
Sort,
|
|
||||||
Upper,
|
|
||||||
Super,
|
|
||||||
Join,
|
|
||||||
Callback,
|
|
||||||
ParenLeft,
|
|
||||||
ParenRight,
|
|
||||||
None,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FunctionData {
|
|
||||||
explicit FunctionData(const Operation& op, const CallbackFunction& cb = CallbackFunction {}): operation(op), callback(cb) {}
|
|
||||||
const Operation operation;
|
|
||||||
const CallbackFunction callback;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
|
||||||
const int VARIADIC {-1};
|
|
||||||
|
|
||||||
std::map<std::pair<std::string, int>, FunctionData> function_storage = {
|
|
||||||
{std::make_pair("at", 2), FunctionData {Operation::At}},
|
|
||||||
{std::make_pair("default", 2), FunctionData {Operation::Default}},
|
|
||||||
{std::make_pair("divisibleBy", 2), FunctionData {Operation::DivisibleBy}},
|
|
||||||
{std::make_pair("even", 1), FunctionData {Operation::Even}},
|
|
||||||
{std::make_pair("exists", 1), FunctionData {Operation::Exists}},
|
|
||||||
{std::make_pair("existsIn", 2), FunctionData {Operation::ExistsInObject}},
|
|
||||||
{std::make_pair("first", 1), FunctionData {Operation::First}},
|
|
||||||
{std::make_pair("float", 1), FunctionData {Operation::Float}},
|
|
||||||
{std::make_pair("int", 1), FunctionData {Operation::Int}},
|
|
||||||
{std::make_pair("isArray", 1), FunctionData {Operation::IsArray}},
|
|
||||||
{std::make_pair("isBoolean", 1), FunctionData {Operation::IsBoolean}},
|
|
||||||
{std::make_pair("isFloat", 1), FunctionData {Operation::IsFloat}},
|
|
||||||
{std::make_pair("isInteger", 1), FunctionData {Operation::IsInteger}},
|
|
||||||
{std::make_pair("isNumber", 1), FunctionData {Operation::IsNumber}},
|
|
||||||
{std::make_pair("isObject", 1), FunctionData {Operation::IsObject}},
|
|
||||||
{std::make_pair("isString", 1), FunctionData {Operation::IsString}},
|
|
||||||
{std::make_pair("last", 1), FunctionData {Operation::Last}},
|
|
||||||
{std::make_pair("length", 1), FunctionData {Operation::Length}},
|
|
||||||
{std::make_pair("lower", 1), FunctionData {Operation::Lower}},
|
|
||||||
{std::make_pair("max", 1), FunctionData {Operation::Max}},
|
|
||||||
{std::make_pair("min", 1), FunctionData {Operation::Min}},
|
|
||||||
{std::make_pair("odd", 1), FunctionData {Operation::Odd}},
|
|
||||||
{std::make_pair("range", 1), FunctionData {Operation::Range}},
|
|
||||||
{std::make_pair("round", 2), FunctionData {Operation::Round}},
|
|
||||||
{std::make_pair("sort", 1), FunctionData {Operation::Sort}},
|
|
||||||
{std::make_pair("upper", 1), FunctionData {Operation::Upper}},
|
|
||||||
{std::make_pair("super", 0), FunctionData {Operation::Super}},
|
|
||||||
{std::make_pair("super", 1), FunctionData {Operation::Super}},
|
|
||||||
{std::make_pair("join", 2), FunctionData {Operation::Join}},
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
void add_builtin(std::string_view name, int num_args, Operation op) {
|
|
||||||
function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData {op});
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_callback(std::string_view name, int num_args, const CallbackFunction& callback) {
|
|
||||||
function_storage.emplace(std::make_pair(static_cast<std::string>(name), num_args), FunctionData {Operation::Callback, callback});
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionData find_function(std::string_view name, int num_args) const {
|
|
||||||
auto it = function_storage.find(std::make_pair(static_cast<std::string>(name), num_args));
|
|
||||||
if (it != function_storage.end()) {
|
|
||||||
return it->second;
|
|
||||||
|
|
||||||
// Find variadic function
|
|
||||||
} else if (num_args > 0) {
|
|
||||||
it = function_storage.find(std::make_pair(static_cast<std::string>(name), VARIADIC));
|
|
||||||
if (it != function_storage.end()) {
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return FunctionData {Operation::None};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_FUNCTION_STORAGE_HPP_
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
___ _ Version 3.3
|
|
||||||
|_ _|_ __ (_) __ _ https://github.com/pantor/inja
|
|
||||||
| || '_ \ | |/ _` | Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
|
||||||
| || | | || | (_| |
|
|
||||||
|___|_| |_|/ |\__,_| Copyright (c) 2018-2021 Lars Berscheid
|
|
||||||
|__/
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef INCLUDE_INJA_INJA_HPP_
|
|
||||||
#define INCLUDE_INJA_INJA_HPP_
|
|
||||||
|
|
||||||
//#include <nlohmann/json.hpp>
|
|
||||||
#include "../nlohmann/json.hpp"
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
#ifndef INJA_DATA_TYPE
|
|
||||||
using json = nlohmann::json;
|
|
||||||
#else
|
|
||||||
using json = INJA_DATA_TYPE;
|
|
||||||
#endif
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(INJA_NOEXCEPTION)
|
|
||||||
#ifndef INJA_THROW
|
|
||||||
#define INJA_THROW(exception) throw exception
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#include <cstdlib>
|
|
||||||
#ifndef INJA_THROW
|
|
||||||
#define INJA_THROW(exception) \
|
|
||||||
std::abort(); \
|
|
||||||
std::ignore = exception
|
|
||||||
#endif
|
|
||||||
#ifndef INJA_NOEXCEPTION
|
|
||||||
#define INJA_NOEXCEPTION
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "environment.hpp"
|
|
||||||
#include "exceptions.hpp"
|
|
||||||
#include "parser.hpp"
|
|
||||||
#include "renderer.hpp"
|
|
||||||
#include "template.hpp"
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_INJA_HPP_
|
|
||||||
@@ -1,435 +0,0 @@
|
|||||||
#ifndef INCLUDE_INJA_LEXER_HPP_
|
|
||||||
#define INCLUDE_INJA_LEXER_HPP_
|
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
#include <locale>
|
|
||||||
|
|
||||||
#include "config.hpp"
|
|
||||||
#include "token.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Class for lexing an inja Template.
|
|
||||||
*/
|
|
||||||
class Lexer {
|
|
||||||
enum class State {
|
|
||||||
Text,
|
|
||||||
ExpressionStart,
|
|
||||||
ExpressionStartForceLstrip,
|
|
||||||
ExpressionBody,
|
|
||||||
LineStart,
|
|
||||||
LineBody,
|
|
||||||
StatementStart,
|
|
||||||
StatementStartNoLstrip,
|
|
||||||
StatementStartForceLstrip,
|
|
||||||
StatementBody,
|
|
||||||
CommentStart,
|
|
||||||
CommentStartForceLstrip,
|
|
||||||
CommentBody,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class MinusState {
|
|
||||||
Operator,
|
|
||||||
Number,
|
|
||||||
};
|
|
||||||
|
|
||||||
const LexerConfig& config;
|
|
||||||
|
|
||||||
State state;
|
|
||||||
MinusState minus_state;
|
|
||||||
std::string_view m_in;
|
|
||||||
size_t tok_start;
|
|
||||||
size_t pos;
|
|
||||||
|
|
||||||
Token scan_body(std::string_view close, Token::Kind closeKind, std::string_view close_trim = std::string_view(), bool trim = false) {
|
|
||||||
again:
|
|
||||||
// skip whitespace (except for \n as it might be a close)
|
|
||||||
if (tok_start >= m_in.size()) {
|
|
||||||
return make_token(Token::Kind::Eof);
|
|
||||||
}
|
|
||||||
const char ch = m_in[tok_start];
|
|
||||||
if (ch == ' ' || ch == '\t' || ch == '\r') {
|
|
||||||
tok_start += 1;
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for close
|
|
||||||
if (!close_trim.empty() && inja::string_view::starts_with(m_in.substr(tok_start), close_trim)) {
|
|
||||||
state = State::Text;
|
|
||||||
pos = tok_start + close_trim.size();
|
|
||||||
const Token tok = make_token(closeKind);
|
|
||||||
skip_whitespaces_and_newlines();
|
|
||||||
return tok;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inja::string_view::starts_with(m_in.substr(tok_start), close)) {
|
|
||||||
state = State::Text;
|
|
||||||
pos = tok_start + close.size();
|
|
||||||
const Token tok = make_token(closeKind);
|
|
||||||
if (trim) {
|
|
||||||
skip_whitespaces_and_first_newline();
|
|
||||||
}
|
|
||||||
return tok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip \n
|
|
||||||
if (ch == '\n') {
|
|
||||||
tok_start += 1;
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = tok_start + 1;
|
|
||||||
if (std::isalpha(ch)) {
|
|
||||||
minus_state = MinusState::Operator;
|
|
||||||
return scan_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
const MinusState current_minus_state = minus_state;
|
|
||||||
if (minus_state == MinusState::Operator) {
|
|
||||||
minus_state = MinusState::Number;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (ch) {
|
|
||||||
case '+':
|
|
||||||
return make_token(Token::Kind::Plus);
|
|
||||||
case '-':
|
|
||||||
if (current_minus_state == MinusState::Operator) {
|
|
||||||
return make_token(Token::Kind::Minus);
|
|
||||||
}
|
|
||||||
return scan_number();
|
|
||||||
case '*':
|
|
||||||
return make_token(Token::Kind::Times);
|
|
||||||
case '/':
|
|
||||||
return make_token(Token::Kind::Slash);
|
|
||||||
case '^':
|
|
||||||
return make_token(Token::Kind::Power);
|
|
||||||
case '%':
|
|
||||||
return make_token(Token::Kind::Percent);
|
|
||||||
case '.':
|
|
||||||
return make_token(Token::Kind::Dot);
|
|
||||||
case ',':
|
|
||||||
return make_token(Token::Kind::Comma);
|
|
||||||
case ':':
|
|
||||||
return make_token(Token::Kind::Colon);
|
|
||||||
case '(':
|
|
||||||
return make_token(Token::Kind::LeftParen);
|
|
||||||
case ')':
|
|
||||||
minus_state = MinusState::Operator;
|
|
||||||
return make_token(Token::Kind::RightParen);
|
|
||||||
case '[':
|
|
||||||
return make_token(Token::Kind::LeftBracket);
|
|
||||||
case ']':
|
|
||||||
minus_state = MinusState::Operator;
|
|
||||||
return make_token(Token::Kind::RightBracket);
|
|
||||||
case '{':
|
|
||||||
return make_token(Token::Kind::LeftBrace);
|
|
||||||
case '}':
|
|
||||||
minus_state = MinusState::Operator;
|
|
||||||
return make_token(Token::Kind::RightBrace);
|
|
||||||
case '>':
|
|
||||||
if (pos < m_in.size() && m_in[pos] == '=') {
|
|
||||||
pos += 1;
|
|
||||||
return make_token(Token::Kind::GreaterEqual);
|
|
||||||
}
|
|
||||||
return make_token(Token::Kind::GreaterThan);
|
|
||||||
case '<':
|
|
||||||
if (pos < m_in.size() && m_in[pos] == '=') {
|
|
||||||
pos += 1;
|
|
||||||
return make_token(Token::Kind::LessEqual);
|
|
||||||
}
|
|
||||||
return make_token(Token::Kind::LessThan);
|
|
||||||
case '=':
|
|
||||||
if (pos < m_in.size() && m_in[pos] == '=') {
|
|
||||||
pos += 1;
|
|
||||||
return make_token(Token::Kind::Equal);
|
|
||||||
}
|
|
||||||
return make_token(Token::Kind::Unknown);
|
|
||||||
case '!':
|
|
||||||
if (pos < m_in.size() && m_in[pos] == '=') {
|
|
||||||
pos += 1;
|
|
||||||
return make_token(Token::Kind::NotEqual);
|
|
||||||
}
|
|
||||||
return make_token(Token::Kind::Unknown);
|
|
||||||
case '\"':
|
|
||||||
return scan_string();
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':
|
|
||||||
minus_state = MinusState::Operator;
|
|
||||||
return scan_number();
|
|
||||||
case '_':
|
|
||||||
case '@':
|
|
||||||
case '$':
|
|
||||||
minus_state = MinusState::Operator;
|
|
||||||
return scan_id();
|
|
||||||
default:
|
|
||||||
return make_token(Token::Kind::Unknown);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Token scan_id() {
|
|
||||||
for (;;) {
|
|
||||||
if (pos >= m_in.size()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const char ch = m_in[pos];
|
|
||||||
if (!std::isalnum(ch) && ch != '.' && ch != '/' && ch != '_' && ch != '-') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
return make_token(Token::Kind::Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Token scan_number() {
|
|
||||||
for (;;) {
|
|
||||||
if (pos >= m_in.size()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const char ch = m_in[pos];
|
|
||||||
// be very permissive in lexer (we'll catch errors when conversion happens)
|
|
||||||
if (!(std::isdigit(ch) || ch == '.' || ch == 'e' || ch == 'E' || (ch == '+' && (pos == 0 || m_in[pos-1] == 'e' || m_in[pos-1] == 'E')) || (ch == '-' && (pos == 0 || m_in[pos-1] == 'e' || m_in[pos-1] == 'E')))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
return make_token(Token::Kind::Number);
|
|
||||||
}
|
|
||||||
|
|
||||||
Token scan_string() {
|
|
||||||
bool escape {false};
|
|
||||||
for (;;) {
|
|
||||||
if (pos >= m_in.size()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const char ch = m_in[pos++];
|
|
||||||
if (ch == '\\') {
|
|
||||||
escape = true;
|
|
||||||
} else if (!escape && ch == m_in[tok_start]) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
escape = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return make_token(Token::Kind::String);
|
|
||||||
}
|
|
||||||
|
|
||||||
Token make_token(Token::Kind kind) const {
|
|
||||||
return Token(kind, string_view::slice(m_in, tok_start, pos));
|
|
||||||
}
|
|
||||||
|
|
||||||
void skip_whitespaces_and_newlines() {
|
|
||||||
if (pos < m_in.size()) {
|
|
||||||
while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t' || m_in[pos] == '\n' || m_in[pos] == '\r')) {
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void skip_whitespaces_and_first_newline() {
|
|
||||||
if (pos < m_in.size()) {
|
|
||||||
while (pos < m_in.size() && (m_in[pos] == ' ' || m_in[pos] == '\t')) {
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pos < m_in.size()) {
|
|
||||||
const char ch = m_in[pos];
|
|
||||||
if (ch == '\n') {
|
|
||||||
pos += 1;
|
|
||||||
} else if (ch == '\r') {
|
|
||||||
pos += 1;
|
|
||||||
if (pos < m_in.size() && m_in[pos] == '\n') {
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string_view clear_final_line_if_whitespace(std::string_view text) {
|
|
||||||
std::string_view result = text;
|
|
||||||
while (!result.empty()) {
|
|
||||||
const char ch = result.back();
|
|
||||||
if (ch == ' ' || ch == '\t') {
|
|
||||||
result.remove_suffix(1);
|
|
||||||
} else if (ch == '\n' || ch == '\r') {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Lexer(const LexerConfig& config): config(config), state(State::Text), minus_state(MinusState::Number) {}
|
|
||||||
|
|
||||||
SourceLocation current_position() const {
|
|
||||||
return get_source_location(m_in, tok_start);
|
|
||||||
}
|
|
||||||
|
|
||||||
void start(std::string_view input) {
|
|
||||||
m_in = input;
|
|
||||||
tok_start = 0;
|
|
||||||
pos = 0;
|
|
||||||
state = State::Text;
|
|
||||||
minus_state = MinusState::Number;
|
|
||||||
|
|
||||||
// Consume byte order mark (BOM) for UTF-8
|
|
||||||
if (inja::string_view::starts_with(m_in, "\xEF\xBB\xBF")) {
|
|
||||||
m_in = m_in.substr(3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Token scan() {
|
|
||||||
tok_start = pos;
|
|
||||||
|
|
||||||
again:
|
|
||||||
if (tok_start >= m_in.size()) {
|
|
||||||
return make_token(Token::Kind::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
default:
|
|
||||||
case State::Text: {
|
|
||||||
// fast-scan to first open character
|
|
||||||
const size_t open_start = m_in.substr(pos).find_first_of(config.open_chars);
|
|
||||||
if (open_start == std::string_view::npos) {
|
|
||||||
// didn't find open, return remaining text as text token
|
|
||||||
pos = m_in.size();
|
|
||||||
return make_token(Token::Kind::Text);
|
|
||||||
}
|
|
||||||
pos += open_start;
|
|
||||||
|
|
||||||
// try to match one of the opening sequences, and get the close
|
|
||||||
std::string_view open_str = m_in.substr(pos);
|
|
||||||
bool must_lstrip = false;
|
|
||||||
if (inja::string_view::starts_with(open_str, config.expression_open)) {
|
|
||||||
if (inja::string_view::starts_with(open_str, config.expression_open_force_lstrip)) {
|
|
||||||
state = State::ExpressionStartForceLstrip;
|
|
||||||
must_lstrip = true;
|
|
||||||
} else {
|
|
||||||
state = State::ExpressionStart;
|
|
||||||
}
|
|
||||||
} else if (inja::string_view::starts_with(open_str, config.statement_open)) {
|
|
||||||
if (inja::string_view::starts_with(open_str, config.statement_open_no_lstrip)) {
|
|
||||||
state = State::StatementStartNoLstrip;
|
|
||||||
} else if (inja::string_view::starts_with(open_str, config.statement_open_force_lstrip)) {
|
|
||||||
state = State::StatementStartForceLstrip;
|
|
||||||
must_lstrip = true;
|
|
||||||
} else {
|
|
||||||
state = State::StatementStart;
|
|
||||||
must_lstrip = config.lstrip_blocks;
|
|
||||||
}
|
|
||||||
} else if (inja::string_view::starts_with(open_str, config.comment_open)) {
|
|
||||||
if (inja::string_view::starts_with(open_str, config.comment_open_force_lstrip)) {
|
|
||||||
state = State::CommentStartForceLstrip;
|
|
||||||
must_lstrip = true;
|
|
||||||
} else {
|
|
||||||
state = State::CommentStart;
|
|
||||||
must_lstrip = config.lstrip_blocks;
|
|
||||||
}
|
|
||||||
} else if ((pos == 0 || m_in[pos - 1] == '\n') && inja::string_view::starts_with(open_str, config.line_statement)) {
|
|
||||||
state = State::LineStart;
|
|
||||||
} else {
|
|
||||||
pos += 1; // wasn't actually an opening sequence
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view text = string_view::slice(m_in, tok_start, pos);
|
|
||||||
if (must_lstrip) {
|
|
||||||
text = clear_final_line_if_whitespace(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (text.empty()) {
|
|
||||||
goto again; // don't generate empty token
|
|
||||||
}
|
|
||||||
return Token(Token::Kind::Text, text);
|
|
||||||
}
|
|
||||||
case State::ExpressionStart: {
|
|
||||||
state = State::ExpressionBody;
|
|
||||||
pos += config.expression_open.size();
|
|
||||||
return make_token(Token::Kind::ExpressionOpen);
|
|
||||||
}
|
|
||||||
case State::ExpressionStartForceLstrip: {
|
|
||||||
state = State::ExpressionBody;
|
|
||||||
pos += config.expression_open_force_lstrip.size();
|
|
||||||
return make_token(Token::Kind::ExpressionOpen);
|
|
||||||
}
|
|
||||||
case State::LineStart: {
|
|
||||||
state = State::LineBody;
|
|
||||||
pos += config.line_statement.size();
|
|
||||||
return make_token(Token::Kind::LineStatementOpen);
|
|
||||||
}
|
|
||||||
case State::StatementStart: {
|
|
||||||
state = State::StatementBody;
|
|
||||||
pos += config.statement_open.size();
|
|
||||||
return make_token(Token::Kind::StatementOpen);
|
|
||||||
}
|
|
||||||
case State::StatementStartNoLstrip: {
|
|
||||||
state = State::StatementBody;
|
|
||||||
pos += config.statement_open_no_lstrip.size();
|
|
||||||
return make_token(Token::Kind::StatementOpen);
|
|
||||||
}
|
|
||||||
case State::StatementStartForceLstrip: {
|
|
||||||
state = State::StatementBody;
|
|
||||||
pos += config.statement_open_force_lstrip.size();
|
|
||||||
return make_token(Token::Kind::StatementOpen);
|
|
||||||
}
|
|
||||||
case State::CommentStart: {
|
|
||||||
state = State::CommentBody;
|
|
||||||
pos += config.comment_open.size();
|
|
||||||
return make_token(Token::Kind::CommentOpen);
|
|
||||||
}
|
|
||||||
case State::CommentStartForceLstrip: {
|
|
||||||
state = State::CommentBody;
|
|
||||||
pos += config.comment_open_force_lstrip.size();
|
|
||||||
return make_token(Token::Kind::CommentOpen);
|
|
||||||
}
|
|
||||||
case State::ExpressionBody:
|
|
||||||
return scan_body(config.expression_close, Token::Kind::ExpressionClose, config.expression_close_force_rstrip);
|
|
||||||
case State::LineBody:
|
|
||||||
return scan_body("\n", Token::Kind::LineStatementClose);
|
|
||||||
case State::StatementBody:
|
|
||||||
return scan_body(config.statement_close, Token::Kind::StatementClose, config.statement_close_force_rstrip, config.trim_blocks);
|
|
||||||
case State::CommentBody: {
|
|
||||||
// fast-scan to comment close
|
|
||||||
const size_t end = m_in.substr(pos).find(config.comment_close);
|
|
||||||
if (end == std::string_view::npos) {
|
|
||||||
pos = m_in.size();
|
|
||||||
return make_token(Token::Kind::Eof);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for trim pattern
|
|
||||||
const bool must_rstrip = inja::string_view::starts_with(m_in.substr(pos + end - 1), config.comment_close_force_rstrip);
|
|
||||||
|
|
||||||
// return the entire comment in the close token
|
|
||||||
state = State::Text;
|
|
||||||
pos += end + config.comment_close.size();
|
|
||||||
Token tok = make_token(Token::Kind::CommentClose);
|
|
||||||
|
|
||||||
if (must_rstrip || config.trim_blocks) {
|
|
||||||
skip_whitespaces_and_first_newline();
|
|
||||||
}
|
|
||||||
return tok;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const LexerConfig& get_config() const {
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_LEXER_HPP_
|
|
||||||
@@ -1,372 +0,0 @@
|
|||||||
#ifndef INCLUDE_INJA_NODE_HPP_
|
|
||||||
#define INCLUDE_INJA_NODE_HPP_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "function_storage.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
|
|
||||||
class NodeVisitor;
|
|
||||||
class BlockNode;
|
|
||||||
class TextNode;
|
|
||||||
class ExpressionNode;
|
|
||||||
class LiteralNode;
|
|
||||||
class DataNode;
|
|
||||||
class FunctionNode;
|
|
||||||
class ExpressionListNode;
|
|
||||||
class StatementNode;
|
|
||||||
class ForStatementNode;
|
|
||||||
class ForArrayStatementNode;
|
|
||||||
class ForObjectStatementNode;
|
|
||||||
class IfStatementNode;
|
|
||||||
class IncludeStatementNode;
|
|
||||||
class ExtendsStatementNode;
|
|
||||||
class BlockStatementNode;
|
|
||||||
class SetStatementNode;
|
|
||||||
|
|
||||||
class NodeVisitor {
|
|
||||||
public:
|
|
||||||
virtual ~NodeVisitor() = default;
|
|
||||||
|
|
||||||
virtual void visit(const BlockNode& node) = 0;
|
|
||||||
virtual void visit(const TextNode& node) = 0;
|
|
||||||
virtual void visit(const ExpressionNode& node) = 0;
|
|
||||||
virtual void visit(const LiteralNode& node) = 0;
|
|
||||||
virtual void visit(const DataNode& node) = 0;
|
|
||||||
virtual void visit(const FunctionNode& node) = 0;
|
|
||||||
virtual void visit(const ExpressionListNode& node) = 0;
|
|
||||||
virtual void visit(const StatementNode& node) = 0;
|
|
||||||
virtual void visit(const ForStatementNode& node) = 0;
|
|
||||||
virtual void visit(const ForArrayStatementNode& node) = 0;
|
|
||||||
virtual void visit(const ForObjectStatementNode& node) = 0;
|
|
||||||
virtual void visit(const IfStatementNode& node) = 0;
|
|
||||||
virtual void visit(const IncludeStatementNode& node) = 0;
|
|
||||||
virtual void visit(const ExtendsStatementNode& node) = 0;
|
|
||||||
virtual void visit(const BlockStatementNode& node) = 0;
|
|
||||||
virtual void visit(const SetStatementNode& node) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Base node class for the abstract syntax tree (AST).
|
|
||||||
*/
|
|
||||||
class AstNode {
|
|
||||||
public:
|
|
||||||
virtual void accept(NodeVisitor& v) const = 0;
|
|
||||||
|
|
||||||
size_t pos;
|
|
||||||
|
|
||||||
AstNode(size_t pos): pos(pos) {}
|
|
||||||
virtual ~AstNode() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class BlockNode : public AstNode {
|
|
||||||
public:
|
|
||||||
std::vector<std::shared_ptr<AstNode>> nodes;
|
|
||||||
|
|
||||||
explicit BlockNode(): AstNode(0) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TextNode : public AstNode {
|
|
||||||
public:
|
|
||||||
const size_t length;
|
|
||||||
|
|
||||||
explicit TextNode(size_t pos, size_t length): AstNode(pos), length(length) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ExpressionNode : public AstNode {
|
|
||||||
public:
|
|
||||||
explicit ExpressionNode(size_t pos): AstNode(pos) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class LiteralNode : public ExpressionNode {
|
|
||||||
public:
|
|
||||||
const json value;
|
|
||||||
|
|
||||||
explicit LiteralNode(std::string_view data_text, size_t pos): ExpressionNode(pos), value(json::parse(data_text)) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class DataNode : public ExpressionNode {
|
|
||||||
public:
|
|
||||||
const std::string name;
|
|
||||||
const json::json_pointer ptr;
|
|
||||||
|
|
||||||
static std::string convert_dot_to_ptr(std::string_view ptr_name) {
|
|
||||||
std::string result;
|
|
||||||
do {
|
|
||||||
std::string_view part;
|
|
||||||
std::tie(part, ptr_name) = string_view::split(ptr_name, '.');
|
|
||||||
result.push_back('/');
|
|
||||||
result.append(part.begin(), part.end());
|
|
||||||
} while (!ptr_name.empty());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit DataNode(std::string_view ptr_name, size_t pos): ExpressionNode(pos), name(ptr_name), ptr(json::json_pointer(convert_dot_to_ptr(ptr_name))) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class FunctionNode : public ExpressionNode {
|
|
||||||
using Op = FunctionStorage::Operation;
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum class Associativity {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsigned int precedence;
|
|
||||||
Associativity associativity;
|
|
||||||
|
|
||||||
Op operation;
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
int number_args; // Should also be negative -> -1 for unknown number
|
|
||||||
std::vector<std::shared_ptr<ExpressionNode>> arguments;
|
|
||||||
CallbackFunction callback;
|
|
||||||
|
|
||||||
explicit FunctionNode(std::string_view name, size_t pos)
|
|
||||||
: ExpressionNode(pos), precedence(8), associativity(Associativity::Left), operation(Op::Callback), name(name), number_args(1) {}
|
|
||||||
explicit FunctionNode(Op operation, size_t pos): ExpressionNode(pos), operation(operation), number_args(1) {
|
|
||||||
switch (operation) {
|
|
||||||
case Op::Not: {
|
|
||||||
number_args = 1;
|
|
||||||
precedence = 4;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::And: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 1;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::Or: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 1;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::In: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 2;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::Equal: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 2;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::NotEqual: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 2;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::Greater: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 2;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::GreaterEqual: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 2;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::Less: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 2;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::LessEqual: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 2;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::Add: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 3;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::Subtract: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 3;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::Multiplication: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 4;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::Division: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 4;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::Power: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 5;
|
|
||||||
associativity = Associativity::Right;
|
|
||||||
} break;
|
|
||||||
case Op::Modulo: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 4;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
case Op::AtId: {
|
|
||||||
number_args = 2;
|
|
||||||
precedence = 8;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
} break;
|
|
||||||
default: {
|
|
||||||
precedence = 1;
|
|
||||||
associativity = Associativity::Left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ExpressionListNode : public AstNode {
|
|
||||||
public:
|
|
||||||
std::shared_ptr<ExpressionNode> root;
|
|
||||||
|
|
||||||
explicit ExpressionListNode(): AstNode(0) {}
|
|
||||||
explicit ExpressionListNode(size_t pos): AstNode(pos) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class StatementNode : public AstNode {
|
|
||||||
public:
|
|
||||||
StatementNode(size_t pos): AstNode(pos) {}
|
|
||||||
|
|
||||||
virtual void accept(NodeVisitor& v) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ForStatementNode : public StatementNode {
|
|
||||||
public:
|
|
||||||
ExpressionListNode condition;
|
|
||||||
BlockNode body;
|
|
||||||
BlockNode* const parent;
|
|
||||||
|
|
||||||
ForStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent) {}
|
|
||||||
|
|
||||||
virtual void accept(NodeVisitor& v) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ForArrayStatementNode : public ForStatementNode {
|
|
||||||
public:
|
|
||||||
const std::string value;
|
|
||||||
|
|
||||||
explicit ForArrayStatementNode(const std::string& value, BlockNode* const parent, size_t pos): ForStatementNode(parent, pos), value(value) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ForObjectStatementNode : public ForStatementNode {
|
|
||||||
public:
|
|
||||||
const std::string key;
|
|
||||||
const std::string value;
|
|
||||||
|
|
||||||
explicit ForObjectStatementNode(const std::string& key, const std::string& value, BlockNode* const parent, size_t pos)
|
|
||||||
: ForStatementNode(parent, pos), key(key), value(value) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class IfStatementNode : public StatementNode {
|
|
||||||
public:
|
|
||||||
ExpressionListNode condition;
|
|
||||||
BlockNode true_statement;
|
|
||||||
BlockNode false_statement;
|
|
||||||
BlockNode* const parent;
|
|
||||||
|
|
||||||
const bool is_nested;
|
|
||||||
bool has_false_statement {false};
|
|
||||||
|
|
||||||
explicit IfStatementNode(BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(false) {}
|
|
||||||
explicit IfStatementNode(bool is_nested, BlockNode* const parent, size_t pos): StatementNode(pos), parent(parent), is_nested(is_nested) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class IncludeStatementNode : public StatementNode {
|
|
||||||
public:
|
|
||||||
const std::string file;
|
|
||||||
|
|
||||||
explicit IncludeStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ExtendsStatementNode : public StatementNode {
|
|
||||||
public:
|
|
||||||
const std::string file;
|
|
||||||
|
|
||||||
explicit ExtendsStatementNode(const std::string& file, size_t pos): StatementNode(pos), file(file) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class BlockStatementNode : public StatementNode {
|
|
||||||
public:
|
|
||||||
const std::string name;
|
|
||||||
BlockNode block;
|
|
||||||
BlockNode* const parent;
|
|
||||||
|
|
||||||
explicit BlockStatementNode(BlockNode* const parent, const std::string& name, size_t pos): StatementNode(pos), name(name), parent(parent) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
class SetStatementNode : public StatementNode {
|
|
||||||
public:
|
|
||||||
const std::string key;
|
|
||||||
ExpressionListNode expression;
|
|
||||||
|
|
||||||
explicit SetStatementNode(const std::string& key, size_t pos): StatementNode(pos), key(key) {}
|
|
||||||
|
|
||||||
void accept(NodeVisitor& v) const {
|
|
||||||
v.visit(*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_NODE_HPP_
|
|
||||||
@@ -1,659 +0,0 @@
|
|||||||
#ifndef INCLUDE_INJA_PARSER_HPP_
|
|
||||||
#define INCLUDE_INJA_PARSER_HPP_
|
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
#include <stack>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "config.hpp"
|
|
||||||
#include "exceptions.hpp"
|
|
||||||
#include "function_storage.hpp"
|
|
||||||
#include "lexer.hpp"
|
|
||||||
#include "node.hpp"
|
|
||||||
#include "template.hpp"
|
|
||||||
#include "token.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Class for parsing an inja Template.
|
|
||||||
*/
|
|
||||||
class Parser {
|
|
||||||
const ParserConfig& config;
|
|
||||||
|
|
||||||
Lexer lexer;
|
|
||||||
TemplateStorage& template_storage;
|
|
||||||
const FunctionStorage& function_storage;
|
|
||||||
|
|
||||||
Token tok, peek_tok;
|
|
||||||
bool have_peek_tok {false};
|
|
||||||
|
|
||||||
size_t current_paren_level {0};
|
|
||||||
size_t current_bracket_level {0};
|
|
||||||
size_t current_brace_level {0};
|
|
||||||
|
|
||||||
std::string_view literal_start;
|
|
||||||
|
|
||||||
BlockNode* current_block {nullptr};
|
|
||||||
ExpressionListNode* current_expression_list {nullptr};
|
|
||||||
std::stack<std::pair<FunctionNode*, size_t>> function_stack;
|
|
||||||
std::vector<std::shared_ptr<ExpressionNode>> arguments;
|
|
||||||
|
|
||||||
std::stack<std::shared_ptr<FunctionNode>> operator_stack;
|
|
||||||
std::stack<IfStatementNode*> if_statement_stack;
|
|
||||||
std::stack<ForStatementNode*> for_statement_stack;
|
|
||||||
std::stack<BlockStatementNode*> block_statement_stack;
|
|
||||||
|
|
||||||
inline void throw_parser_error(const std::string& message) const {
|
|
||||||
INJA_THROW(ParserError(message, lexer.current_position()));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void get_next_token() {
|
|
||||||
if (have_peek_tok) {
|
|
||||||
tok = peek_tok;
|
|
||||||
have_peek_tok = false;
|
|
||||||
} else {
|
|
||||||
tok = lexer.scan();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void get_peek_token() {
|
|
||||||
if (!have_peek_tok) {
|
|
||||||
peek_tok = lexer.scan();
|
|
||||||
have_peek_tok = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void add_literal(const char* content_ptr) {
|
|
||||||
std::string_view data_text(literal_start.data(), tok.text.data() - literal_start.data() + tok.text.size());
|
|
||||||
arguments.emplace_back(std::make_shared<LiteralNode>(data_text, data_text.data() - content_ptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void add_operator() {
|
|
||||||
auto function = operator_stack.top();
|
|
||||||
operator_stack.pop();
|
|
||||||
|
|
||||||
for (int i = 0; i < function->number_args; ++i) {
|
|
||||||
function->arguments.insert(function->arguments.begin(), arguments.back());
|
|
||||||
arguments.pop_back();
|
|
||||||
}
|
|
||||||
arguments.emplace_back(function);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_to_template_storage(std::string_view path, std::string& template_name) {
|
|
||||||
if (template_storage.find(template_name) != template_storage.end()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string original_path = static_cast<std::string>(path);
|
|
||||||
std::string original_name = template_name;
|
|
||||||
|
|
||||||
if (config.search_included_templates_in_files) {
|
|
||||||
// Build the relative path
|
|
||||||
template_name = original_path + original_name;
|
|
||||||
if (template_name.compare(0, 2, "./") == 0) {
|
|
||||||
template_name.erase(0, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (template_storage.find(template_name) == template_storage.end()) {
|
|
||||||
// Load file
|
|
||||||
std::ifstream file;
|
|
||||||
file.open(template_name);
|
|
||||||
if (!file.fail()) {
|
|
||||||
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
|
||||||
|
|
||||||
auto include_template = Template(text);
|
|
||||||
template_storage.emplace(template_name, include_template);
|
|
||||||
parse_into_template(template_storage[template_name], template_name);
|
|
||||||
return;
|
|
||||||
} else if (!config.include_callback) {
|
|
||||||
INJA_THROW(FileError("failed accessing file at '" + template_name + "'"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try include callback
|
|
||||||
if (config.include_callback) {
|
|
||||||
auto include_template = config.include_callback(original_path, original_name);
|
|
||||||
template_storage.emplace(template_name, include_template);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string parse_filename(const Token& tok) const {
|
|
||||||
if (tok.kind != Token::Kind::String) {
|
|
||||||
throw_parser_error("expected string, got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tok.text.length() < 2) {
|
|
||||||
throw_parser_error("expected filename, got '" + static_cast<std::string>(tok.text) + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove first and last character ""
|
|
||||||
return std::string {tok.text.substr(1, tok.text.length() - 2)};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parse_expression(Template& tmpl, Token::Kind closing) {
|
|
||||||
while (tok.kind != closing && tok.kind != Token::Kind::Eof) {
|
|
||||||
// Literals
|
|
||||||
switch (tok.kind) {
|
|
||||||
case Token::Kind::String: {
|
|
||||||
if (current_brace_level == 0 && current_bracket_level == 0) {
|
|
||||||
literal_start = tok.text;
|
|
||||||
add_literal(tmpl.content.c_str());
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Number: {
|
|
||||||
if (current_brace_level == 0 && current_bracket_level == 0) {
|
|
||||||
literal_start = tok.text;
|
|
||||||
add_literal(tmpl.content.c_str());
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Token::Kind::LeftBracket: {
|
|
||||||
if (current_brace_level == 0 && current_bracket_level == 0) {
|
|
||||||
literal_start = tok.text;
|
|
||||||
}
|
|
||||||
current_bracket_level += 1;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::LeftBrace: {
|
|
||||||
if (current_brace_level == 0 && current_bracket_level == 0) {
|
|
||||||
literal_start = tok.text;
|
|
||||||
}
|
|
||||||
current_brace_level += 1;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::RightBracket: {
|
|
||||||
if (current_bracket_level == 0) {
|
|
||||||
throw_parser_error("unexpected ']'");
|
|
||||||
}
|
|
||||||
|
|
||||||
current_bracket_level -= 1;
|
|
||||||
if (current_brace_level == 0 && current_bracket_level == 0) {
|
|
||||||
add_literal(tmpl.content.c_str());
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Token::Kind::RightBrace: {
|
|
||||||
if (current_brace_level == 0) {
|
|
||||||
throw_parser_error("unexpected '}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
current_brace_level -= 1;
|
|
||||||
if (current_brace_level == 0 && current_bracket_level == 0) {
|
|
||||||
add_literal(tmpl.content.c_str());
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Id: {
|
|
||||||
get_peek_token();
|
|
||||||
|
|
||||||
// Data Literal
|
|
||||||
if (tok.text == static_cast<decltype(tok.text)>("true") || tok.text == static_cast<decltype(tok.text)>("false") ||
|
|
||||||
tok.text == static_cast<decltype(tok.text)>("null")) {
|
|
||||||
if (current_brace_level == 0 && current_bracket_level == 0) {
|
|
||||||
literal_start = tok.text;
|
|
||||||
add_literal(tmpl.content.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Operator
|
|
||||||
} else if (tok.text == "and" || tok.text == "or" || tok.text == "in" || tok.text == "not") {
|
|
||||||
goto parse_operator;
|
|
||||||
|
|
||||||
// Functions
|
|
||||||
} else if (peek_tok.kind == Token::Kind::LeftParen) {
|
|
||||||
operator_stack.emplace(std::make_shared<FunctionNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
|
|
||||||
function_stack.emplace(operator_stack.top().get(), current_paren_level);
|
|
||||||
|
|
||||||
// Variables
|
|
||||||
} else {
|
|
||||||
arguments.emplace_back(std::make_shared<DataNode>(static_cast<std::string>(tok.text), tok.text.data() - tmpl.content.c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Operators
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Equal:
|
|
||||||
case Token::Kind::NotEqual:
|
|
||||||
case Token::Kind::GreaterThan:
|
|
||||||
case Token::Kind::GreaterEqual:
|
|
||||||
case Token::Kind::LessThan:
|
|
||||||
case Token::Kind::LessEqual:
|
|
||||||
case Token::Kind::Plus:
|
|
||||||
case Token::Kind::Minus:
|
|
||||||
case Token::Kind::Times:
|
|
||||||
case Token::Kind::Slash:
|
|
||||||
case Token::Kind::Power:
|
|
||||||
case Token::Kind::Percent:
|
|
||||||
case Token::Kind::Dot: {
|
|
||||||
|
|
||||||
parse_operator:
|
|
||||||
FunctionStorage::Operation operation;
|
|
||||||
switch (tok.kind) {
|
|
||||||
case Token::Kind::Id: {
|
|
||||||
if (tok.text == "and") {
|
|
||||||
operation = FunctionStorage::Operation::And;
|
|
||||||
} else if (tok.text == "or") {
|
|
||||||
operation = FunctionStorage::Operation::Or;
|
|
||||||
} else if (tok.text == "in") {
|
|
||||||
operation = FunctionStorage::Operation::In;
|
|
||||||
} else if (tok.text == "not") {
|
|
||||||
operation = FunctionStorage::Operation::Not;
|
|
||||||
} else {
|
|
||||||
throw_parser_error("unknown operator in parser.");
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Equal: {
|
|
||||||
operation = FunctionStorage::Operation::Equal;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::NotEqual: {
|
|
||||||
operation = FunctionStorage::Operation::NotEqual;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::GreaterThan: {
|
|
||||||
operation = FunctionStorage::Operation::Greater;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::GreaterEqual: {
|
|
||||||
operation = FunctionStorage::Operation::GreaterEqual;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::LessThan: {
|
|
||||||
operation = FunctionStorage::Operation::Less;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::LessEqual: {
|
|
||||||
operation = FunctionStorage::Operation::LessEqual;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Plus: {
|
|
||||||
operation = FunctionStorage::Operation::Add;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Minus: {
|
|
||||||
operation = FunctionStorage::Operation::Subtract;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Times: {
|
|
||||||
operation = FunctionStorage::Operation::Multiplication;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Slash: {
|
|
||||||
operation = FunctionStorage::Operation::Division;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Power: {
|
|
||||||
operation = FunctionStorage::Operation::Power;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Percent: {
|
|
||||||
operation = FunctionStorage::Operation::Modulo;
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Dot: {
|
|
||||||
operation = FunctionStorage::Operation::AtId;
|
|
||||||
} break;
|
|
||||||
default: {
|
|
||||||
throw_parser_error("unknown operator in parser.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto function_node = std::make_shared<FunctionNode>(operation, tok.text.data() - tmpl.content.c_str());
|
|
||||||
|
|
||||||
while (!operator_stack.empty() &&
|
|
||||||
((operator_stack.top()->precedence > function_node->precedence) ||
|
|
||||||
(operator_stack.top()->precedence == function_node->precedence && function_node->associativity == FunctionNode::Associativity::Left)) &&
|
|
||||||
(operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft)) {
|
|
||||||
add_operator();
|
|
||||||
}
|
|
||||||
|
|
||||||
operator_stack.emplace(function_node);
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Comma: {
|
|
||||||
if (current_brace_level == 0 && current_bracket_level == 0) {
|
|
||||||
if (function_stack.empty()) {
|
|
||||||
throw_parser_error("unexpected ','");
|
|
||||||
}
|
|
||||||
|
|
||||||
function_stack.top().first->number_args += 1;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Token::Kind::Colon: {
|
|
||||||
if (current_brace_level == 0 && current_bracket_level == 0) {
|
|
||||||
throw_parser_error("unexpected ':'");
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Token::Kind::LeftParen: {
|
|
||||||
current_paren_level += 1;
|
|
||||||
operator_stack.emplace(std::make_shared<FunctionNode>(FunctionStorage::Operation::ParenLeft, tok.text.data() - tmpl.content.c_str()));
|
|
||||||
|
|
||||||
get_peek_token();
|
|
||||||
if (peek_tok.kind == Token::Kind::RightParen) {
|
|
||||||
if (!function_stack.empty() && function_stack.top().second == current_paren_level - 1) {
|
|
||||||
function_stack.top().first->number_args = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Token::Kind::RightParen: {
|
|
||||||
current_paren_level -= 1;
|
|
||||||
while (!operator_stack.empty() && operator_stack.top()->operation != FunctionStorage::Operation::ParenLeft) {
|
|
||||||
add_operator();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!operator_stack.empty() && operator_stack.top()->operation == FunctionStorage::Operation::ParenLeft) {
|
|
||||||
operator_stack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!function_stack.empty() && function_stack.top().second == current_paren_level) {
|
|
||||||
auto func = function_stack.top().first;
|
|
||||||
auto function_data = function_storage.find_function(func->name, func->number_args);
|
|
||||||
if (function_data.operation == FunctionStorage::Operation::None) {
|
|
||||||
throw_parser_error("unknown function " + func->name);
|
|
||||||
}
|
|
||||||
func->operation = function_data.operation;
|
|
||||||
if (function_data.operation == FunctionStorage::Operation::Callback) {
|
|
||||||
func->callback = function_data.callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operator_stack.empty()) {
|
|
||||||
throw_parser_error("internal error at function " + func->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
add_operator();
|
|
||||||
function_stack.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_next_token();
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!operator_stack.empty()) {
|
|
||||||
add_operator();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arguments.size() == 1) {
|
|
||||||
current_expression_list->root = arguments[0];
|
|
||||||
arguments = {};
|
|
||||||
} else if (arguments.size() > 1) {
|
|
||||||
throw_parser_error("malformed expression");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parse_statement(Template& tmpl, Token::Kind closing, std::string_view path) {
|
|
||||||
if (tok.kind != Token::Kind::Id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tok.text == static_cast<decltype(tok.text)>("if")) {
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
auto if_statement_node = std::make_shared<IfStatementNode>(current_block, tok.text.data() - tmpl.content.c_str());
|
|
||||||
current_block->nodes.emplace_back(if_statement_node);
|
|
||||||
if_statement_stack.emplace(if_statement_node.get());
|
|
||||||
current_block = &if_statement_node->true_statement;
|
|
||||||
current_expression_list = &if_statement_node->condition;
|
|
||||||
|
|
||||||
if (!parse_expression(tmpl, closing)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (tok.text == static_cast<decltype(tok.text)>("else")) {
|
|
||||||
if (if_statement_stack.empty()) {
|
|
||||||
throw_parser_error("else without matching if");
|
|
||||||
}
|
|
||||||
auto& if_statement_data = if_statement_stack.top();
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
if_statement_data->has_false_statement = true;
|
|
||||||
current_block = &if_statement_data->false_statement;
|
|
||||||
|
|
||||||
// Chained else if
|
|
||||||
if (tok.kind == Token::Kind::Id && tok.text == static_cast<decltype(tok.text)>("if")) {
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
auto if_statement_node = std::make_shared<IfStatementNode>(true, current_block, tok.text.data() - tmpl.content.c_str());
|
|
||||||
current_block->nodes.emplace_back(if_statement_node);
|
|
||||||
if_statement_stack.emplace(if_statement_node.get());
|
|
||||||
current_block = &if_statement_node->true_statement;
|
|
||||||
current_expression_list = &if_statement_node->condition;
|
|
||||||
|
|
||||||
if (!parse_expression(tmpl, closing)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (tok.text == static_cast<decltype(tok.text)>("endif")) {
|
|
||||||
if (if_statement_stack.empty()) {
|
|
||||||
throw_parser_error("endif without matching if");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nested if statements
|
|
||||||
while (if_statement_stack.top()->is_nested) {
|
|
||||||
if_statement_stack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& if_statement_data = if_statement_stack.top();
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
current_block = if_statement_data->parent;
|
|
||||||
if_statement_stack.pop();
|
|
||||||
} else if (tok.text == static_cast<decltype(tok.text)>("block")) {
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
if (tok.kind != Token::Kind::Id) {
|
|
||||||
throw_parser_error("expected block name, got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string block_name = static_cast<std::string>(tok.text);
|
|
||||||
|
|
||||||
auto block_statement_node = std::make_shared<BlockStatementNode>(current_block, block_name, tok.text.data() - tmpl.content.c_str());
|
|
||||||
current_block->nodes.emplace_back(block_statement_node);
|
|
||||||
block_statement_stack.emplace(block_statement_node.get());
|
|
||||||
current_block = &block_statement_node->block;
|
|
||||||
auto success = tmpl.block_storage.emplace(block_name, block_statement_node);
|
|
||||||
if (!success.second) {
|
|
||||||
throw_parser_error("block with the name '" + block_name + "' does already exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
get_next_token();
|
|
||||||
} else if (tok.text == static_cast<decltype(tok.text)>("endblock")) {
|
|
||||||
if (block_statement_stack.empty()) {
|
|
||||||
throw_parser_error("endblock without matching block");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& block_statement_data = block_statement_stack.top();
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
current_block = block_statement_data->parent;
|
|
||||||
block_statement_stack.pop();
|
|
||||||
} else if (tok.text == static_cast<decltype(tok.text)>("for")) {
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
// options: for a in arr; for a, b in obj
|
|
||||||
if (tok.kind != Token::Kind::Id) {
|
|
||||||
throw_parser_error("expected id, got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
Token value_token = tok;
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
// Object type
|
|
||||||
std::shared_ptr<ForStatementNode> for_statement_node;
|
|
||||||
if (tok.kind == Token::Kind::Comma) {
|
|
||||||
get_next_token();
|
|
||||||
if (tok.kind != Token::Kind::Id) {
|
|
||||||
throw_parser_error("expected id, got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
Token key_token = std::move(value_token);
|
|
||||||
value_token = tok;
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
for_statement_node = std::make_shared<ForObjectStatementNode>(static_cast<std::string>(key_token.text), static_cast<std::string>(value_token.text),
|
|
||||||
current_block, tok.text.data() - tmpl.content.c_str());
|
|
||||||
|
|
||||||
// Array type
|
|
||||||
} else {
|
|
||||||
for_statement_node =
|
|
||||||
std::make_shared<ForArrayStatementNode>(static_cast<std::string>(value_token.text), current_block, tok.text.data() - tmpl.content.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
current_block->nodes.emplace_back(for_statement_node);
|
|
||||||
for_statement_stack.emplace(for_statement_node.get());
|
|
||||||
current_block = &for_statement_node->body;
|
|
||||||
current_expression_list = &for_statement_node->condition;
|
|
||||||
|
|
||||||
if (tok.kind != Token::Kind::Id || tok.text != static_cast<decltype(tok.text)>("in")) {
|
|
||||||
throw_parser_error("expected 'in', got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
if (!parse_expression(tmpl, closing)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (tok.text == static_cast<decltype(tok.text)>("endfor")) {
|
|
||||||
if (for_statement_stack.empty()) {
|
|
||||||
throw_parser_error("endfor without matching for");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& for_statement_data = for_statement_stack.top();
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
current_block = for_statement_data->parent;
|
|
||||||
for_statement_stack.pop();
|
|
||||||
} else if (tok.text == static_cast<decltype(tok.text)>("include")) {
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
std::string template_name = parse_filename(tok);
|
|
||||||
add_to_template_storage(path, template_name);
|
|
||||||
|
|
||||||
current_block->nodes.emplace_back(std::make_shared<IncludeStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
|
|
||||||
|
|
||||||
get_next_token();
|
|
||||||
} else if (tok.text == static_cast<decltype(tok.text)>("extends")) {
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
std::string template_name = parse_filename(tok);
|
|
||||||
add_to_template_storage(path, template_name);
|
|
||||||
|
|
||||||
current_block->nodes.emplace_back(std::make_shared<ExtendsStatementNode>(template_name, tok.text.data() - tmpl.content.c_str()));
|
|
||||||
|
|
||||||
get_next_token();
|
|
||||||
} else if (tok.text == static_cast<decltype(tok.text)>("set")) {
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
if (tok.kind != Token::Kind::Id) {
|
|
||||||
throw_parser_error("expected variable name, got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string key = static_cast<std::string>(tok.text);
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
auto set_statement_node = std::make_shared<SetStatementNode>(key, tok.text.data() - tmpl.content.c_str());
|
|
||||||
current_block->nodes.emplace_back(set_statement_node);
|
|
||||||
current_expression_list = &set_statement_node->expression;
|
|
||||||
|
|
||||||
if (tok.text != static_cast<decltype(tok.text)>("=")) {
|
|
||||||
throw_parser_error("expected '=', got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
if (!parse_expression(tmpl, closing)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_into(Template& tmpl, std::string_view path) {
|
|
||||||
lexer.start(tmpl.content);
|
|
||||||
current_block = &tmpl.root;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
get_next_token();
|
|
||||||
switch (tok.kind) {
|
|
||||||
case Token::Kind::Eof: {
|
|
||||||
if (!if_statement_stack.empty()) {
|
|
||||||
throw_parser_error("unmatched if");
|
|
||||||
}
|
|
||||||
if (!for_statement_stack.empty()) {
|
|
||||||
throw_parser_error("unmatched for");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case Token::Kind::Text: {
|
|
||||||
current_block->nodes.emplace_back(std::make_shared<TextNode>(tok.text.data() - tmpl.content.c_str(), tok.text.size()));
|
|
||||||
} break;
|
|
||||||
case Token::Kind::StatementOpen: {
|
|
||||||
get_next_token();
|
|
||||||
if (!parse_statement(tmpl, Token::Kind::StatementClose, path)) {
|
|
||||||
throw_parser_error("expected statement, got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
if (tok.kind != Token::Kind::StatementClose) {
|
|
||||||
throw_parser_error("expected statement close, got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Token::Kind::LineStatementOpen: {
|
|
||||||
get_next_token();
|
|
||||||
if (!parse_statement(tmpl, Token::Kind::LineStatementClose, path)) {
|
|
||||||
throw_parser_error("expected statement, got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
if (tok.kind != Token::Kind::LineStatementClose && tok.kind != Token::Kind::Eof) {
|
|
||||||
throw_parser_error("expected line statement close, got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Token::Kind::ExpressionOpen: {
|
|
||||||
get_next_token();
|
|
||||||
|
|
||||||
auto expression_list_node = std::make_shared<ExpressionListNode>(tok.text.data() - tmpl.content.c_str());
|
|
||||||
current_block->nodes.emplace_back(expression_list_node);
|
|
||||||
current_expression_list = expression_list_node.get();
|
|
||||||
|
|
||||||
if (!parse_expression(tmpl, Token::Kind::ExpressionClose)) {
|
|
||||||
throw_parser_error("expected expression, got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tok.kind != Token::Kind::ExpressionClose) {
|
|
||||||
throw_parser_error("expected expression close, got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Token::Kind::CommentOpen: {
|
|
||||||
get_next_token();
|
|
||||||
if (tok.kind != Token::Kind::CommentClose) {
|
|
||||||
throw_parser_error("expected comment close, got '" + tok.describe() + "'");
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
default: {
|
|
||||||
throw_parser_error("unexpected token '" + tok.describe() + "'");
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Parser(const ParserConfig& parser_config, const LexerConfig& lexer_config, TemplateStorage& template_storage,
|
|
||||||
const FunctionStorage& function_storage)
|
|
||||||
: config(parser_config), lexer(lexer_config), template_storage(template_storage), function_storage(function_storage) {}
|
|
||||||
|
|
||||||
Template parse(std::string_view input, std::string_view path) {
|
|
||||||
auto result = Template(static_cast<std::string>(input));
|
|
||||||
parse_into(result, path);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Template parse(std::string_view input) {
|
|
||||||
return parse(input, "./");
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_into_template(Template& tmpl, std::string_view filename) {
|
|
||||||
std::string_view path = filename.substr(0, filename.find_last_of("/\\") + 1);
|
|
||||||
|
|
||||||
// StringRef path = sys::path::parent_path(filename);
|
|
||||||
auto sub_parser = Parser(config, lexer.get_config(), template_storage, function_storage);
|
|
||||||
sub_parser.parse_into(tmpl, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string load_file(const std::string& filename) {
|
|
||||||
std::ifstream file;
|
|
||||||
file.open(filename);
|
|
||||||
if (file.fail()) {
|
|
||||||
INJA_THROW(FileError("failed accessing file at '" + filename + "'"));
|
|
||||||
}
|
|
||||||
std::string text((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_PARSER_HPP_
|
|
||||||
@@ -1,633 +0,0 @@
|
|||||||
#ifndef INCLUDE_INJA_RENDERER_HPP_
|
|
||||||
#define INCLUDE_INJA_RENDERER_HPP_
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <numeric>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "config.hpp"
|
|
||||||
#include "exceptions.hpp"
|
|
||||||
#include "node.hpp"
|
|
||||||
#include "template.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Class for rendering a Template with data.
|
|
||||||
*/
|
|
||||||
class Renderer : public NodeVisitor {
|
|
||||||
using Op = FunctionStorage::Operation;
|
|
||||||
|
|
||||||
const RenderConfig config;
|
|
||||||
const TemplateStorage& template_storage;
|
|
||||||
const FunctionStorage& function_storage;
|
|
||||||
|
|
||||||
const Template* current_template;
|
|
||||||
size_t current_level {0};
|
|
||||||
std::vector<const Template*> template_stack;
|
|
||||||
std::vector<const BlockStatementNode*> block_statement_stack;
|
|
||||||
|
|
||||||
const json* data_input;
|
|
||||||
std::ostream* output_stream;
|
|
||||||
|
|
||||||
json additional_data;
|
|
||||||
json* current_loop_data = &additional_data["loop"];
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<json>> data_tmp_stack;
|
|
||||||
std::stack<const json*> data_eval_stack;
|
|
||||||
std::stack<const DataNode*> not_found_stack;
|
|
||||||
|
|
||||||
bool break_rendering {false};
|
|
||||||
|
|
||||||
static bool truthy(const json* data) {
|
|
||||||
if (data->is_boolean()) {
|
|
||||||
return data->get<bool>();
|
|
||||||
} else if (data->is_number()) {
|
|
||||||
return (*data != 0);
|
|
||||||
} else if (data->is_null()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !data->empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_data(const std::shared_ptr<json> value) {
|
|
||||||
if (value->is_string()) {
|
|
||||||
*output_stream << value->get_ref<const json::string_t&>();
|
|
||||||
} else if (value->is_number_integer()) {
|
|
||||||
*output_stream << value->get<const json::number_integer_t>();
|
|
||||||
} else if (value->is_null()) {
|
|
||||||
} else {
|
|
||||||
*output_stream << value->dump();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::shared_ptr<json> eval_expression_list(const ExpressionListNode& expression_list) {
|
|
||||||
if (!expression_list.root) {
|
|
||||||
throw_renderer_error("empty expression", expression_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
expression_list.root->accept(*this);
|
|
||||||
|
|
||||||
if (data_eval_stack.empty()) {
|
|
||||||
throw_renderer_error("empty expression", expression_list);
|
|
||||||
} else if (data_eval_stack.size() != 1) {
|
|
||||||
throw_renderer_error("malformed expression", expression_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto result = data_eval_stack.top();
|
|
||||||
data_eval_stack.pop();
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
if (not_found_stack.empty()) {
|
|
||||||
throw_renderer_error("expression could not be evaluated", expression_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto node = not_found_stack.top();
|
|
||||||
not_found_stack.pop();
|
|
||||||
|
|
||||||
throw_renderer_error("variable '" + static_cast<std::string>(node->name) + "' not found", *node);
|
|
||||||
}
|
|
||||||
return std::make_shared<json>(*result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void throw_renderer_error(const std::string& message, const AstNode& node) {
|
|
||||||
SourceLocation loc = get_source_location(current_template->content, node.pos);
|
|
||||||
INJA_THROW(RenderError(message, loc));
|
|
||||||
}
|
|
||||||
|
|
||||||
void make_result(const json&& result) {
|
|
||||||
auto result_ptr = std::make_shared<json>(result);
|
|
||||||
data_tmp_stack.push_back(result_ptr);
|
|
||||||
data_eval_stack.push(result_ptr.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <size_t N, size_t N_start = 0, bool throw_not_found = true> std::array<const json*, N> get_arguments(const FunctionNode& node) {
|
|
||||||
if (node.arguments.size() < N_start + N) {
|
|
||||||
throw_renderer_error("function needs " + std::to_string(N_start + N) + " variables, but has only found " + std::to_string(node.arguments.size()), node);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = N_start; i < N_start + N; i += 1) {
|
|
||||||
node.arguments[i]->accept(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data_eval_stack.size() < N) {
|
|
||||||
throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::array<const json*, N> result;
|
|
||||||
for (size_t i = 0; i < N; i += 1) {
|
|
||||||
result[N - i - 1] = data_eval_stack.top();
|
|
||||||
data_eval_stack.pop();
|
|
||||||
|
|
||||||
if (!result[N - i - 1]) {
|
|
||||||
const auto data_node = not_found_stack.top();
|
|
||||||
not_found_stack.pop();
|
|
||||||
|
|
||||||
if (throw_not_found) {
|
|
||||||
throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool throw_not_found = true> Arguments get_argument_vector(const FunctionNode& node) {
|
|
||||||
const size_t N = node.arguments.size();
|
|
||||||
for (auto a : node.arguments) {
|
|
||||||
a->accept(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data_eval_stack.size() < N) {
|
|
||||||
throw_renderer_error("function needs " + std::to_string(N) + " variables, but has only found " + std::to_string(data_eval_stack.size()), node);
|
|
||||||
}
|
|
||||||
|
|
||||||
Arguments result {N};
|
|
||||||
for (size_t i = 0; i < N; i += 1) {
|
|
||||||
result[N - i - 1] = data_eval_stack.top();
|
|
||||||
data_eval_stack.pop();
|
|
||||||
|
|
||||||
if (!result[N - i - 1]) {
|
|
||||||
const auto data_node = not_found_stack.top();
|
|
||||||
not_found_stack.pop();
|
|
||||||
|
|
||||||
if (throw_not_found) {
|
|
||||||
throw_renderer_error("variable '" + static_cast<std::string>(data_node->name) + "' not found", *data_node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const BlockNode& node) {
|
|
||||||
for (auto& n : node.nodes) {
|
|
||||||
n->accept(*this);
|
|
||||||
|
|
||||||
if (break_rendering) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const TextNode& node) {
|
|
||||||
output_stream->write(current_template->content.c_str() + node.pos, node.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const ExpressionNode&) {}
|
|
||||||
|
|
||||||
void visit(const LiteralNode& node) {
|
|
||||||
data_eval_stack.push(&node.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const DataNode& node) {
|
|
||||||
if (additional_data.contains(node.ptr)) {
|
|
||||||
data_eval_stack.push(&(additional_data[node.ptr]));
|
|
||||||
} else if (data_input->contains(node.ptr)) {
|
|
||||||
data_eval_stack.push(&(*data_input)[node.ptr]);
|
|
||||||
} else {
|
|
||||||
// Try to evaluate as a no-argument callback
|
|
||||||
const auto function_data = function_storage.find_function(node.name, 0);
|
|
||||||
if (function_data.operation == FunctionStorage::Operation::Callback) {
|
|
||||||
Arguments empty_args {};
|
|
||||||
const auto value = std::make_shared<json>(function_data.callback(empty_args));
|
|
||||||
data_tmp_stack.push_back(value);
|
|
||||||
data_eval_stack.push(value.get());
|
|
||||||
} else {
|
|
||||||
data_eval_stack.push(nullptr);
|
|
||||||
not_found_stack.emplace(&node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const FunctionNode& node) {
|
|
||||||
switch (node.operation) {
|
|
||||||
case Op::Not: {
|
|
||||||
const auto args = get_arguments<1>(node);
|
|
||||||
make_result(!truthy(args[0]));
|
|
||||||
} break;
|
|
||||||
case Op::And: {
|
|
||||||
make_result(truthy(get_arguments<1, 0>(node)[0]) && truthy(get_arguments<1, 1>(node)[0]));
|
|
||||||
} break;
|
|
||||||
case Op::Or: {
|
|
||||||
make_result(truthy(get_arguments<1, 0>(node)[0]) || truthy(get_arguments<1, 1>(node)[0]));
|
|
||||||
} break;
|
|
||||||
case Op::In: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
make_result(std::find(args[1]->begin(), args[1]->end(), *args[0]) != args[1]->end());
|
|
||||||
} break;
|
|
||||||
case Op::Equal: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
make_result(*args[0] == *args[1]);
|
|
||||||
} break;
|
|
||||||
case Op::NotEqual: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
make_result(*args[0] != *args[1]);
|
|
||||||
} break;
|
|
||||||
case Op::Greater: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
make_result(*args[0] > *args[1]);
|
|
||||||
} break;
|
|
||||||
case Op::GreaterEqual: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
make_result(*args[0] >= *args[1]);
|
|
||||||
} break;
|
|
||||||
case Op::Less: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
make_result(*args[0] < *args[1]);
|
|
||||||
} break;
|
|
||||||
case Op::LessEqual: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
make_result(*args[0] <= *args[1]);
|
|
||||||
} break;
|
|
||||||
case Op::Add: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
if (args[0]->is_string() && args[1]->is_string()) {
|
|
||||||
make_result(args[0]->get_ref<const std::string&>() + args[1]->get_ref<const std::string&>());
|
|
||||||
} else if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
|
|
||||||
make_result(args[0]->get<int>() + args[1]->get<int>());
|
|
||||||
} else {
|
|
||||||
make_result(args[0]->get<double>() + args[1]->get<double>());
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Op::Subtract: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
|
|
||||||
make_result(args[0]->get<int>() - args[1]->get<int>());
|
|
||||||
} else {
|
|
||||||
make_result(args[0]->get<double>() - args[1]->get<double>());
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Op::Multiplication: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
if (args[0]->is_number_integer() && args[1]->is_number_integer()) {
|
|
||||||
make_result(args[0]->get<int>() * args[1]->get<int>());
|
|
||||||
} else {
|
|
||||||
make_result(args[0]->get<double>() * args[1]->get<double>());
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Op::Division: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
if (args[1]->get<double>() == 0) {
|
|
||||||
throw_renderer_error("division by zero", node);
|
|
||||||
}
|
|
||||||
make_result(args[0]->get<double>() / args[1]->get<double>());
|
|
||||||
} break;
|
|
||||||
case Op::Power: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
if (args[0]->is_number_integer() && args[1]->get<int>() >= 0) {
|
|
||||||
int result = static_cast<int>(std::pow(args[0]->get<int>(), args[1]->get<int>()));
|
|
||||||
make_result(result);
|
|
||||||
} else {
|
|
||||||
double result = std::pow(args[0]->get<double>(), args[1]->get<int>());
|
|
||||||
make_result(result);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Op::Modulo: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
make_result(args[0]->get<int>() % args[1]->get<int>());
|
|
||||||
} break;
|
|
||||||
case Op::AtId: {
|
|
||||||
const auto container = get_arguments<1, 0, false>(node)[0];
|
|
||||||
node.arguments[1]->accept(*this);
|
|
||||||
if (not_found_stack.empty()) {
|
|
||||||
throw_renderer_error("could not find element with given name", node);
|
|
||||||
}
|
|
||||||
const auto id_node = not_found_stack.top();
|
|
||||||
not_found_stack.pop();
|
|
||||||
data_eval_stack.pop();
|
|
||||||
data_eval_stack.push(&container->at(id_node->name));
|
|
||||||
} break;
|
|
||||||
case Op::At: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
if (args[0]->is_object()) {
|
|
||||||
data_eval_stack.push(&args[0]->at(args[1]->get<std::string>()));
|
|
||||||
} else {
|
|
||||||
data_eval_stack.push(&args[0]->at(args[1]->get<int>()));
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Op::Default: {
|
|
||||||
const auto test_arg = get_arguments<1, 0, false>(node)[0];
|
|
||||||
data_eval_stack.push(test_arg ? test_arg : get_arguments<1, 1>(node)[0]);
|
|
||||||
} break;
|
|
||||||
case Op::DivisibleBy: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
const int divisor = args[1]->get<int>();
|
|
||||||
make_result((divisor != 0) && (args[0]->get<int>() % divisor == 0));
|
|
||||||
} break;
|
|
||||||
case Op::Even: {
|
|
||||||
make_result(get_arguments<1>(node)[0]->get<int>() % 2 == 0);
|
|
||||||
} break;
|
|
||||||
case Op::Exists: {
|
|
||||||
auto&& name = get_arguments<1>(node)[0]->get_ref<const std::string&>();
|
|
||||||
make_result(data_input->contains(json::json_pointer(DataNode::convert_dot_to_ptr(name))));
|
|
||||||
} break;
|
|
||||||
case Op::ExistsInObject: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
auto&& name = args[1]->get_ref<const std::string&>();
|
|
||||||
make_result(args[0]->find(name) != args[0]->end());
|
|
||||||
} break;
|
|
||||||
case Op::First: {
|
|
||||||
const auto result = &get_arguments<1>(node)[0]->front();
|
|
||||||
data_eval_stack.push(result);
|
|
||||||
} break;
|
|
||||||
case Op::Float: {
|
|
||||||
make_result(std::stod(get_arguments<1>(node)[0]->get_ref<const std::string&>()));
|
|
||||||
} break;
|
|
||||||
case Op::Int: {
|
|
||||||
make_result(std::stoi(get_arguments<1>(node)[0]->get_ref<const std::string&>()));
|
|
||||||
} break;
|
|
||||||
case Op::Last: {
|
|
||||||
const auto result = &get_arguments<1>(node)[0]->back();
|
|
||||||
data_eval_stack.push(result);
|
|
||||||
} break;
|
|
||||||
case Op::Length: {
|
|
||||||
const auto val = get_arguments<1>(node)[0];
|
|
||||||
if (val->is_string()) {
|
|
||||||
make_result(val->get_ref<const std::string&>().length());
|
|
||||||
} else {
|
|
||||||
make_result(val->size());
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Op::Lower: {
|
|
||||||
std::string result = get_arguments<1>(node)[0]->get<std::string>();
|
|
||||||
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
|
|
||||||
make_result(std::move(result));
|
|
||||||
} break;
|
|
||||||
case Op::Max: {
|
|
||||||
const auto args = get_arguments<1>(node);
|
|
||||||
const auto result = std::max_element(args[0]->begin(), args[0]->end());
|
|
||||||
data_eval_stack.push(&(*result));
|
|
||||||
} break;
|
|
||||||
case Op::Min: {
|
|
||||||
const auto args = get_arguments<1>(node);
|
|
||||||
const auto result = std::min_element(args[0]->begin(), args[0]->end());
|
|
||||||
data_eval_stack.push(&(*result));
|
|
||||||
} break;
|
|
||||||
case Op::Odd: {
|
|
||||||
make_result(get_arguments<1>(node)[0]->get<int>() % 2 != 0);
|
|
||||||
} break;
|
|
||||||
case Op::Range: {
|
|
||||||
std::vector<int> result(get_arguments<1>(node)[0]->get<int>());
|
|
||||||
std::iota(result.begin(), result.end(), 0);
|
|
||||||
make_result(std::move(result));
|
|
||||||
} break;
|
|
||||||
case Op::Round: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
const int precision = args[1]->get<int>();
|
|
||||||
const double result = std::round(args[0]->get<double>() * std::pow(10.0, precision)) / std::pow(10.0, precision);
|
|
||||||
if (precision == 0) {
|
|
||||||
make_result(int(result));
|
|
||||||
} else {
|
|
||||||
make_result(result);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case Op::Sort: {
|
|
||||||
auto result_ptr = std::make_shared<json>(get_arguments<1>(node)[0]->get<std::vector<json>>());
|
|
||||||
std::sort(result_ptr->begin(), result_ptr->end());
|
|
||||||
data_tmp_stack.push_back(result_ptr);
|
|
||||||
data_eval_stack.push(result_ptr.get());
|
|
||||||
} break;
|
|
||||||
case Op::Upper: {
|
|
||||||
std::string result = get_arguments<1>(node)[0]->get<std::string>();
|
|
||||||
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
|
|
||||||
make_result(std::move(result));
|
|
||||||
} break;
|
|
||||||
case Op::IsBoolean: {
|
|
||||||
make_result(get_arguments<1>(node)[0]->is_boolean());
|
|
||||||
} break;
|
|
||||||
case Op::IsNumber: {
|
|
||||||
make_result(get_arguments<1>(node)[0]->is_number());
|
|
||||||
} break;
|
|
||||||
case Op::IsInteger: {
|
|
||||||
make_result(get_arguments<1>(node)[0]->is_number_integer());
|
|
||||||
} break;
|
|
||||||
case Op::IsFloat: {
|
|
||||||
make_result(get_arguments<1>(node)[0]->is_number_float());
|
|
||||||
} break;
|
|
||||||
case Op::IsObject: {
|
|
||||||
make_result(get_arguments<1>(node)[0]->is_object());
|
|
||||||
} break;
|
|
||||||
case Op::IsArray: {
|
|
||||||
make_result(get_arguments<1>(node)[0]->is_array());
|
|
||||||
} break;
|
|
||||||
case Op::IsString: {
|
|
||||||
make_result(get_arguments<1>(node)[0]->is_string());
|
|
||||||
} break;
|
|
||||||
case Op::Callback: {
|
|
||||||
auto args = get_argument_vector(node);
|
|
||||||
make_result(node.callback(args));
|
|
||||||
} break;
|
|
||||||
case Op::Super: {
|
|
||||||
const auto args = get_argument_vector(node);
|
|
||||||
const size_t old_level = current_level;
|
|
||||||
const size_t level_diff = (args.size() == 1) ? args[0]->get<int>() : 1;
|
|
||||||
const size_t level = current_level + level_diff;
|
|
||||||
|
|
||||||
if (block_statement_stack.empty()) {
|
|
||||||
throw_renderer_error("super() call is not within a block", node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (level < 1 || level > template_stack.size() - 1) {
|
|
||||||
throw_renderer_error("level of super() call does not match parent templates (between 1 and " + std::to_string(template_stack.size() - 1) + ")", node);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto current_block_statement = block_statement_stack.back();
|
|
||||||
const Template* new_template = template_stack.at(level);
|
|
||||||
const Template* old_template = current_template;
|
|
||||||
const auto block_it = new_template->block_storage.find(current_block_statement->name);
|
|
||||||
if (block_it != new_template->block_storage.end()) {
|
|
||||||
current_template = new_template;
|
|
||||||
current_level = level;
|
|
||||||
block_it->second->block.accept(*this);
|
|
||||||
current_level = old_level;
|
|
||||||
current_template = old_template;
|
|
||||||
} else {
|
|
||||||
throw_renderer_error("could not find block with name '" + current_block_statement->name + "'", node);
|
|
||||||
}
|
|
||||||
make_result(nullptr);
|
|
||||||
} break;
|
|
||||||
case Op::Join: {
|
|
||||||
const auto args = get_arguments<2>(node);
|
|
||||||
const auto separator = args[1]->get<std::string>();
|
|
||||||
std::ostringstream os;
|
|
||||||
std::string sep;
|
|
||||||
for (const auto& value : *args[0]) {
|
|
||||||
os << sep;
|
|
||||||
if (value.is_string()) {
|
|
||||||
os << value.get<std::string>(); // otherwise the value is surrounded with ""
|
|
||||||
} else {
|
|
||||||
os << value.dump();
|
|
||||||
}
|
|
||||||
sep = separator;
|
|
||||||
}
|
|
||||||
make_result(os.str());
|
|
||||||
} break;
|
|
||||||
case Op::ParenLeft:
|
|
||||||
case Op::ParenRight:
|
|
||||||
case Op::None:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const ExpressionListNode& node) {
|
|
||||||
print_data(eval_expression_list(node));
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const StatementNode&) {}
|
|
||||||
|
|
||||||
void visit(const ForStatementNode&) {}
|
|
||||||
|
|
||||||
void visit(const ForArrayStatementNode& node) {
|
|
||||||
const auto result = eval_expression_list(node.condition);
|
|
||||||
if (!result->is_array()) {
|
|
||||||
throw_renderer_error("object must be an array", node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!current_loop_data->empty()) {
|
|
||||||
auto tmp = *current_loop_data; // Because of clang-3
|
|
||||||
(*current_loop_data)["parent"] = std::move(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = 0;
|
|
||||||
(*current_loop_data)["is_first"] = true;
|
|
||||||
(*current_loop_data)["is_last"] = (result->size() <= 1);
|
|
||||||
for (auto it = result->begin(); it != result->end(); ++it) {
|
|
||||||
additional_data[static_cast<std::string>(node.value)] = *it;
|
|
||||||
|
|
||||||
(*current_loop_data)["index"] = index;
|
|
||||||
(*current_loop_data)["index1"] = index + 1;
|
|
||||||
if (index == 1) {
|
|
||||||
(*current_loop_data)["is_first"] = false;
|
|
||||||
}
|
|
||||||
if (index == result->size() - 1) {
|
|
||||||
(*current_loop_data)["is_last"] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
node.body.accept(*this);
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
|
|
||||||
additional_data[static_cast<std::string>(node.value)].clear();
|
|
||||||
if (!(*current_loop_data)["parent"].empty()) {
|
|
||||||
const auto tmp = (*current_loop_data)["parent"];
|
|
||||||
*current_loop_data = std::move(tmp);
|
|
||||||
} else {
|
|
||||||
current_loop_data = &additional_data["loop"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const ForObjectStatementNode& node) {
|
|
||||||
const auto result = eval_expression_list(node.condition);
|
|
||||||
if (!result->is_object()) {
|
|
||||||
throw_renderer_error("object must be an object", node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!current_loop_data->empty()) {
|
|
||||||
(*current_loop_data)["parent"] = std::move(*current_loop_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t index = 0;
|
|
||||||
(*current_loop_data)["is_first"] = true;
|
|
||||||
(*current_loop_data)["is_last"] = (result->size() <= 1);
|
|
||||||
for (auto it = result->begin(); it != result->end(); ++it) {
|
|
||||||
additional_data[static_cast<std::string>(node.key)] = it.key();
|
|
||||||
additional_data[static_cast<std::string>(node.value)] = it.value();
|
|
||||||
|
|
||||||
(*current_loop_data)["index"] = index;
|
|
||||||
(*current_loop_data)["index1"] = index + 1;
|
|
||||||
if (index == 1) {
|
|
||||||
(*current_loop_data)["is_first"] = false;
|
|
||||||
}
|
|
||||||
if (index == result->size() - 1) {
|
|
||||||
(*current_loop_data)["is_last"] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
node.body.accept(*this);
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
|
|
||||||
additional_data[static_cast<std::string>(node.key)].clear();
|
|
||||||
additional_data[static_cast<std::string>(node.value)].clear();
|
|
||||||
if (!(*current_loop_data)["parent"].empty()) {
|
|
||||||
*current_loop_data = std::move((*current_loop_data)["parent"]);
|
|
||||||
} else {
|
|
||||||
current_loop_data = &additional_data["loop"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const IfStatementNode& node) {
|
|
||||||
const auto result = eval_expression_list(node.condition);
|
|
||||||
if (truthy(result.get())) {
|
|
||||||
node.true_statement.accept(*this);
|
|
||||||
} else if (node.has_false_statement) {
|
|
||||||
node.false_statement.accept(*this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const IncludeStatementNode& node) {
|
|
||||||
auto sub_renderer = Renderer(config, template_storage, function_storage);
|
|
||||||
const auto included_template_it = template_storage.find(node.file);
|
|
||||||
if (included_template_it != template_storage.end()) {
|
|
||||||
sub_renderer.render_to(*output_stream, included_template_it->second, *data_input, &additional_data);
|
|
||||||
} else if (config.throw_at_missing_includes) {
|
|
||||||
throw_renderer_error("include '" + node.file + "' not found", node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const ExtendsStatementNode& node) {
|
|
||||||
const auto included_template_it = template_storage.find(node.file);
|
|
||||||
if (included_template_it != template_storage.end()) {
|
|
||||||
const Template* parent_template = &included_template_it->second;
|
|
||||||
render_to(*output_stream, *parent_template, *data_input, &additional_data);
|
|
||||||
break_rendering = true;
|
|
||||||
} else if (config.throw_at_missing_includes) {
|
|
||||||
throw_renderer_error("extends '" + node.file + "' not found", node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const BlockStatementNode& node) {
|
|
||||||
const size_t old_level = current_level;
|
|
||||||
current_level = 0;
|
|
||||||
current_template = template_stack.front();
|
|
||||||
const auto block_it = current_template->block_storage.find(node.name);
|
|
||||||
if (block_it != current_template->block_storage.end()) {
|
|
||||||
block_statement_stack.emplace_back(&node);
|
|
||||||
block_it->second->block.accept(*this);
|
|
||||||
block_statement_stack.pop_back();
|
|
||||||
}
|
|
||||||
current_level = old_level;
|
|
||||||
current_template = template_stack.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const SetStatementNode& node) {
|
|
||||||
std::string ptr = node.key;
|
|
||||||
replace_substring(ptr, ".", "/");
|
|
||||||
ptr = "/" + ptr;
|
|
||||||
additional_data[json::json_pointer(ptr)] = *eval_expression_list(node.expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
Renderer(const RenderConfig& config, const TemplateStorage& template_storage, const FunctionStorage& function_storage)
|
|
||||||
: config(config), template_storage(template_storage), function_storage(function_storage) {}
|
|
||||||
|
|
||||||
void render_to(std::ostream& os, const Template& tmpl, const json& data, json* loop_data = nullptr) {
|
|
||||||
output_stream = &os;
|
|
||||||
current_template = &tmpl;
|
|
||||||
data_input = &data;
|
|
||||||
if (loop_data) {
|
|
||||||
additional_data = *loop_data;
|
|
||||||
current_loop_data = &additional_data["loop"];
|
|
||||||
}
|
|
||||||
|
|
||||||
template_stack.emplace_back(current_template);
|
|
||||||
current_template->root.accept(*this);
|
|
||||||
|
|
||||||
data_tmp_stack.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_RENDERER_HPP_
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#ifndef INCLUDE_INJA_STATISTICS_HPP_
|
|
||||||
#define INCLUDE_INJA_STATISTICS_HPP_
|
|
||||||
|
|
||||||
#include "node.hpp"
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief A class for counting statistics on a Template.
|
|
||||||
*/
|
|
||||||
class StatisticsVisitor : public NodeVisitor {
|
|
||||||
void visit(const BlockNode& node) {
|
|
||||||
for (auto& n : node.nodes) {
|
|
||||||
n->accept(*this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const TextNode&) {}
|
|
||||||
void visit(const ExpressionNode&) {}
|
|
||||||
void visit(const LiteralNode&) {}
|
|
||||||
|
|
||||||
void visit(const DataNode&) {
|
|
||||||
variable_counter += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const FunctionNode& node) {
|
|
||||||
for (auto& n : node.arguments) {
|
|
||||||
n->accept(*this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const ExpressionListNode& node) {
|
|
||||||
node.root->accept(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const StatementNode&) {}
|
|
||||||
void visit(const ForStatementNode&) {}
|
|
||||||
|
|
||||||
void visit(const ForArrayStatementNode& node) {
|
|
||||||
node.condition.accept(*this);
|
|
||||||
node.body.accept(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const ForObjectStatementNode& node) {
|
|
||||||
node.condition.accept(*this);
|
|
||||||
node.body.accept(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const IfStatementNode& node) {
|
|
||||||
node.condition.accept(*this);
|
|
||||||
node.true_statement.accept(*this);
|
|
||||||
node.false_statement.accept(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const IncludeStatementNode&) {}
|
|
||||||
|
|
||||||
void visit(const ExtendsStatementNode&) {}
|
|
||||||
|
|
||||||
void visit(const BlockStatementNode& node) {
|
|
||||||
node.block.accept(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit(const SetStatementNode&) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
unsigned int variable_counter;
|
|
||||||
|
|
||||||
explicit StatisticsVisitor(): variable_counter(0) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_STATISTICS_HPP_
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#ifndef INCLUDE_INJA_TEMPLATE_HPP_
|
|
||||||
#define INCLUDE_INJA_TEMPLATE_HPP_
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "node.hpp"
|
|
||||||
#include "statistics.hpp"
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief The main inja Template.
|
|
||||||
*/
|
|
||||||
struct Template {
|
|
||||||
BlockNode root;
|
|
||||||
std::string content;
|
|
||||||
std::map<std::string, std::shared_ptr<BlockStatementNode>> block_storage;
|
|
||||||
|
|
||||||
explicit Template() {}
|
|
||||||
explicit Template(const std::string& content): content(content) {}
|
|
||||||
|
|
||||||
/// Return number of variables (total number, not distinct ones) in the template
|
|
||||||
int count_variables() {
|
|
||||||
auto statistic_visitor = StatisticsVisitor();
|
|
||||||
root.accept(statistic_visitor);
|
|
||||||
return statistic_visitor.variable_counter;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using TemplateStorage = std::map<std::string, Template>;
|
|
||||||
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_TEMPLATE_HPP_
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#ifndef INCLUDE_INJA_TOKEN_HPP_
|
|
||||||
#define INCLUDE_INJA_TOKEN_HPP_
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Helper-class for the inja Lexer.
|
|
||||||
*/
|
|
||||||
struct Token {
|
|
||||||
enum class Kind {
|
|
||||||
Text,
|
|
||||||
ExpressionOpen, // {{
|
|
||||||
ExpressionClose, // }}
|
|
||||||
LineStatementOpen, // ##
|
|
||||||
LineStatementClose, // \n
|
|
||||||
StatementOpen, // {%
|
|
||||||
StatementClose, // %}
|
|
||||||
CommentOpen, // {#
|
|
||||||
CommentClose, // #}
|
|
||||||
Id, // this, this.foo
|
|
||||||
Number, // 1, 2, -1, 5.2, -5.3
|
|
||||||
String, // "this"
|
|
||||||
Plus, // +
|
|
||||||
Minus, // -
|
|
||||||
Times, // *
|
|
||||||
Slash, // /
|
|
||||||
Percent, // %
|
|
||||||
Power, // ^
|
|
||||||
Comma, // ,
|
|
||||||
Dot, // .
|
|
||||||
Colon, // :
|
|
||||||
LeftParen, // (
|
|
||||||
RightParen, // )
|
|
||||||
LeftBracket, // [
|
|
||||||
RightBracket, // ]
|
|
||||||
LeftBrace, // {
|
|
||||||
RightBrace, // }
|
|
||||||
Equal, // ==
|
|
||||||
NotEqual, // !=
|
|
||||||
GreaterThan, // >
|
|
||||||
GreaterEqual, // >=
|
|
||||||
LessThan, // <
|
|
||||||
LessEqual, // <=
|
|
||||||
Unknown,
|
|
||||||
Eof,
|
|
||||||
};
|
|
||||||
|
|
||||||
Kind kind {Kind::Unknown};
|
|
||||||
std::string_view text;
|
|
||||||
|
|
||||||
explicit constexpr Token() = default;
|
|
||||||
explicit constexpr Token(Kind kind, std::string_view text): kind(kind), text(text) {}
|
|
||||||
|
|
||||||
std::string describe() const {
|
|
||||||
switch (kind) {
|
|
||||||
case Kind::Text:
|
|
||||||
return "<text>";
|
|
||||||
case Kind::LineStatementClose:
|
|
||||||
return "<eol>";
|
|
||||||
case Kind::Eof:
|
|
||||||
return "<eof>";
|
|
||||||
default:
|
|
||||||
return static_cast<std::string>(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_TOKEN_HPP_
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
#ifndef INCLUDE_INJA_UTILS_HPP_
|
|
||||||
#define INCLUDE_INJA_UTILS_HPP_
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "exceptions.hpp"
|
|
||||||
|
|
||||||
namespace inja {
|
|
||||||
|
|
||||||
namespace string_view {
|
|
||||||
inline std::string_view slice(std::string_view view, size_t start, size_t end) {
|
|
||||||
start = std::min(start, view.size());
|
|
||||||
end = std::min(std::max(start, end), view.size());
|
|
||||||
return view.substr(start, end - start);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::pair<std::string_view, std::string_view> split(std::string_view view, char Separator) {
|
|
||||||
size_t idx = view.find(Separator);
|
|
||||||
if (idx == std::string_view::npos) {
|
|
||||||
return std::make_pair(view, std::string_view());
|
|
||||||
}
|
|
||||||
return std::make_pair(slice(view, 0, idx), slice(view, idx + 1, std::string_view::npos));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool starts_with(std::string_view view, std::string_view prefix) {
|
|
||||||
return (view.size() >= prefix.size() && view.compare(0, prefix.size(), prefix) == 0);
|
|
||||||
}
|
|
||||||
} // namespace string_view
|
|
||||||
|
|
||||||
inline SourceLocation get_source_location(std::string_view content, size_t pos) {
|
|
||||||
// Get line and offset position (starts at 1:1)
|
|
||||||
auto sliced = string_view::slice(content, 0, pos);
|
|
||||||
std::size_t last_newline = sliced.rfind("\n");
|
|
||||||
|
|
||||||
if (last_newline == std::string_view::npos) {
|
|
||||||
return {1, sliced.length() + 1};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count newlines
|
|
||||||
size_t count_lines = 0;
|
|
||||||
size_t search_start = 0;
|
|
||||||
while (search_start <= sliced.size()) {
|
|
||||||
search_start = sliced.find("\n", search_start) + 1;
|
|
||||||
if (search_start == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
count_lines += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {count_lines + 1, sliced.length() - last_newline};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void replace_substring(std::string& s, const std::string& f, const std::string& t) {
|
|
||||||
if (f.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (auto pos = s.find(f); // find first occurrence of f
|
|
||||||
pos != std::string::npos; // make sure f was found
|
|
||||||
s.replace(pos, f.size(), t), // replace with t, and
|
|
||||||
pos = s.find(f, pos + t.size())) // find next occurrence of f
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace inja
|
|
||||||
|
|
||||||
#endif // INCLUDE_INJA_UTILS_HPP_
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <nlohmann/detail/conversions/from_json.hpp>
|
|
||||||
#include <nlohmann/detail/conversions/to_json.hpp>
|
|
||||||
#include <nlohmann/detail/meta/identity_tag.hpp>
|
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
|
|
||||||
/// @sa https://json.nlohmann.me/api/adl_serializer/
|
|
||||||
template<typename ValueType, typename>
|
|
||||||
struct adl_serializer
|
|
||||||
{
|
|
||||||
/// @brief convert a JSON value to any value type
|
|
||||||
/// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
|
|
||||||
template<typename BasicJsonType, typename TargetType = ValueType>
|
|
||||||
static auto from_json(BasicJsonType && j, TargetType& val) noexcept(
|
|
||||||
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
|
|
||||||
-> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
|
|
||||||
{
|
|
||||||
::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief convert a JSON value to any value type
|
|
||||||
/// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
|
|
||||||
template<typename BasicJsonType, typename TargetType = ValueType>
|
|
||||||
static auto from_json(BasicJsonType && j) noexcept(
|
|
||||||
noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))
|
|
||||||
-> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))
|
|
||||||
{
|
|
||||||
return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief convert any value type to a JSON value
|
|
||||||
/// @sa https://json.nlohmann.me/api/adl_serializer/to_json/
|
|
||||||
template<typename BasicJsonType, typename TargetType = ValueType>
|
|
||||||
static auto to_json(BasicJsonType& j, TargetType && val) noexcept(
|
|
||||||
noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))
|
|
||||||
-> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())
|
|
||||||
{
|
|
||||||
::nlohmann::to_json(j, std::forward<TargetType>(val));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint> // uint8_t, uint64_t
|
|
||||||
#include <tuple> // tie
|
|
||||||
#include <utility> // move
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
|
|
||||||
/// @brief an internal type for a backed binary type
|
|
||||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/
|
|
||||||
template<typename BinaryType>
|
|
||||||
class byte_container_with_subtype : public BinaryType
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using container_type = BinaryType;
|
|
||||||
using subtype_type = std::uint64_t;
|
|
||||||
|
|
||||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
|
|
||||||
byte_container_with_subtype() noexcept(noexcept(container_type()))
|
|
||||||
: container_type()
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
|
|
||||||
byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))
|
|
||||||
: container_type(b)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
|
|
||||||
byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))
|
|
||||||
: container_type(std::move(b))
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
|
|
||||||
byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b)))
|
|
||||||
: container_type(b)
|
|
||||||
, m_subtype(subtype_)
|
|
||||||
, m_has_subtype(true)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
|
|
||||||
byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b))))
|
|
||||||
: container_type(std::move(b))
|
|
||||||
, m_subtype(subtype_)
|
|
||||||
, m_has_subtype(true)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool operator==(const byte_container_with_subtype& rhs) const
|
|
||||||
{
|
|
||||||
return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
|
|
||||||
std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const byte_container_with_subtype& rhs) const
|
|
||||||
{
|
|
||||||
return !(rhs == *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief sets the binary subtype
|
|
||||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/
|
|
||||||
void set_subtype(subtype_type subtype_) noexcept
|
|
||||||
{
|
|
||||||
m_subtype = subtype_;
|
|
||||||
m_has_subtype = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief return the binary subtype
|
|
||||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/
|
|
||||||
constexpr subtype_type subtype() const noexcept
|
|
||||||
{
|
|
||||||
return m_has_subtype ? m_subtype : static_cast<subtype_type>(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief return whether the value has a subtype
|
|
||||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/
|
|
||||||
constexpr bool has_subtype() const noexcept
|
|
||||||
{
|
|
||||||
return m_has_subtype;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief clears the binary subtype
|
|
||||||
/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/
|
|
||||||
void clear_subtype() noexcept
|
|
||||||
{
|
|
||||||
m_subtype = 0;
|
|
||||||
m_has_subtype = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
subtype_type m_subtype = 0;
|
|
||||||
bool m_has_subtype = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,492 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm> // transform
|
|
||||||
#include <array> // array
|
|
||||||
#include <forward_list> // forward_list
|
|
||||||
#include <iterator> // inserter, front_inserter, end
|
|
||||||
#include <map> // map
|
|
||||||
#include <string> // string
|
|
||||||
#include <tuple> // tuple, make_tuple
|
|
||||||
#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
|
|
||||||
#include <unordered_map> // unordered_map
|
|
||||||
#include <utility> // pair, declval
|
|
||||||
#include <valarray> // valarray
|
|
||||||
|
|
||||||
#include <nlohmann/detail/exceptions.hpp>
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
|
||||||
#include <nlohmann/detail/meta/identity_tag.hpp>
|
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
|
||||||
|
|
||||||
#if JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
#include <experimental/filesystem>
|
|
||||||
namespace nlohmann::detail
|
|
||||||
{
|
|
||||||
namespace std_fs = std::experimental::filesystem;
|
|
||||||
} // namespace nlohmann::detail
|
|
||||||
#elif JSON_HAS_FILESYSTEM
|
|
||||||
#include <filesystem>
|
|
||||||
namespace nlohmann::detail
|
|
||||||
{
|
|
||||||
namespace std_fs = std::filesystem;
|
|
||||||
} // namespace nlohmann::detail
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
n = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// overloads for basic_json template parameters
|
|
||||||
template < typename BasicJsonType, typename ArithmeticType,
|
|
||||||
enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
|
|
||||||
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
|
|
||||||
int > = 0 >
|
|
||||||
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
|
|
||||||
{
|
|
||||||
switch (static_cast<value_t>(j))
|
|
||||||
{
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
{
|
|
||||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case value_t::number_integer:
|
|
||||||
{
|
|
||||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case value_t::number_float:
|
|
||||||
{
|
|
||||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::object:
|
|
||||||
case value_t::array:
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <
|
|
||||||
typename BasicJsonType, typename ConstructibleStringType,
|
|
||||||
enable_if_t <
|
|
||||||
is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value&&
|
|
||||||
!std::is_same<typename BasicJsonType::string_t,
|
|
||||||
ConstructibleStringType>::value,
|
|
||||||
int > = 0 >
|
|
||||||
void from_json(const BasicJsonType& j, ConstructibleStringType& s)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
|
|
||||||
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
|
|
||||||
{
|
|
||||||
get_arithmetic_value(j, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
|
|
||||||
{
|
|
||||||
get_arithmetic_value(j, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
|
|
||||||
{
|
|
||||||
get_arithmetic_value(j, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename EnumType,
|
|
||||||
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
|
|
||||||
void from_json(const BasicJsonType& j, EnumType& e)
|
|
||||||
{
|
|
||||||
typename std::underlying_type<EnumType>::type val;
|
|
||||||
get_arithmetic_value(j, val);
|
|
||||||
e = static_cast<EnumType>(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// forward_list doesn't have an insert method
|
|
||||||
template<typename BasicJsonType, typename T, typename Allocator,
|
|
||||||
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
|
|
||||||
void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
l.clear();
|
|
||||||
std::transform(j.rbegin(), j.rend(),
|
|
||||||
std::front_inserter(l), [](const BasicJsonType & i)
|
|
||||||
{
|
|
||||||
return i.template get<T>();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// valarray doesn't have an insert method
|
|
||||||
template<typename BasicJsonType, typename T,
|
|
||||||
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
|
|
||||||
void from_json(const BasicJsonType& j, std::valarray<T>& l)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
l.resize(j.size());
|
|
||||||
std::transform(j.begin(), j.end(), std::begin(l),
|
|
||||||
[](const BasicJsonType & elem)
|
|
||||||
{
|
|
||||||
return elem.template get<T>();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename T, std::size_t N>
|
|
||||||
auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
||||||
-> decltype(j.template get<T>(), void())
|
|
||||||
{
|
|
||||||
for (std::size_t i = 0; i < N; ++i)
|
|
||||||
{
|
|
||||||
arr[i] = j.at(i).template get<T>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
|
|
||||||
{
|
|
||||||
arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename T, std::size_t N>
|
|
||||||
auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
|
|
||||||
priority_tag<2> /*unused*/)
|
|
||||||
-> decltype(j.template get<T>(), void())
|
|
||||||
{
|
|
||||||
for (std::size_t i = 0; i < N; ++i)
|
|
||||||
{
|
|
||||||
arr[i] = j.at(i).template get<T>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename ConstructibleArrayType,
|
|
||||||
enable_if_t<
|
|
||||||
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
|
|
||||||
int> = 0>
|
|
||||||
auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
|
|
||||||
-> decltype(
|
|
||||||
arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),
|
|
||||||
j.template get<typename ConstructibleArrayType::value_type>(),
|
|
||||||
void())
|
|
||||||
{
|
|
||||||
using std::end;
|
|
||||||
|
|
||||||
ConstructibleArrayType ret;
|
|
||||||
ret.reserve(j.size());
|
|
||||||
std::transform(j.begin(), j.end(),
|
|
||||||
std::inserter(ret, end(ret)), [](const BasicJsonType & i)
|
|
||||||
{
|
|
||||||
// get<BasicJsonType>() returns *this, this won't call a from_json
|
|
||||||
// method when value_type is BasicJsonType
|
|
||||||
return i.template get<typename ConstructibleArrayType::value_type>();
|
|
||||||
});
|
|
||||||
arr = std::move(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename ConstructibleArrayType,
|
|
||||||
enable_if_t<
|
|
||||||
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
|
|
||||||
int> = 0>
|
|
||||||
void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
|
|
||||||
priority_tag<0> /*unused*/)
|
|
||||||
{
|
|
||||||
using std::end;
|
|
||||||
|
|
||||||
ConstructibleArrayType ret;
|
|
||||||
std::transform(
|
|
||||||
j.begin(), j.end(), std::inserter(ret, end(ret)),
|
|
||||||
[](const BasicJsonType & i)
|
|
||||||
{
|
|
||||||
// get<BasicJsonType>() returns *this, this won't call a from_json
|
|
||||||
// method when value_type is BasicJsonType
|
|
||||||
return i.template get<typename ConstructibleArrayType::value_type>();
|
|
||||||
});
|
|
||||||
arr = std::move(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, typename ConstructibleArrayType,
|
|
||||||
enable_if_t <
|
|
||||||
is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
|
|
||||||
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
|
|
||||||
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
|
|
||||||
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
|
|
||||||
!is_basic_json<ConstructibleArrayType>::value,
|
|
||||||
int > = 0 >
|
|
||||||
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
|
|
||||||
-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),
|
|
||||||
j.template get<typename ConstructibleArrayType::value_type>(),
|
|
||||||
void())
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
|
|
||||||
from_json_array_impl(j, arr, priority_tag<3> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, typename T, std::size_t... Idx >
|
|
||||||
std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,
|
|
||||||
identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
|
|
||||||
{
|
|
||||||
return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, typename T, std::size_t N >
|
|
||||||
auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
|
|
||||||
-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
|
|
||||||
return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
|
|
||||||
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename ConstructibleObjectType,
|
|
||||||
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
|
||||||
void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstructibleObjectType ret;
|
|
||||||
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
|
|
||||||
using value_type = typename ConstructibleObjectType::value_type;
|
|
||||||
std::transform(
|
|
||||||
inner_object->begin(), inner_object->end(),
|
|
||||||
std::inserter(ret, ret.begin()),
|
|
||||||
[](typename BasicJsonType::object_t::value_type const & p)
|
|
||||||
{
|
|
||||||
return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
|
|
||||||
});
|
|
||||||
obj = std::move(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
// overload for arithmetic types, not chosen for basic_json template arguments
|
|
||||||
// (BooleanType, etc..); note: Is it really necessary to provide explicit
|
|
||||||
// overloads for boolean_t etc. in case of a custom BooleanType which is not
|
|
||||||
// an arithmetic type?
|
|
||||||
template < typename BasicJsonType, typename ArithmeticType,
|
|
||||||
enable_if_t <
|
|
||||||
std::is_arithmetic<ArithmeticType>::value&&
|
|
||||||
!std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
|
|
||||||
!std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
|
|
||||||
!std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
|
|
||||||
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
|
|
||||||
int > = 0 >
|
|
||||||
void from_json(const BasicJsonType& j, ArithmeticType& val)
|
|
||||||
{
|
|
||||||
switch (static_cast<value_t>(j))
|
|
||||||
{
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
{
|
|
||||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case value_t::number_integer:
|
|
||||||
{
|
|
||||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case value_t::number_float:
|
|
||||||
{
|
|
||||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case value_t::boolean:
|
|
||||||
{
|
|
||||||
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::object:
|
|
||||||
case value_t::array:
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename... Args, std::size_t... Idx>
|
|
||||||
std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
|
|
||||||
{
|
|
||||||
return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, class A1, class A2 >
|
|
||||||
std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
|
|
||||||
{
|
|
||||||
return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),
|
|
||||||
std::forward<BasicJsonType>(j).at(1).template get<A2>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename A1, typename A2>
|
|
||||||
void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
|
|
||||||
{
|
|
||||||
p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename... Args>
|
|
||||||
std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
|
|
||||||
{
|
|
||||||
return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename... Args>
|
|
||||||
void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
|
|
||||||
{
|
|
||||||
t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename TupleRelated>
|
|
||||||
auto from_json(BasicJsonType&& j, TupleRelated&& t)
|
|
||||||
-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
|
|
||||||
return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
|
|
||||||
typename = enable_if_t < !std::is_constructible <
|
|
||||||
typename BasicJsonType::string_t, Key >::value >>
|
|
||||||
void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
m.clear();
|
|
||||||
for (const auto& p : j)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j));
|
|
||||||
}
|
|
||||||
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
|
|
||||||
typename = enable_if_t < !std::is_constructible <
|
|
||||||
typename BasicJsonType::string_t, Key >::value >>
|
|
||||||
void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
m.clear();
|
|
||||||
for (const auto& p : j)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j));
|
|
||||||
}
|
|
||||||
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void from_json(const BasicJsonType& j, std_fs::path& p)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
|
||||||
{
|
|
||||||
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j));
|
|
||||||
}
|
|
||||||
p = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct from_json_fn
|
|
||||||
{
|
|
||||||
template<typename BasicJsonType, typename T>
|
|
||||||
auto operator()(const BasicJsonType& j, T&& val) const
|
|
||||||
noexcept(noexcept(from_json(j, std::forward<T>(val))))
|
|
||||||
-> decltype(from_json(j, std::forward<T>(val)))
|
|
||||||
{
|
|
||||||
return from_json(j, std::forward<T>(val));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
/// namespace to hold default `from_json` function
|
|
||||||
/// to see why this is required:
|
|
||||||
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
|
|
||||||
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
|
|
||||||
{
|
|
||||||
constexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value; // NOLINT(misc-definitions-in-headers)
|
|
||||||
} // namespace
|
|
||||||
} // namespace nlohmann
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,430 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm> // copy
|
|
||||||
#include <iterator> // begin, end
|
|
||||||
#include <string> // string
|
|
||||||
#include <tuple> // tuple, get
|
|
||||||
#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
|
|
||||||
#include <utility> // move, forward, declval, pair
|
|
||||||
#include <valarray> // valarray
|
|
||||||
#include <vector> // vector
|
|
||||||
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
#include <nlohmann/detail/iterators/iteration_proxy.hpp>
|
|
||||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
|
||||||
|
|
||||||
#if JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
#include <experimental/filesystem>
|
|
||||||
namespace nlohmann::detail
|
|
||||||
{
|
|
||||||
namespace std_fs = std::experimental::filesystem;
|
|
||||||
} // namespace nlohmann::detail
|
|
||||||
#elif JSON_HAS_FILESYSTEM
|
|
||||||
#include <filesystem>
|
|
||||||
namespace nlohmann::detail
|
|
||||||
{
|
|
||||||
namespace std_fs = std::filesystem;
|
|
||||||
} // namespace nlohmann::detail
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
//////////////////
|
|
||||||
// constructors //
|
|
||||||
//////////////////
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Note all external_constructor<>::construct functions need to call
|
|
||||||
* j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an
|
|
||||||
* allocated value (e.g., a string). See bug issue
|
|
||||||
* https://github.com/nlohmann/json/issues/2865 for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
template<value_t> struct external_constructor;
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct external_constructor<value_t::boolean>
|
|
||||||
{
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::boolean;
|
|
||||||
j.m_value = b;
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct external_constructor<value_t::string>
|
|
||||||
{
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::string;
|
|
||||||
j.m_value = s;
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::string;
|
|
||||||
j.m_value = std::move(s);
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, typename CompatibleStringType,
|
|
||||||
enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
|
|
||||||
int > = 0 >
|
|
||||||
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::string;
|
|
||||||
j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct external_constructor<value_t::binary>
|
|
||||||
{
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::binary;
|
|
||||||
j.m_value = typename BasicJsonType::binary_t(b);
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::binary;
|
|
||||||
j.m_value = typename BasicJsonType::binary_t(std::move(b));
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct external_constructor<value_t::number_float>
|
|
||||||
{
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::number_float;
|
|
||||||
j.m_value = val;
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct external_constructor<value_t::number_unsigned>
|
|
||||||
{
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::number_unsigned;
|
|
||||||
j.m_value = val;
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct external_constructor<value_t::number_integer>
|
|
||||||
{
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::number_integer;
|
|
||||||
j.m_value = val;
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct external_constructor<value_t::array>
|
|
||||||
{
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::array;
|
|
||||||
j.m_value = arr;
|
|
||||||
j.set_parents();
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::array;
|
|
||||||
j.m_value = std::move(arr);
|
|
||||||
j.set_parents();
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, typename CompatibleArrayType,
|
|
||||||
enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
|
|
||||||
int > = 0 >
|
|
||||||
static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
|
|
||||||
{
|
|
||||||
using std::begin;
|
|
||||||
using std::end;
|
|
||||||
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::array;
|
|
||||||
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
|
||||||
j.set_parents();
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::array;
|
|
||||||
j.m_value = value_t::array;
|
|
||||||
j.m_value.array->reserve(arr.size());
|
|
||||||
for (const bool x : arr)
|
|
||||||
{
|
|
||||||
j.m_value.array->push_back(x);
|
|
||||||
j.set_parent(j.m_value.array->back());
|
|
||||||
}
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename T,
|
|
||||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
|
||||||
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::array;
|
|
||||||
j.m_value = value_t::array;
|
|
||||||
j.m_value.array->resize(arr.size());
|
|
||||||
if (arr.size() > 0)
|
|
||||||
{
|
|
||||||
std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
|
|
||||||
}
|
|
||||||
j.set_parents();
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct external_constructor<value_t::object>
|
|
||||||
{
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::object;
|
|
||||||
j.m_value = obj;
|
|
||||||
j.set_parents();
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
|
||||||
{
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::object;
|
|
||||||
j.m_value = std::move(obj);
|
|
||||||
j.set_parents();
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, typename CompatibleObjectType,
|
|
||||||
enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
|
|
||||||
static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
|
|
||||||
{
|
|
||||||
using std::begin;
|
|
||||||
using std::end;
|
|
||||||
|
|
||||||
j.m_value.destroy(j.m_type);
|
|
||||||
j.m_type = value_t::object;
|
|
||||||
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
|
||||||
j.set_parents();
|
|
||||||
j.assert_invariant();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/////////////
|
|
||||||
// to_json //
|
|
||||||
/////////////
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename T,
|
|
||||||
enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
|
|
||||||
void to_json(BasicJsonType& j, T b) noexcept
|
|
||||||
{
|
|
||||||
external_constructor<value_t::boolean>::construct(j, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleString,
|
|
||||||
enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
|
|
||||||
void to_json(BasicJsonType& j, const CompatibleString& s)
|
|
||||||
{
|
|
||||||
external_constructor<value_t::string>::construct(j, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
|
||||||
{
|
|
||||||
external_constructor<value_t::string>::construct(j, std::move(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename FloatType,
|
|
||||||
enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
|
|
||||||
void to_json(BasicJsonType& j, FloatType val) noexcept
|
|
||||||
{
|
|
||||||
external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
|
|
||||||
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
|
|
||||||
void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
|
|
||||||
{
|
|
||||||
external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleNumberIntegerType,
|
|
||||||
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
|
|
||||||
void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
|
|
||||||
{
|
|
||||||
external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename EnumType,
|
|
||||||
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
|
|
||||||
void to_json(BasicJsonType& j, EnumType e) noexcept
|
|
||||||
{
|
|
||||||
using underlying_type = typename std::underlying_type<EnumType>::type;
|
|
||||||
external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void to_json(BasicJsonType& j, const std::vector<bool>& e)
|
|
||||||
{
|
|
||||||
external_constructor<value_t::array>::construct(j, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, typename CompatibleArrayType,
|
|
||||||
enable_if_t < is_compatible_array_type<BasicJsonType,
|
|
||||||
CompatibleArrayType>::value&&
|
|
||||||
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
|
|
||||||
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
|
|
||||||
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
|
|
||||||
!is_basic_json<CompatibleArrayType>::value,
|
|
||||||
int > = 0 >
|
|
||||||
void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
|
|
||||||
{
|
|
||||||
external_constructor<value_t::array>::construct(j, arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
|
|
||||||
{
|
|
||||||
external_constructor<value_t::binary>::construct(j, bin);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename T,
|
|
||||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
|
||||||
void to_json(BasicJsonType& j, const std::valarray<T>& arr)
|
|
||||||
{
|
|
||||||
external_constructor<value_t::array>::construct(j, std::move(arr));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
|
||||||
{
|
|
||||||
external_constructor<value_t::array>::construct(j, std::move(arr));
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, typename CompatibleObjectType,
|
|
||||||
enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
|
|
||||||
void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
|
|
||||||
{
|
|
||||||
external_constructor<value_t::object>::construct(j, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
|
||||||
{
|
|
||||||
external_constructor<value_t::object>::construct(j, std::move(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <
|
|
||||||
typename BasicJsonType, typename T, std::size_t N,
|
|
||||||
enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
|
|
||||||
const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
||||||
int > = 0 >
|
|
||||||
void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
||||||
{
|
|
||||||
external_constructor<value_t::array>::construct(j, arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >
|
|
||||||
void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
|
|
||||||
{
|
|
||||||
j = { p.first, p.second };
|
|
||||||
}
|
|
||||||
|
|
||||||
// for https://github.com/nlohmann/json/pull/1134
|
|
||||||
template<typename BasicJsonType, typename T,
|
|
||||||
enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
|
|
||||||
void to_json(BasicJsonType& j, const T& b)
|
|
||||||
{
|
|
||||||
j = { {b.key(), b.value()} };
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
|
|
||||||
void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
|
|
||||||
{
|
|
||||||
j = { std::get<Idx>(t)... };
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
|
|
||||||
void to_json(BasicJsonType& j, const T& t)
|
|
||||||
{
|
|
||||||
to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
void to_json(BasicJsonType& j, const std_fs::path& p)
|
|
||||||
{
|
|
||||||
j = p.string();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct to_json_fn
|
|
||||||
{
|
|
||||||
template<typename BasicJsonType, typename T>
|
|
||||||
auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
|
|
||||||
-> decltype(to_json(j, std::forward<T>(val)), void())
|
|
||||||
{
|
|
||||||
return to_json(j, std::forward<T>(val));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
/// namespace to hold default `to_json` function
|
|
||||||
/// to see why this is required:
|
|
||||||
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
|
|
||||||
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
|
|
||||||
{
|
|
||||||
constexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value; // NOLINT(misc-definitions-in-headers)
|
|
||||||
} // namespace
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,236 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <exception> // exception
|
|
||||||
#include <stdexcept> // runtime_error
|
|
||||||
#include <string> // to_string
|
|
||||||
#include <vector> // vector
|
|
||||||
|
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
|
||||||
#include <nlohmann/detail/string_escape.hpp>
|
|
||||||
#include <nlohmann/detail/input/position_t.hpp>
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
////////////////
|
|
||||||
// exceptions //
|
|
||||||
////////////////
|
|
||||||
|
|
||||||
/// @brief general exception of the @ref basic_json class
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/exception/
|
|
||||||
class exception : public std::exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// returns the explanatory string
|
|
||||||
const char* what() const noexcept override
|
|
||||||
{
|
|
||||||
return m.what();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the id of the exception
|
|
||||||
const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
|
|
||||||
|
|
||||||
protected:
|
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
|
||||||
exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)
|
|
||||||
|
|
||||||
static std::string name(const std::string& ename, int id_)
|
|
||||||
{
|
|
||||||
return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static std::string diagnostics(const BasicJsonType& leaf_element)
|
|
||||||
{
|
|
||||||
#if JSON_DIAGNOSTICS
|
|
||||||
std::vector<std::string> tokens;
|
|
||||||
for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent)
|
|
||||||
{
|
|
||||||
switch (current->m_parent->type())
|
|
||||||
{
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i)
|
|
||||||
{
|
|
||||||
if (¤t->m_parent->m_value.array->operator[](i) == current)
|
|
||||||
{
|
|
||||||
tokens.emplace_back(std::to_string(i));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::object:
|
|
||||||
{
|
|
||||||
for (const auto& element : *current->m_parent->m_value.object)
|
|
||||||
{
|
|
||||||
if (&element.second == current)
|
|
||||||
{
|
|
||||||
tokens.emplace_back(element.first.c_str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null: // LCOV_EXCL_LINE
|
|
||||||
case value_t::string: // LCOV_EXCL_LINE
|
|
||||||
case value_t::boolean: // LCOV_EXCL_LINE
|
|
||||||
case value_t::number_integer: // LCOV_EXCL_LINE
|
|
||||||
case value_t::number_unsigned: // LCOV_EXCL_LINE
|
|
||||||
case value_t::number_float: // LCOV_EXCL_LINE
|
|
||||||
case value_t::binary: // LCOV_EXCL_LINE
|
|
||||||
case value_t::discarded: // LCOV_EXCL_LINE
|
|
||||||
default: // LCOV_EXCL_LINE
|
|
||||||
break; // LCOV_EXCL_LINE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tokens.empty())
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
|
|
||||||
[](const std::string & a, const std::string & b)
|
|
||||||
{
|
|
||||||
return a + "/" + detail::escape(b);
|
|
||||||
}) + ") ";
|
|
||||||
#else
|
|
||||||
static_cast<void>(leaf_element);
|
|
||||||
return "";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// an exception object as storage for error messages
|
|
||||||
std::runtime_error m;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief exception indicating a parse error
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/parse_error/
|
|
||||||
class parse_error : public exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
@brief create a parse error exception
|
|
||||||
@param[in] id_ the id of the exception
|
|
||||||
@param[in] pos the position where the error occurred (or with
|
|
||||||
chars_read_total=0 if the position cannot be
|
|
||||||
determined)
|
|
||||||
@param[in] what_arg the explanatory string
|
|
||||||
@return parse_error object
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context)
|
|
||||||
{
|
|
||||||
std::string w = exception::name("parse_error", id_) + "parse error" +
|
|
||||||
position_string(pos) + ": " + exception::diagnostics(context) + what_arg;
|
|
||||||
return {id_, pos.chars_read_total, w.c_str()};
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context)
|
|
||||||
{
|
|
||||||
std::string w = exception::name("parse_error", id_) + "parse error" +
|
|
||||||
(byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") +
|
|
||||||
": " + exception::diagnostics(context) + what_arg;
|
|
||||||
return {id_, byte_, w.c_str()};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief byte index of the parse error
|
|
||||||
|
|
||||||
The byte index of the last read character in the input file.
|
|
||||||
|
|
||||||
@note For an input with n bytes, 1 is the index of the first character and
|
|
||||||
n+1 is the index of the terminating null byte or the end of file.
|
|
||||||
This also holds true when reading a byte vector (CBOR or MessagePack).
|
|
||||||
*/
|
|
||||||
const std::size_t byte;
|
|
||||||
|
|
||||||
private:
|
|
||||||
parse_error(int id_, std::size_t byte_, const char* what_arg)
|
|
||||||
: exception(id_, what_arg), byte(byte_) {}
|
|
||||||
|
|
||||||
static std::string position_string(const position_t& pos)
|
|
||||||
{
|
|
||||||
return " at line " + std::to_string(pos.lines_read + 1) +
|
|
||||||
", column " + std::to_string(pos.chars_read_current_line);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief exception indicating errors with iterators
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/
|
|
||||||
class invalid_iterator : public exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context)
|
|
||||||
{
|
|
||||||
std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg;
|
|
||||||
return {id_, w.c_str()};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
|
||||||
invalid_iterator(int id_, const char* what_arg)
|
|
||||||
: exception(id_, what_arg) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief exception indicating executing a member function with a wrong type
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/type_error/
|
|
||||||
class type_error : public exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
|
|
||||||
{
|
|
||||||
std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg;
|
|
||||||
return {id_, w.c_str()};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
|
||||||
type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief exception indicating access out of the defined range
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/
|
|
||||||
class out_of_range : public exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context)
|
|
||||||
{
|
|
||||||
std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg;
|
|
||||||
return {id_, w.c_str()};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
|
||||||
out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief exception indicating other library errors
|
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/other_error/
|
|
||||||
class other_error : public exception
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
|
|
||||||
{
|
|
||||||
std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg;
|
|
||||||
return {id_, w.c_str()};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
|
||||||
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint> // uint8_t
|
|
||||||
#include <cstddef> // size_t
|
|
||||||
#include <functional> // hash
|
|
||||||
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
|
|
||||||
// boost::hash_combine
|
|
||||||
inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
|
|
||||||
{
|
|
||||||
seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief hash a JSON value
|
|
||||||
|
|
||||||
The hash function tries to rely on std::hash where possible. Furthermore, the
|
|
||||||
type of the JSON value is taken into account to have different hash values for
|
|
||||||
null, 0, 0U, and false, etc.
|
|
||||||
|
|
||||||
@tparam BasicJsonType basic_json specialization
|
|
||||||
@param j JSON value to hash
|
|
||||||
@return hash value of j
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
std::size_t hash(const BasicJsonType& j)
|
|
||||||
{
|
|
||||||
using string_t = typename BasicJsonType::string_t;
|
|
||||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
|
||||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
|
||||||
using number_float_t = typename BasicJsonType::number_float_t;
|
|
||||||
|
|
||||||
const auto type = static_cast<std::size_t>(j.type());
|
|
||||||
switch (j.type())
|
|
||||||
{
|
|
||||||
case BasicJsonType::value_t::null:
|
|
||||||
case BasicJsonType::value_t::discarded:
|
|
||||||
{
|
|
||||||
return combine(type, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
case BasicJsonType::value_t::object:
|
|
||||||
{
|
|
||||||
auto seed = combine(type, j.size());
|
|
||||||
for (const auto& element : j.items())
|
|
||||||
{
|
|
||||||
const auto h = std::hash<string_t> {}(element.key());
|
|
||||||
seed = combine(seed, h);
|
|
||||||
seed = combine(seed, hash(element.value()));
|
|
||||||
}
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BasicJsonType::value_t::array:
|
|
||||||
{
|
|
||||||
auto seed = combine(type, j.size());
|
|
||||||
for (const auto& element : j)
|
|
||||||
{
|
|
||||||
seed = combine(seed, hash(element));
|
|
||||||
}
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
case BasicJsonType::value_t::string:
|
|
||||||
{
|
|
||||||
const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());
|
|
||||||
return combine(type, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
case BasicJsonType::value_t::boolean:
|
|
||||||
{
|
|
||||||
const auto h = std::hash<bool> {}(j.template get<bool>());
|
|
||||||
return combine(type, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
case BasicJsonType::value_t::number_integer:
|
|
||||||
{
|
|
||||||
const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());
|
|
||||||
return combine(type, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
case BasicJsonType::value_t::number_unsigned:
|
|
||||||
{
|
|
||||||
const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
|
|
||||||
return combine(type, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
case BasicJsonType::value_t::number_float:
|
|
||||||
{
|
|
||||||
const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
|
|
||||||
return combine(type, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
case BasicJsonType::value_t::binary:
|
|
||||||
{
|
|
||||||
auto seed = combine(type, j.get_binary().size());
|
|
||||||
const auto h = std::hash<bool> {}(j.get_binary().has_subtype());
|
|
||||||
seed = combine(seed, h);
|
|
||||||
seed = combine(seed, static_cast<std::size_t>(j.get_binary().subtype()));
|
|
||||||
for (const auto byte : j.get_binary())
|
|
||||||
{
|
|
||||||
seed = combine(seed, std::hash<std::uint8_t> {}(byte));
|
|
||||||
}
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: // LCOV_EXCL_LINE
|
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
|
||||||
return 0; // LCOV_EXCL_LINE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace nlohmann
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,483 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array> // array
|
|
||||||
#include <cstddef> // size_t
|
|
||||||
#include <cstring> // strlen
|
|
||||||
#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
|
|
||||||
#include <memory> // shared_ptr, make_shared, addressof
|
|
||||||
#include <numeric> // accumulate
|
|
||||||
#include <string> // string, char_traits
|
|
||||||
#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
|
|
||||||
#include <utility> // pair, declval
|
|
||||||
|
|
||||||
#ifndef JSON_NO_IO
|
|
||||||
#include <cstdio> // FILE *
|
|
||||||
#include <istream> // istream
|
|
||||||
#endif // JSON_NO_IO
|
|
||||||
|
|
||||||
#include <nlohmann/detail/iterators/iterator_traits.hpp>
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
/// the supported input formats
|
|
||||||
enum class input_format_t { json, cbor, msgpack, ubjson, bson };
|
|
||||||
|
|
||||||
////////////////////
|
|
||||||
// input adapters //
|
|
||||||
////////////////////
|
|
||||||
|
|
||||||
#ifndef JSON_NO_IO
|
|
||||||
/*!
|
|
||||||
Input adapter for stdio file access. This adapter read only 1 byte and do not use any
|
|
||||||
buffer. This adapter is a very low level adapter.
|
|
||||||
*/
|
|
||||||
class file_input_adapter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using char_type = char;
|
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
explicit file_input_adapter(std::FILE* f) noexcept
|
|
||||||
: m_file(f)
|
|
||||||
{}
|
|
||||||
|
|
||||||
// make class move-only
|
|
||||||
file_input_adapter(const file_input_adapter&) = delete;
|
|
||||||
file_input_adapter(file_input_adapter&&) noexcept = default;
|
|
||||||
file_input_adapter& operator=(const file_input_adapter&) = delete;
|
|
||||||
file_input_adapter& operator=(file_input_adapter&&) = delete;
|
|
||||||
~file_input_adapter() = default;
|
|
||||||
|
|
||||||
std::char_traits<char>::int_type get_character() noexcept
|
|
||||||
{
|
|
||||||
return std::fgetc(m_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// the file pointer to read from
|
|
||||||
std::FILE* m_file;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
|
|
||||||
beginning of input. Does not support changing the underlying std::streambuf
|
|
||||||
in mid-input. Maintains underlying std::istream and std::streambuf to support
|
|
||||||
subsequent use of standard std::istream operations to process any input
|
|
||||||
characters following those used in parsing the JSON input. Clears the
|
|
||||||
std::istream flags; any input errors (e.g., EOF) will be detected by the first
|
|
||||||
subsequent call for input from the std::istream.
|
|
||||||
*/
|
|
||||||
class input_stream_adapter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using char_type = char;
|
|
||||||
|
|
||||||
~input_stream_adapter()
|
|
||||||
{
|
|
||||||
// clear stream flags; we use underlying streambuf I/O, do not
|
|
||||||
// maintain ifstream flags, except eof
|
|
||||||
if (is != nullptr)
|
|
||||||
{
|
|
||||||
is->clear(is->rdstate() & std::ios::eofbit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit input_stream_adapter(std::istream& i)
|
|
||||||
: is(&i), sb(i.rdbuf())
|
|
||||||
{}
|
|
||||||
|
|
||||||
// delete because of pointer members
|
|
||||||
input_stream_adapter(const input_stream_adapter&) = delete;
|
|
||||||
input_stream_adapter& operator=(input_stream_adapter&) = delete;
|
|
||||||
input_stream_adapter& operator=(input_stream_adapter&&) = delete;
|
|
||||||
|
|
||||||
input_stream_adapter(input_stream_adapter&& rhs) noexcept
|
|
||||||
: is(rhs.is), sb(rhs.sb)
|
|
||||||
{
|
|
||||||
rhs.is = nullptr;
|
|
||||||
rhs.sb = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
|
|
||||||
// ensure that std::char_traits<char>::eof() and the character 0xFF do not
|
|
||||||
// end up as the same value, e.g. 0xFFFFFFFF.
|
|
||||||
std::char_traits<char>::int_type get_character()
|
|
||||||
{
|
|
||||||
auto res = sb->sbumpc();
|
|
||||||
// set eof manually, as we don't use the istream interface.
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
|
|
||||||
{
|
|
||||||
is->clear(is->rdstate() | std::ios::eofbit);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// the associated input stream
|
|
||||||
std::istream* is = nullptr;
|
|
||||||
std::streambuf* sb = nullptr;
|
|
||||||
};
|
|
||||||
#endif // JSON_NO_IO
|
|
||||||
|
|
||||||
// General-purpose iterator-based adapter. It might not be as fast as
|
|
||||||
// theoretically possible for some containers, but it is extremely versatile.
|
|
||||||
template<typename IteratorType>
|
|
||||||
class iterator_input_adapter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using char_type = typename std::iterator_traits<IteratorType>::value_type;
|
|
||||||
|
|
||||||
iterator_input_adapter(IteratorType first, IteratorType last)
|
|
||||||
: current(std::move(first)), end(std::move(last))
|
|
||||||
{}
|
|
||||||
|
|
||||||
typename std::char_traits<char_type>::int_type get_character()
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_LIKELY(current != end))
|
|
||||||
{
|
|
||||||
auto result = std::char_traits<char_type>::to_int_type(*current);
|
|
||||||
std::advance(current, 1);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::char_traits<char_type>::eof();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
IteratorType current;
|
|
||||||
IteratorType end;
|
|
||||||
|
|
||||||
template<typename BaseInputAdapter, size_t T>
|
|
||||||
friend struct wide_string_input_helper;
|
|
||||||
|
|
||||||
bool empty() const
|
|
||||||
{
|
|
||||||
return current == end;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<typename BaseInputAdapter, size_t T>
|
|
||||||
struct wide_string_input_helper;
|
|
||||||
|
|
||||||
template<typename BaseInputAdapter>
|
|
||||||
struct wide_string_input_helper<BaseInputAdapter, 4>
|
|
||||||
{
|
|
||||||
// UTF-32
|
|
||||||
static void fill_buffer(BaseInputAdapter& input,
|
|
||||||
std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
|
|
||||||
size_t& utf8_bytes_index,
|
|
||||||
size_t& utf8_bytes_filled)
|
|
||||||
{
|
|
||||||
utf8_bytes_index = 0;
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(input.empty()))
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = std::char_traits<char>::eof();
|
|
||||||
utf8_bytes_filled = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// get the current character
|
|
||||||
const auto wc = input.get_character();
|
|
||||||
|
|
||||||
// UTF-32 to UTF-8 encoding
|
|
||||||
if (wc < 0x80)
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
|
||||||
utf8_bytes_filled = 1;
|
|
||||||
}
|
|
||||||
else if (wc <= 0x7FF)
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));
|
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
|
||||||
utf8_bytes_filled = 2;
|
|
||||||
}
|
|
||||||
else if (wc <= 0xFFFF)
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));
|
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
|
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
|
||||||
utf8_bytes_filled = 3;
|
|
||||||
}
|
|
||||||
else if (wc <= 0x10FFFF)
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));
|
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));
|
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
|
|
||||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
|
||||||
utf8_bytes_filled = 4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// unknown character
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
|
||||||
utf8_bytes_filled = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename BaseInputAdapter>
|
|
||||||
struct wide_string_input_helper<BaseInputAdapter, 2>
|
|
||||||
{
|
|
||||||
// UTF-16
|
|
||||||
static void fill_buffer(BaseInputAdapter& input,
|
|
||||||
std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
|
|
||||||
size_t& utf8_bytes_index,
|
|
||||||
size_t& utf8_bytes_filled)
|
|
||||||
{
|
|
||||||
utf8_bytes_index = 0;
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(input.empty()))
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = std::char_traits<char>::eof();
|
|
||||||
utf8_bytes_filled = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// get the current character
|
|
||||||
const auto wc = input.get_character();
|
|
||||||
|
|
||||||
// UTF-16 to UTF-8 encoding
|
|
||||||
if (wc < 0x80)
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
|
||||||
utf8_bytes_filled = 1;
|
|
||||||
}
|
|
||||||
else if (wc <= 0x7FF)
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));
|
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
|
||||||
utf8_bytes_filled = 2;
|
|
||||||
}
|
|
||||||
else if (0xD800 > wc || wc >= 0xE000)
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));
|
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
|
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
|
|
||||||
utf8_bytes_filled = 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!input.empty()))
|
|
||||||
{
|
|
||||||
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
|
||||||
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
|
||||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
|
||||||
utf8_bytes_filled = 4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
|
||||||
utf8_bytes_filled = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Wraps another input apdater to convert wide character types into individual bytes.
|
|
||||||
template<typename BaseInputAdapter, typename WideCharType>
|
|
||||||
class wide_string_input_adapter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using char_type = char;
|
|
||||||
|
|
||||||
wide_string_input_adapter(BaseInputAdapter base)
|
|
||||||
: base_adapter(base) {}
|
|
||||||
|
|
||||||
typename std::char_traits<char>::int_type get_character() noexcept
|
|
||||||
{
|
|
||||||
// check if buffer needs to be filled
|
|
||||||
if (utf8_bytes_index == utf8_bytes_filled)
|
|
||||||
{
|
|
||||||
fill_buffer<sizeof(WideCharType)>();
|
|
||||||
|
|
||||||
JSON_ASSERT(utf8_bytes_filled > 0);
|
|
||||||
JSON_ASSERT(utf8_bytes_index == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// use buffer
|
|
||||||
JSON_ASSERT(utf8_bytes_filled > 0);
|
|
||||||
JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);
|
|
||||||
return utf8_bytes[utf8_bytes_index++];
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
BaseInputAdapter base_adapter;
|
|
||||||
|
|
||||||
template<size_t T>
|
|
||||||
void fill_buffer()
|
|
||||||
{
|
|
||||||
wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// a buffer for UTF-8 bytes
|
|
||||||
std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
|
|
||||||
|
|
||||||
/// index to the utf8_codes array for the next valid byte
|
|
||||||
std::size_t utf8_bytes_index = 0;
|
|
||||||
/// number of valid bytes in the utf8_codes array
|
|
||||||
std::size_t utf8_bytes_filled = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<typename IteratorType, typename Enable = void>
|
|
||||||
struct iterator_input_adapter_factory
|
|
||||||
{
|
|
||||||
using iterator_type = IteratorType;
|
|
||||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
|
||||||
using adapter_type = iterator_input_adapter<iterator_type>;
|
|
||||||
|
|
||||||
static adapter_type create(IteratorType first, IteratorType last)
|
|
||||||
{
|
|
||||||
return adapter_type(std::move(first), std::move(last));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct is_iterator_of_multibyte
|
|
||||||
{
|
|
||||||
using value_type = typename std::iterator_traits<T>::value_type;
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
value = sizeof(value_type) > 1
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename IteratorType>
|
|
||||||
struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
|
|
||||||
{
|
|
||||||
using iterator_type = IteratorType;
|
|
||||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
|
||||||
using base_adapter_type = iterator_input_adapter<iterator_type>;
|
|
||||||
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
|
|
||||||
|
|
||||||
static adapter_type create(IteratorType first, IteratorType last)
|
|
||||||
{
|
|
||||||
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// General purpose iterator-based input
|
|
||||||
template<typename IteratorType>
|
|
||||||
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
|
|
||||||
{
|
|
||||||
using factory_type = iterator_input_adapter_factory<IteratorType>;
|
|
||||||
return factory_type::create(first, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convenience shorthand from container to iterator
|
|
||||||
// Enables ADL on begin(container) and end(container)
|
|
||||||
// Encloses the using declarations in namespace for not to leak them to outside scope
|
|
||||||
|
|
||||||
namespace container_input_adapter_factory_impl
|
|
||||||
{
|
|
||||||
|
|
||||||
using std::begin;
|
|
||||||
using std::end;
|
|
||||||
|
|
||||||
template<typename ContainerType, typename Enable = void>
|
|
||||||
struct container_input_adapter_factory {};
|
|
||||||
|
|
||||||
template<typename ContainerType>
|
|
||||||
struct container_input_adapter_factory< ContainerType,
|
|
||||||
void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>>
|
|
||||||
{
|
|
||||||
using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));
|
|
||||||
|
|
||||||
static adapter_type create(const ContainerType& container)
|
|
||||||
{
|
|
||||||
return input_adapter(begin(container), end(container));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace container_input_adapter_factory_impl
|
|
||||||
|
|
||||||
template<typename ContainerType>
|
|
||||||
typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)
|
|
||||||
{
|
|
||||||
return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef JSON_NO_IO
|
|
||||||
// Special cases with fast paths
|
|
||||||
inline file_input_adapter input_adapter(std::FILE* file)
|
|
||||||
{
|
|
||||||
return file_input_adapter(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline input_stream_adapter input_adapter(std::istream& stream)
|
|
||||||
{
|
|
||||||
return input_stream_adapter(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline input_stream_adapter input_adapter(std::istream&& stream)
|
|
||||||
{
|
|
||||||
return input_stream_adapter(stream);
|
|
||||||
}
|
|
||||||
#endif // JSON_NO_IO
|
|
||||||
|
|
||||||
using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));
|
|
||||||
|
|
||||||
// Null-delimited strings, and the like.
|
|
||||||
template < typename CharT,
|
|
||||||
typename std::enable_if <
|
|
||||||
std::is_pointer<CharT>::value&&
|
|
||||||
!std::is_array<CharT>::value&&
|
|
||||||
std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
|
|
||||||
sizeof(typename std::remove_pointer<CharT>::type) == 1,
|
|
||||||
int >::type = 0 >
|
|
||||||
contiguous_bytes_input_adapter input_adapter(CharT b)
|
|
||||||
{
|
|
||||||
auto length = std::strlen(reinterpret_cast<const char*>(b));
|
|
||||||
const auto* ptr = reinterpret_cast<const char*>(b);
|
|
||||||
return input_adapter(ptr, ptr + length);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, std::size_t N>
|
|
||||||
auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
||||||
{
|
|
||||||
return input_adapter(array, array + N);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This class only handles inputs of input_buffer_adapter type.
|
|
||||||
// It's required so that expressions like {ptr, len} can be implicitly cast
|
|
||||||
// to the correct adapter.
|
|
||||||
class span_input_adapter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
template < typename CharT,
|
|
||||||
typename std::enable_if <
|
|
||||||
std::is_pointer<CharT>::value&&
|
|
||||||
std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
|
|
||||||
sizeof(typename std::remove_pointer<CharT>::type) == 1,
|
|
||||||
int >::type = 0 >
|
|
||||||
span_input_adapter(CharT b, std::size_t l)
|
|
||||||
: ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}
|
|
||||||
|
|
||||||
template<class IteratorType,
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
|
|
||||||
int>::type = 0>
|
|
||||||
span_input_adapter(IteratorType first, IteratorType last)
|
|
||||||
: ia(input_adapter(first, last)) {}
|
|
||||||
|
|
||||||
contiguous_bytes_input_adapter&& get()
|
|
||||||
{
|
|
||||||
return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
contiguous_bytes_input_adapter ia;
|
|
||||||
};
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,711 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <string> // string
|
|
||||||
#include <utility> // move
|
|
||||||
#include <vector> // vector
|
|
||||||
|
|
||||||
#include <nlohmann/detail/exceptions.hpp>
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief SAX interface
|
|
||||||
|
|
||||||
This class describes the SAX interface used by @ref nlohmann::json::sax_parse.
|
|
||||||
Each function is called in different situations while the input is parsed. The
|
|
||||||
boolean return value informs the parser whether to continue processing the
|
|
||||||
input.
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
struct json_sax
|
|
||||||
{
|
|
||||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
|
||||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
|
||||||
using number_float_t = typename BasicJsonType::number_float_t;
|
|
||||||
using string_t = typename BasicJsonType::string_t;
|
|
||||||
using binary_t = typename BasicJsonType::binary_t;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief a null value was read
|
|
||||||
@return whether parsing should proceed
|
|
||||||
*/
|
|
||||||
virtual bool null() = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief a boolean value was read
|
|
||||||
@param[in] val boolean value
|
|
||||||
@return whether parsing should proceed
|
|
||||||
*/
|
|
||||||
virtual bool boolean(bool val) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief an integer number was read
|
|
||||||
@param[in] val integer value
|
|
||||||
@return whether parsing should proceed
|
|
||||||
*/
|
|
||||||
virtual bool number_integer(number_integer_t val) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief an unsigned integer number was read
|
|
||||||
@param[in] val unsigned integer value
|
|
||||||
@return whether parsing should proceed
|
|
||||||
*/
|
|
||||||
virtual bool number_unsigned(number_unsigned_t val) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief a floating-point number was read
|
|
||||||
@param[in] val floating-point value
|
|
||||||
@param[in] s raw token value
|
|
||||||
@return whether parsing should proceed
|
|
||||||
*/
|
|
||||||
virtual bool number_float(number_float_t val, const string_t& s) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief a string value was read
|
|
||||||
@param[in] val string value
|
|
||||||
@return whether parsing should proceed
|
|
||||||
@note It is safe to move the passed string value.
|
|
||||||
*/
|
|
||||||
virtual bool string(string_t& val) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief a binary value was read
|
|
||||||
@param[in] val binary value
|
|
||||||
@return whether parsing should proceed
|
|
||||||
@note It is safe to move the passed binary value.
|
|
||||||
*/
|
|
||||||
virtual bool binary(binary_t& val) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief the beginning of an object was read
|
|
||||||
@param[in] elements number of object elements or -1 if unknown
|
|
||||||
@return whether parsing should proceed
|
|
||||||
@note binary formats may report the number of elements
|
|
||||||
*/
|
|
||||||
virtual bool start_object(std::size_t elements) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief an object key was read
|
|
||||||
@param[in] val object key
|
|
||||||
@return whether parsing should proceed
|
|
||||||
@note It is safe to move the passed string.
|
|
||||||
*/
|
|
||||||
virtual bool key(string_t& val) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief the end of an object was read
|
|
||||||
@return whether parsing should proceed
|
|
||||||
*/
|
|
||||||
virtual bool end_object() = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief the beginning of an array was read
|
|
||||||
@param[in] elements number of array elements or -1 if unknown
|
|
||||||
@return whether parsing should proceed
|
|
||||||
@note binary formats may report the number of elements
|
|
||||||
*/
|
|
||||||
virtual bool start_array(std::size_t elements) = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief the end of an array was read
|
|
||||||
@return whether parsing should proceed
|
|
||||||
*/
|
|
||||||
virtual bool end_array() = 0;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief a parse error occurred
|
|
||||||
@param[in] position the position in the input where the error occurs
|
|
||||||
@param[in] last_token the last read token
|
|
||||||
@param[in] ex an exception object describing the error
|
|
||||||
@return whether parsing should proceed (must return false)
|
|
||||||
*/
|
|
||||||
virtual bool parse_error(std::size_t position,
|
|
||||||
const std::string& last_token,
|
|
||||||
const detail::exception& ex) = 0;
|
|
||||||
|
|
||||||
json_sax() = default;
|
|
||||||
json_sax(const json_sax&) = default;
|
|
||||||
json_sax(json_sax&&) noexcept = default;
|
|
||||||
json_sax& operator=(const json_sax&) = default;
|
|
||||||
json_sax& operator=(json_sax&&) noexcept = default;
|
|
||||||
virtual ~json_sax() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
/*!
|
|
||||||
@brief SAX implementation to create a JSON value from SAX events
|
|
||||||
|
|
||||||
This class implements the @ref json_sax interface and processes the SAX events
|
|
||||||
to create a JSON value which makes it basically a DOM parser. The structure or
|
|
||||||
hierarchy of the JSON value is managed by the stack `ref_stack` which contains
|
|
||||||
a pointer to the respective array or object for each recursion depth.
|
|
||||||
|
|
||||||
After successful parsing, the value that is passed by reference to the
|
|
||||||
constructor contains the parsed value.
|
|
||||||
|
|
||||||
@tparam BasicJsonType the JSON type
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
class json_sax_dom_parser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
|
||||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
|
||||||
using number_float_t = typename BasicJsonType::number_float_t;
|
|
||||||
using string_t = typename BasicJsonType::string_t;
|
|
||||||
using binary_t = typename BasicJsonType::binary_t;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@param[in,out] r reference to a JSON value that is manipulated while
|
|
||||||
parsing
|
|
||||||
@param[in] allow_exceptions_ whether parse errors yield exceptions
|
|
||||||
*/
|
|
||||||
explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)
|
|
||||||
: root(r), allow_exceptions(allow_exceptions_)
|
|
||||||
{}
|
|
||||||
|
|
||||||
// make class move-only
|
|
||||||
json_sax_dom_parser(const json_sax_dom_parser&) = delete;
|
|
||||||
json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
|
|
||||||
json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;
|
|
||||||
json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
|
|
||||||
~json_sax_dom_parser() = default;
|
|
||||||
|
|
||||||
bool null()
|
|
||||||
{
|
|
||||||
handle_value(nullptr);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool boolean(bool val)
|
|
||||||
{
|
|
||||||
handle_value(val);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool number_integer(number_integer_t val)
|
|
||||||
{
|
|
||||||
handle_value(val);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool number_unsigned(number_unsigned_t val)
|
|
||||||
{
|
|
||||||
handle_value(val);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool number_float(number_float_t val, const string_t& /*unused*/)
|
|
||||||
{
|
|
||||||
handle_value(val);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool string(string_t& val)
|
|
||||||
{
|
|
||||||
handle_value(val);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool binary(binary_t& val)
|
|
||||||
{
|
|
||||||
handle_value(std::move(val));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool start_object(std::size_t len)
|
|
||||||
{
|
|
||||||
ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
|
|
||||||
{
|
|
||||||
JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool key(string_t& val)
|
|
||||||
{
|
|
||||||
// add null at given key and store the reference for later
|
|
||||||
object_element = &(ref_stack.back()->m_value.object->operator[](val));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool end_object()
|
|
||||||
{
|
|
||||||
ref_stack.back()->set_parents();
|
|
||||||
ref_stack.pop_back();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool start_array(std::size_t len)
|
|
||||||
{
|
|
||||||
ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
|
|
||||||
{
|
|
||||||
JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool end_array()
|
|
||||||
{
|
|
||||||
ref_stack.back()->set_parents();
|
|
||||||
ref_stack.pop_back();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Exception>
|
|
||||||
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
|
|
||||||
const Exception& ex)
|
|
||||||
{
|
|
||||||
errored = true;
|
|
||||||
static_cast<void>(ex);
|
|
||||||
if (allow_exceptions)
|
|
||||||
{
|
|
||||||
JSON_THROW(ex);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool is_errored() const
|
|
||||||
{
|
|
||||||
return errored;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/*!
|
|
||||||
@invariant If the ref stack is empty, then the passed value will be the new
|
|
||||||
root.
|
|
||||||
@invariant If the ref stack contains a value, then it is an array or an
|
|
||||||
object to which we can add elements
|
|
||||||
*/
|
|
||||||
template<typename Value>
|
|
||||||
JSON_HEDLEY_RETURNS_NON_NULL
|
|
||||||
BasicJsonType* handle_value(Value&& v)
|
|
||||||
{
|
|
||||||
if (ref_stack.empty())
|
|
||||||
{
|
|
||||||
root = BasicJsonType(std::forward<Value>(v));
|
|
||||||
return &root;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
|
|
||||||
|
|
||||||
if (ref_stack.back()->is_array())
|
|
||||||
{
|
|
||||||
ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));
|
|
||||||
return &(ref_stack.back()->m_value.array->back());
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_ASSERT(ref_stack.back()->is_object());
|
|
||||||
JSON_ASSERT(object_element);
|
|
||||||
*object_element = BasicJsonType(std::forward<Value>(v));
|
|
||||||
return object_element;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the parsed JSON value
|
|
||||||
BasicJsonType& root;
|
|
||||||
/// stack to model hierarchy of values
|
|
||||||
std::vector<BasicJsonType*> ref_stack {};
|
|
||||||
/// helper to hold the reference for the next object element
|
|
||||||
BasicJsonType* object_element = nullptr;
|
|
||||||
/// whether a syntax error occurred
|
|
||||||
bool errored = false;
|
|
||||||
/// whether to throw exceptions in case of errors
|
|
||||||
const bool allow_exceptions = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
class json_sax_dom_callback_parser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
|
||||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
|
||||||
using number_float_t = typename BasicJsonType::number_float_t;
|
|
||||||
using string_t = typename BasicJsonType::string_t;
|
|
||||||
using binary_t = typename BasicJsonType::binary_t;
|
|
||||||
using parser_callback_t = typename BasicJsonType::parser_callback_t;
|
|
||||||
using parse_event_t = typename BasicJsonType::parse_event_t;
|
|
||||||
|
|
||||||
json_sax_dom_callback_parser(BasicJsonType& r,
|
|
||||||
const parser_callback_t cb,
|
|
||||||
const bool allow_exceptions_ = true)
|
|
||||||
: root(r), callback(cb), allow_exceptions(allow_exceptions_)
|
|
||||||
{
|
|
||||||
keep_stack.push_back(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// make class move-only
|
|
||||||
json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;
|
|
||||||
json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
|
|
||||||
json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;
|
|
||||||
json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
|
|
||||||
~json_sax_dom_callback_parser() = default;
|
|
||||||
|
|
||||||
bool null()
|
|
||||||
{
|
|
||||||
handle_value(nullptr);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool boolean(bool val)
|
|
||||||
{
|
|
||||||
handle_value(val);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool number_integer(number_integer_t val)
|
|
||||||
{
|
|
||||||
handle_value(val);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool number_unsigned(number_unsigned_t val)
|
|
||||||
{
|
|
||||||
handle_value(val);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool number_float(number_float_t val, const string_t& /*unused*/)
|
|
||||||
{
|
|
||||||
handle_value(val);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool string(string_t& val)
|
|
||||||
{
|
|
||||||
handle_value(val);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool binary(binary_t& val)
|
|
||||||
{
|
|
||||||
handle_value(std::move(val));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool start_object(std::size_t len)
|
|
||||||
{
|
|
||||||
// check callback for object start
|
|
||||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
|
|
||||||
keep_stack.push_back(keep);
|
|
||||||
|
|
||||||
auto val = handle_value(BasicJsonType::value_t::object, true);
|
|
||||||
ref_stack.push_back(val.second);
|
|
||||||
|
|
||||||
// check object limit
|
|
||||||
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
|
|
||||||
{
|
|
||||||
JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool key(string_t& val)
|
|
||||||
{
|
|
||||||
BasicJsonType k = BasicJsonType(val);
|
|
||||||
|
|
||||||
// check callback for key
|
|
||||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
|
|
||||||
key_keep_stack.push_back(keep);
|
|
||||||
|
|
||||||
// add discarded value at given key and store the reference for later
|
|
||||||
if (keep && ref_stack.back())
|
|
||||||
{
|
|
||||||
object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool end_object()
|
|
||||||
{
|
|
||||||
if (ref_stack.back())
|
|
||||||
{
|
|
||||||
if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
|
|
||||||
{
|
|
||||||
// discard object
|
|
||||||
*ref_stack.back() = discarded;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ref_stack.back()->set_parents();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_ASSERT(!ref_stack.empty());
|
|
||||||
JSON_ASSERT(!keep_stack.empty());
|
|
||||||
ref_stack.pop_back();
|
|
||||||
keep_stack.pop_back();
|
|
||||||
|
|
||||||
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
|
|
||||||
{
|
|
||||||
// remove discarded value
|
|
||||||
for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->is_discarded())
|
|
||||||
{
|
|
||||||
ref_stack.back()->erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool start_array(std::size_t len)
|
|
||||||
{
|
|
||||||
const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
|
|
||||||
keep_stack.push_back(keep);
|
|
||||||
|
|
||||||
auto val = handle_value(BasicJsonType::value_t::array, true);
|
|
||||||
ref_stack.push_back(val.second);
|
|
||||||
|
|
||||||
// check array limit
|
|
||||||
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast<std::size_t>(-1) && len > ref_stack.back()->max_size()))
|
|
||||||
{
|
|
||||||
JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool end_array()
|
|
||||||
{
|
|
||||||
bool keep = true;
|
|
||||||
|
|
||||||
if (ref_stack.back())
|
|
||||||
{
|
|
||||||
keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
|
|
||||||
if (keep)
|
|
||||||
{
|
|
||||||
ref_stack.back()->set_parents();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// discard array
|
|
||||||
*ref_stack.back() = discarded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_ASSERT(!ref_stack.empty());
|
|
||||||
JSON_ASSERT(!keep_stack.empty());
|
|
||||||
ref_stack.pop_back();
|
|
||||||
keep_stack.pop_back();
|
|
||||||
|
|
||||||
// remove discarded value
|
|
||||||
if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())
|
|
||||||
{
|
|
||||||
ref_stack.back()->m_value.array->pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Exception>
|
|
||||||
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
|
|
||||||
const Exception& ex)
|
|
||||||
{
|
|
||||||
errored = true;
|
|
||||||
static_cast<void>(ex);
|
|
||||||
if (allow_exceptions)
|
|
||||||
{
|
|
||||||
JSON_THROW(ex);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool is_errored() const
|
|
||||||
{
|
|
||||||
return errored;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/*!
|
|
||||||
@param[in] v value to add to the JSON value we build during parsing
|
|
||||||
@param[in] skip_callback whether we should skip calling the callback
|
|
||||||
function; this is required after start_array() and
|
|
||||||
start_object() SAX events, because otherwise we would call the
|
|
||||||
callback function with an empty array or object, respectively.
|
|
||||||
|
|
||||||
@invariant If the ref stack is empty, then the passed value will be the new
|
|
||||||
root.
|
|
||||||
@invariant If the ref stack contains a value, then it is an array or an
|
|
||||||
object to which we can add elements
|
|
||||||
|
|
||||||
@return pair of boolean (whether value should be kept) and pointer (to the
|
|
||||||
passed value in the ref_stack hierarchy; nullptr if not kept)
|
|
||||||
*/
|
|
||||||
template<typename Value>
|
|
||||||
std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
|
|
||||||
{
|
|
||||||
JSON_ASSERT(!keep_stack.empty());
|
|
||||||
|
|
||||||
// do not handle this value if we know it would be added to a discarded
|
|
||||||
// container
|
|
||||||
if (!keep_stack.back())
|
|
||||||
{
|
|
||||||
return {false, nullptr};
|
|
||||||
}
|
|
||||||
|
|
||||||
// create value
|
|
||||||
auto value = BasicJsonType(std::forward<Value>(v));
|
|
||||||
|
|
||||||
// check callback
|
|
||||||
const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
|
|
||||||
|
|
||||||
// do not handle this value if we just learnt it shall be discarded
|
|
||||||
if (!keep)
|
|
||||||
{
|
|
||||||
return {false, nullptr};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ref_stack.empty())
|
|
||||||
{
|
|
||||||
root = std::move(value);
|
|
||||||
return {true, &root};
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip this value if we already decided to skip the parent
|
|
||||||
// (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)
|
|
||||||
if (!ref_stack.back())
|
|
||||||
{
|
|
||||||
return {false, nullptr};
|
|
||||||
}
|
|
||||||
|
|
||||||
// we now only expect arrays and objects
|
|
||||||
JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
|
|
||||||
|
|
||||||
// array
|
|
||||||
if (ref_stack.back()->is_array())
|
|
||||||
{
|
|
||||||
ref_stack.back()->m_value.array->emplace_back(std::move(value));
|
|
||||||
return {true, &(ref_stack.back()->m_value.array->back())};
|
|
||||||
}
|
|
||||||
|
|
||||||
// object
|
|
||||||
JSON_ASSERT(ref_stack.back()->is_object());
|
|
||||||
// check if we should store an element for the current key
|
|
||||||
JSON_ASSERT(!key_keep_stack.empty());
|
|
||||||
const bool store_element = key_keep_stack.back();
|
|
||||||
key_keep_stack.pop_back();
|
|
||||||
|
|
||||||
if (!store_element)
|
|
||||||
{
|
|
||||||
return {false, nullptr};
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_ASSERT(object_element);
|
|
||||||
*object_element = std::move(value);
|
|
||||||
return {true, object_element};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the parsed JSON value
|
|
||||||
BasicJsonType& root;
|
|
||||||
/// stack to model hierarchy of values
|
|
||||||
std::vector<BasicJsonType*> ref_stack {};
|
|
||||||
/// stack to manage which values to keep
|
|
||||||
std::vector<bool> keep_stack {};
|
|
||||||
/// stack to manage which object keys to keep
|
|
||||||
std::vector<bool> key_keep_stack {};
|
|
||||||
/// helper to hold the reference for the next object element
|
|
||||||
BasicJsonType* object_element = nullptr;
|
|
||||||
/// whether a syntax error occurred
|
|
||||||
bool errored = false;
|
|
||||||
/// callback function
|
|
||||||
const parser_callback_t callback = nullptr;
|
|
||||||
/// whether to throw exceptions in case of errors
|
|
||||||
const bool allow_exceptions = true;
|
|
||||||
/// a discarded value for the callback
|
|
||||||
BasicJsonType discarded = BasicJsonType::value_t::discarded;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
class json_sax_acceptor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
|
||||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
|
||||||
using number_float_t = typename BasicJsonType::number_float_t;
|
|
||||||
using string_t = typename BasicJsonType::string_t;
|
|
||||||
using binary_t = typename BasicJsonType::binary_t;
|
|
||||||
|
|
||||||
bool null()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool boolean(bool /*unused*/)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool number_integer(number_integer_t /*unused*/)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool number_unsigned(number_unsigned_t /*unused*/)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool string(string_t& /*unused*/)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool binary(binary_t& /*unused*/)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool start_object(std::size_t /*unused*/ = static_cast<std::size_t>(-1))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool key(string_t& /*unused*/)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool end_object()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool start_array(std::size_t /*unused*/ = static_cast<std::size_t>(-1))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool end_array()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
} // namespace nlohmann
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,499 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cmath> // isfinite
|
|
||||||
#include <cstdint> // uint8_t
|
|
||||||
#include <functional> // function
|
|
||||||
#include <string> // string
|
|
||||||
#include <utility> // move
|
|
||||||
#include <vector> // vector
|
|
||||||
|
|
||||||
#include <nlohmann/detail/exceptions.hpp>
|
|
||||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
|
||||||
#include <nlohmann/detail/input/json_sax.hpp>
|
|
||||||
#include <nlohmann/detail/input/lexer.hpp>
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
#include <nlohmann/detail/meta/is_sax.hpp>
|
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
////////////
|
|
||||||
// parser //
|
|
||||||
////////////
|
|
||||||
|
|
||||||
enum class parse_event_t : std::uint8_t
|
|
||||||
{
|
|
||||||
/// the parser read `{` and started to process a JSON object
|
|
||||||
object_start,
|
|
||||||
/// the parser read `}` and finished processing a JSON object
|
|
||||||
object_end,
|
|
||||||
/// the parser read `[` and started to process a JSON array
|
|
||||||
array_start,
|
|
||||||
/// the parser read `]` and finished processing a JSON array
|
|
||||||
array_end,
|
|
||||||
/// the parser read a key of a value in an object
|
|
||||||
key,
|
|
||||||
/// the parser finished reading a JSON value
|
|
||||||
value
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
using parser_callback_t =
|
|
||||||
std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief syntax analysis
|
|
||||||
|
|
||||||
This class implements a recursive descent parser.
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType, typename InputAdapterType>
|
|
||||||
class parser
|
|
||||||
{
|
|
||||||
using number_integer_t = typename BasicJsonType::number_integer_t;
|
|
||||||
using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
|
||||||
using number_float_t = typename BasicJsonType::number_float_t;
|
|
||||||
using string_t = typename BasicJsonType::string_t;
|
|
||||||
using lexer_t = lexer<BasicJsonType, InputAdapterType>;
|
|
||||||
using token_type = typename lexer_t::token_type;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// a parser reading from an input adapter
|
|
||||||
explicit parser(InputAdapterType&& adapter,
|
|
||||||
const parser_callback_t<BasicJsonType> cb = nullptr,
|
|
||||||
const bool allow_exceptions_ = true,
|
|
||||||
const bool skip_comments = false)
|
|
||||||
: callback(cb)
|
|
||||||
, m_lexer(std::move(adapter), skip_comments)
|
|
||||||
, allow_exceptions(allow_exceptions_)
|
|
||||||
{
|
|
||||||
// read first token
|
|
||||||
get_token();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief public parser interface
|
|
||||||
|
|
||||||
@param[in] strict whether to expect the last token to be EOF
|
|
||||||
@param[in,out] result parsed JSON value
|
|
||||||
|
|
||||||
@throw parse_error.101 in case of an unexpected token
|
|
||||||
@throw parse_error.102 if to_unicode fails or surrogate error
|
|
||||||
@throw parse_error.103 if to_unicode fails
|
|
||||||
*/
|
|
||||||
void parse(const bool strict, BasicJsonType& result)
|
|
||||||
{
|
|
||||||
if (callback)
|
|
||||||
{
|
|
||||||
json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
|
|
||||||
sax_parse_internal(&sdp);
|
|
||||||
|
|
||||||
// in strict mode, input must be completely read
|
|
||||||
if (strict && (get_token() != token_type::end_of_input))
|
|
||||||
{
|
|
||||||
sdp.parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(),
|
|
||||||
exception_message(token_type::end_of_input, "value"), BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case of an error, return discarded value
|
|
||||||
if (sdp.is_errored())
|
|
||||||
{
|
|
||||||
result = value_t::discarded;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set top-level value to null if it was discarded by the callback
|
|
||||||
// function
|
|
||||||
if (result.is_discarded())
|
|
||||||
{
|
|
||||||
result = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
|
|
||||||
sax_parse_internal(&sdp);
|
|
||||||
|
|
||||||
// in strict mode, input must be completely read
|
|
||||||
if (strict && (get_token() != token_type::end_of_input))
|
|
||||||
{
|
|
||||||
sdp.parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case of an error, return discarded value
|
|
||||||
if (sdp.is_errored())
|
|
||||||
{
|
|
||||||
result = value_t::discarded;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.assert_invariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief public accept interface
|
|
||||||
|
|
||||||
@param[in] strict whether to expect the last token to be EOF
|
|
||||||
@return whether the input is a proper JSON text
|
|
||||||
*/
|
|
||||||
bool accept(const bool strict = true)
|
|
||||||
{
|
|
||||||
json_sax_acceptor<BasicJsonType> sax_acceptor;
|
|
||||||
return sax_parse(&sax_acceptor, strict);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename SAX>
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
bool sax_parse(SAX* sax, const bool strict = true)
|
|
||||||
{
|
|
||||||
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
|
||||||
const bool result = sax_parse_internal(sax);
|
|
||||||
|
|
||||||
// strict mode: next byte must be EOF
|
|
||||||
if (result && strict && (get_token() != token_type::end_of_input))
|
|
||||||
{
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template<typename SAX>
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
|
||||||
bool sax_parse_internal(SAX* sax)
|
|
||||||
{
|
|
||||||
// stack to remember the hierarchy of structured values we are parsing
|
|
||||||
// true = array; false = object
|
|
||||||
std::vector<bool> states;
|
|
||||||
// value to avoid a goto (see comment where set to true)
|
|
||||||
bool skip_to_state_evaluation = false;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (!skip_to_state_evaluation)
|
|
||||||
{
|
|
||||||
// invariant: get_token() was called before each iteration
|
|
||||||
switch (last_token)
|
|
||||||
{
|
|
||||||
case token_type::begin_object:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast<std::size_t>(-1))))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// closing } -> we are done
|
|
||||||
if (get_token() == token_type::end_object)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse key
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
|
|
||||||
{
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType()));
|
|
||||||
}
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse separator (:)
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
|
||||||
{
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// remember we are now inside an object
|
|
||||||
states.push_back(false);
|
|
||||||
|
|
||||||
// parse values
|
|
||||||
get_token();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
case token_type::begin_array:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast<std::size_t>(-1))))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// closing ] -> we are done
|
|
||||||
if (get_token() == token_type::end_array)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remember we are now inside an array
|
|
||||||
states.push_back(true);
|
|
||||||
|
|
||||||
// parse values (no need to call get_token)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
case token_type::value_float:
|
|
||||||
{
|
|
||||||
const auto res = m_lexer.get_number_float();
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
|
|
||||||
{
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case token_type::literal_false:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case token_type::literal_null:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->null()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case token_type::literal_true:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case token_type::value_integer:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case token_type::value_string:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case token_type::value_unsigned:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case token_type::parse_error:
|
|
||||||
{
|
|
||||||
// using "uninitialized" to avoid "expected" message
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
case token_type::uninitialized:
|
|
||||||
case token_type::end_array:
|
|
||||||
case token_type::end_object:
|
|
||||||
case token_type::name_separator:
|
|
||||||
case token_type::value_separator:
|
|
||||||
case token_type::end_of_input:
|
|
||||||
case token_type::literal_or_value:
|
|
||||||
default: // the last token was unexpected
|
|
||||||
{
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
skip_to_state_evaluation = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we reached this line after we successfully parsed a value
|
|
||||||
if (states.empty())
|
|
||||||
{
|
|
||||||
// empty stack: we reached the end of the hierarchy: done
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (states.back()) // array
|
|
||||||
{
|
|
||||||
// comma -> next value
|
|
||||||
if (get_token() == token_type::value_separator)
|
|
||||||
{
|
|
||||||
// parse a new value
|
|
||||||
get_token();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// closing ]
|
|
||||||
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are done with this array. Before we can parse a
|
|
||||||
// new value, we need to evaluate the new state first.
|
|
||||||
// By setting skip_to_state_evaluation to false, we
|
|
||||||
// are effectively jumping to the beginning of this if.
|
|
||||||
JSON_ASSERT(!states.empty());
|
|
||||||
states.pop_back();
|
|
||||||
skip_to_state_evaluation = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// states.back() is false -> object
|
|
||||||
|
|
||||||
// comma -> next value
|
|
||||||
if (get_token() == token_type::value_separator)
|
|
||||||
{
|
|
||||||
// parse key
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))
|
|
||||||
{
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse separator (:)
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
|
|
||||||
{
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse values
|
|
||||||
get_token();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// closing }
|
|
||||||
if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are done with this object. Before we can parse a
|
|
||||||
// new value, we need to evaluate the new state first.
|
|
||||||
// By setting skip_to_state_evaluation to false, we
|
|
||||||
// are effectively jumping to the beginning of this if.
|
|
||||||
JSON_ASSERT(!states.empty());
|
|
||||||
states.pop_back();
|
|
||||||
skip_to_state_evaluation = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sax->parse_error(m_lexer.get_position(),
|
|
||||||
m_lexer.get_token_string(),
|
|
||||||
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get next token from lexer
|
|
||||||
token_type get_token()
|
|
||||||
{
|
|
||||||
return last_token = m_lexer.scan();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string exception_message(const token_type expected, const std::string& context)
|
|
||||||
{
|
|
||||||
std::string error_msg = "syntax error ";
|
|
||||||
|
|
||||||
if (!context.empty())
|
|
||||||
{
|
|
||||||
error_msg += "while parsing " + context + " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
error_msg += "- ";
|
|
||||||
|
|
||||||
if (last_token == token_type::parse_error)
|
|
||||||
{
|
|
||||||
error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" +
|
|
||||||
m_lexer.get_token_string() + "'";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expected != token_type::uninitialized)
|
|
||||||
{
|
|
||||||
error_msg += "; expected " + std::string(lexer_t::token_type_name(expected));
|
|
||||||
}
|
|
||||||
|
|
||||||
return error_msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// callback function
|
|
||||||
const parser_callback_t<BasicJsonType> callback = nullptr;
|
|
||||||
/// the type of the last read token
|
|
||||||
token_type last_token = token_type::uninitialized;
|
|
||||||
/// the lexer
|
|
||||||
lexer_t m_lexer;
|
|
||||||
/// whether to throw exceptions in case of errors
|
|
||||||
const bool allow_exceptions = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef> // size_t
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
/// struct to capture the start position of the current token
|
|
||||||
struct position_t
|
|
||||||
{
|
|
||||||
/// the total number of characters read
|
|
||||||
std::size_t chars_read_total = 0;
|
|
||||||
/// the number of characters read in the current line
|
|
||||||
std::size_t chars_read_current_line = 0;
|
|
||||||
/// the number of lines read
|
|
||||||
std::size_t lines_read = 0;
|
|
||||||
|
|
||||||
/// conversion to size_t to preserve SAX interface
|
|
||||||
constexpr operator size_t() const
|
|
||||||
{
|
|
||||||
return chars_read_total;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <nlohmann/detail/iterators/primitive_iterator.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
/*!
|
|
||||||
@brief an iterator value
|
|
||||||
|
|
||||||
@note This structure could easily be a union, but MSVC currently does not allow
|
|
||||||
unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType> struct internal_iterator
|
|
||||||
{
|
|
||||||
/// iterator for JSON objects
|
|
||||||
typename BasicJsonType::object_t::iterator object_iterator {};
|
|
||||||
/// iterator for JSON arrays
|
|
||||||
typename BasicJsonType::array_t::iterator array_iterator {};
|
|
||||||
/// generic iterator for all other types
|
|
||||||
primitive_iterator_t primitive_iterator {};
|
|
||||||
};
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,739 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
|
|
||||||
#include <type_traits> // conditional, is_const, remove_const
|
|
||||||
|
|
||||||
#include <nlohmann/detail/exceptions.hpp>
|
|
||||||
#include <nlohmann/detail/iterators/internal_iterator.hpp>
|
|
||||||
#include <nlohmann/detail/iterators/primitive_iterator.hpp>
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
// forward declare, to be able to friend it later on
|
|
||||||
template<typename IteratorType> class iteration_proxy;
|
|
||||||
template<typename IteratorType> class iteration_proxy_value;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief a template for a bidirectional iterator for the @ref basic_json class
|
|
||||||
This class implements a both iterators (iterator and const_iterator) for the
|
|
||||||
@ref basic_json class.
|
|
||||||
@note An iterator is called *initialized* when a pointer to a JSON value has
|
|
||||||
been set (e.g., by a constructor or a copy assignment). If the iterator is
|
|
||||||
default-constructed, it is *uninitialized* and most methods are undefined.
|
|
||||||
**The library uses assertions to detect calls on uninitialized iterators.**
|
|
||||||
@requirement The class satisfies the following concept requirements:
|
|
||||||
-
|
|
||||||
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
|
|
||||||
The iterator that can be moved can be moved in both directions (i.e.
|
|
||||||
incremented and decremented).
|
|
||||||
@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
|
|
||||||
iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
|
|
||||||
{
|
|
||||||
/// the iterator with BasicJsonType of different const-ness
|
|
||||||
using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
|
|
||||||
/// allow basic_json to access private members
|
|
||||||
friend other_iter_impl;
|
|
||||||
friend BasicJsonType;
|
|
||||||
friend iteration_proxy<iter_impl>;
|
|
||||||
friend iteration_proxy_value<iter_impl>;
|
|
||||||
|
|
||||||
using object_t = typename BasicJsonType::object_t;
|
|
||||||
using array_t = typename BasicJsonType::array_t;
|
|
||||||
// make sure BasicJsonType is basic_json or const basic_json
|
|
||||||
static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
|
|
||||||
"iter_impl only accepts (const) basic_json");
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
|
|
||||||
/// The C++ Standard has never required user-defined iterators to derive from std::iterator.
|
|
||||||
/// A user-defined iterator should provide publicly accessible typedefs named
|
|
||||||
/// iterator_category, value_type, difference_type, pointer, and reference.
|
|
||||||
/// Note that value_type is required to be non-const, even for constant iterators.
|
|
||||||
using iterator_category = std::bidirectional_iterator_tag;
|
|
||||||
|
|
||||||
/// the type of the values when the iterator is dereferenced
|
|
||||||
using value_type = typename BasicJsonType::value_type;
|
|
||||||
/// a type to represent differences between iterators
|
|
||||||
using difference_type = typename BasicJsonType::difference_type;
|
|
||||||
/// defines a pointer to the type iterated over (value_type)
|
|
||||||
using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
|
|
||||||
typename BasicJsonType::const_pointer,
|
|
||||||
typename BasicJsonType::pointer>::type;
|
|
||||||
/// defines a reference to the type iterated over (value_type)
|
|
||||||
using reference =
|
|
||||||
typename std::conditional<std::is_const<BasicJsonType>::value,
|
|
||||||
typename BasicJsonType::const_reference,
|
|
||||||
typename BasicJsonType::reference>::type;
|
|
||||||
|
|
||||||
iter_impl() = default;
|
|
||||||
~iter_impl() = default;
|
|
||||||
iter_impl(iter_impl&&) noexcept = default;
|
|
||||||
iter_impl& operator=(iter_impl&&) noexcept = default;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief constructor for a given JSON instance
|
|
||||||
@param[in] object pointer to a JSON object for this iterator
|
|
||||||
@pre object != nullptr
|
|
||||||
@post The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
explicit iter_impl(pointer object) noexcept : m_object(object)
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
switch (m_object->m_type)
|
|
||||||
{
|
|
||||||
case value_t::object:
|
|
||||||
{
|
|
||||||
m_it.object_iterator = typename object_t::iterator();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
m_it.array_iterator = typename array_t::iterator();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
m_it.primitive_iterator = primitive_iterator_t();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@note The conventional copy constructor and copy assignment are implicitly
|
|
||||||
defined. Combined with the following converting constructor and
|
|
||||||
assignment, they support: (1) copy from iterator to iterator, (2)
|
|
||||||
copy from const iterator to const iterator, and (3) conversion from
|
|
||||||
iterator to const iterator. However conversion from const iterator
|
|
||||||
to iterator is not defined.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief const copy constructor
|
|
||||||
@param[in] other const iterator to copy from
|
|
||||||
@note This copy constructor had to be defined explicitly to circumvent a bug
|
|
||||||
occurring on msvc v19.0 compiler (VS 2015) debug build. For more
|
|
||||||
information refer to: https://github.com/nlohmann/json/issues/1608
|
|
||||||
*/
|
|
||||||
iter_impl(const iter_impl<const BasicJsonType>& other) noexcept
|
|
||||||
: m_object(other.m_object), m_it(other.m_it)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief converting assignment
|
|
||||||
@param[in] other const iterator to copy from
|
|
||||||
@return const/non-const iterator
|
|
||||||
@note It is not checked whether @a other is initialized.
|
|
||||||
*/
|
|
||||||
iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept
|
|
||||||
{
|
|
||||||
if (&other != this)
|
|
||||||
{
|
|
||||||
m_object = other.m_object;
|
|
||||||
m_it = other.m_it;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief converting constructor
|
|
||||||
@param[in] other non-const iterator to copy from
|
|
||||||
@note It is not checked whether @a other is initialized.
|
|
||||||
*/
|
|
||||||
iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
|
|
||||||
: m_object(other.m_object), m_it(other.m_it)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief converting assignment
|
|
||||||
@param[in] other non-const iterator to copy from
|
|
||||||
@return const/non-const iterator
|
|
||||||
@note It is not checked whether @a other is initialized.
|
|
||||||
*/
|
|
||||||
iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp)
|
|
||||||
{
|
|
||||||
m_object = other.m_object;
|
|
||||||
m_it = other.m_it;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
|
||||||
/*!
|
|
||||||
@brief set the iterator to the first value
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
void set_begin() noexcept
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
switch (m_object->m_type)
|
|
||||||
{
|
|
||||||
case value_t::object:
|
|
||||||
{
|
|
||||||
m_it.object_iterator = m_object->m_value.object->begin();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
m_it.array_iterator = m_object->m_value.array->begin();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
{
|
|
||||||
// set to end so begin()==end() is true: null is empty
|
|
||||||
m_it.primitive_iterator.set_end();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
m_it.primitive_iterator.set_begin();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief set the iterator past the last value
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
void set_end() noexcept
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
switch (m_object->m_type)
|
|
||||||
{
|
|
||||||
case value_t::object:
|
|
||||||
{
|
|
||||||
m_it.object_iterator = m_object->m_value.object->end();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
m_it.array_iterator = m_object->m_value.array->end();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
m_it.primitive_iterator.set_end();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
/*!
|
|
||||||
@brief return a reference to the value pointed to by the iterator
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
reference operator*() const
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
switch (m_object->m_type)
|
|
||||||
{
|
|
||||||
case value_t::object:
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
|
|
||||||
return m_it.object_iterator->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
|
|
||||||
return *m_it.array_iterator;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
|
|
||||||
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
|
|
||||||
{
|
|
||||||
return *m_object;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief dereference the iterator
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
pointer operator->() const
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
switch (m_object->m_type)
|
|
||||||
{
|
|
||||||
case value_t::object:
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end());
|
|
||||||
return &(m_it.object_iterator->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end());
|
|
||||||
return &*m_it.array_iterator;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
|
|
||||||
{
|
|
||||||
return m_object;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief post-increment (it++)
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
iter_impl const operator++(int) // NOLINT(readability-const-return-type)
|
|
||||||
{
|
|
||||||
auto result = *this;
|
|
||||||
++(*this);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief pre-increment (++it)
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
iter_impl& operator++()
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
switch (m_object->m_type)
|
|
||||||
{
|
|
||||||
case value_t::object:
|
|
||||||
{
|
|
||||||
std::advance(m_it.object_iterator, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
std::advance(m_it.array_iterator, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
++m_it.primitive_iterator;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief post-decrement (it--)
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
iter_impl const operator--(int) // NOLINT(readability-const-return-type)
|
|
||||||
{
|
|
||||||
auto result = *this;
|
|
||||||
--(*this);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief pre-decrement (--it)
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
iter_impl& operator--()
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
switch (m_object->m_type)
|
|
||||||
{
|
|
||||||
case value_t::object:
|
|
||||||
{
|
|
||||||
std::advance(m_it.object_iterator, -1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
std::advance(m_it.array_iterator, -1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
--m_it.primitive_iterator;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief comparison: equal
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
|
|
||||||
bool operator==(const IterImpl& other) const
|
|
||||||
{
|
|
||||||
// if objects are not the same, the comparison is undefined
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
|
||||||
{
|
|
||||||
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object));
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
switch (m_object->m_type)
|
|
||||||
{
|
|
||||||
case value_t::object:
|
|
||||||
return (m_it.object_iterator == other.m_it.object_iterator);
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
return (m_it.array_iterator == other.m_it.array_iterator);
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
return (m_it.primitive_iterator == other.m_it.primitive_iterator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief comparison: not equal
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
|
|
||||||
bool operator!=(const IterImpl& other) const
|
|
||||||
{
|
|
||||||
return !operator==(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief comparison: smaller
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
bool operator<(const iter_impl& other) const
|
|
||||||
{
|
|
||||||
// if objects are not the same, the comparison is undefined
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
|
|
||||||
{
|
|
||||||
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object));
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
switch (m_object->m_type)
|
|
||||||
{
|
|
||||||
case value_t::object:
|
|
||||||
JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object));
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
return (m_it.array_iterator < other.m_it.array_iterator);
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
return (m_it.primitive_iterator < other.m_it.primitive_iterator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief comparison: less than or equal
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
bool operator<=(const iter_impl& other) const
|
|
||||||
{
|
|
||||||
return !other.operator < (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief comparison: greater than
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
bool operator>(const iter_impl& other) const
|
|
||||||
{
|
|
||||||
return !operator<=(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief comparison: greater than or equal
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
bool operator>=(const iter_impl& other) const
|
|
||||||
{
|
|
||||||
return !operator<(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief add to iterator
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
iter_impl& operator+=(difference_type i)
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
switch (m_object->m_type)
|
|
||||||
{
|
|
||||||
case value_t::object:
|
|
||||||
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object));
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
std::advance(m_it.array_iterator, i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
m_it.primitive_iterator += i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief subtract from iterator
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
iter_impl& operator-=(difference_type i)
|
|
||||||
{
|
|
||||||
return operator+=(-i);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief add to iterator
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
iter_impl operator+(difference_type i) const
|
|
||||||
{
|
|
||||||
auto result = *this;
|
|
||||||
result += i;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief addition of distance and iterator
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
friend iter_impl operator+(difference_type i, const iter_impl& it)
|
|
||||||
{
|
|
||||||
auto result = it;
|
|
||||||
result += i;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief subtract from iterator
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
iter_impl operator-(difference_type i) const
|
|
||||||
{
|
|
||||||
auto result = *this;
|
|
||||||
result -= i;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief return difference
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
difference_type operator-(const iter_impl& other) const
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
switch (m_object->m_type)
|
|
||||||
{
|
|
||||||
case value_t::object:
|
|
||||||
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object));
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
return m_it.array_iterator - other.m_it.array_iterator;
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
return m_it.primitive_iterator - other.m_it.primitive_iterator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief access to successor
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
reference operator[](difference_type n) const
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
switch (m_object->m_type)
|
|
||||||
{
|
|
||||||
case value_t::object:
|
|
||||||
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object));
|
|
||||||
|
|
||||||
case value_t::array:
|
|
||||||
return *std::next(m_it.array_iterator, n);
|
|
||||||
|
|
||||||
case value_t::null:
|
|
||||||
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
|
|
||||||
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))
|
|
||||||
{
|
|
||||||
return *m_object;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief return the key of an object iterator
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
const typename object_t::key_type& key() const
|
|
||||||
{
|
|
||||||
JSON_ASSERT(m_object != nullptr);
|
|
||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(m_object->is_object()))
|
|
||||||
{
|
|
||||||
return m_it.object_iterator->first;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief return the value of an iterator
|
|
||||||
@pre The iterator is initialized; i.e. `m_object != nullptr`.
|
|
||||||
*/
|
|
||||||
reference value() const
|
|
||||||
{
|
|
||||||
return operator*();
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
|
||||||
/// associated JSON instance
|
|
||||||
pointer m_object = nullptr;
|
|
||||||
/// the actual iterator of the associated instance
|
|
||||||
internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};
|
|
||||||
};
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef> // size_t
|
|
||||||
#include <iterator> // input_iterator_tag
|
|
||||||
#include <string> // string, to_string
|
|
||||||
#include <tuple> // tuple_size, get, tuple_element
|
|
||||||
#include <utility> // move
|
|
||||||
|
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
template<typename string_type>
|
|
||||||
void int_to_string( string_type& target, std::size_t value )
|
|
||||||
{
|
|
||||||
// For ADL
|
|
||||||
using std::to_string;
|
|
||||||
target = to_string(value);
|
|
||||||
}
|
|
||||||
template<typename IteratorType> class iteration_proxy_value
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using difference_type = std::ptrdiff_t;
|
|
||||||
using value_type = iteration_proxy_value;
|
|
||||||
using pointer = value_type * ;
|
|
||||||
using reference = value_type & ;
|
|
||||||
using iterator_category = std::input_iterator_tag;
|
|
||||||
using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// the iterator
|
|
||||||
IteratorType anchor;
|
|
||||||
/// an index for arrays (used to create key names)
|
|
||||||
std::size_t array_index = 0;
|
|
||||||
/// last stringified array index
|
|
||||||
mutable std::size_t array_index_last = 0;
|
|
||||||
/// a string representation of the array index
|
|
||||||
mutable string_type array_index_str = "0";
|
|
||||||
/// an empty string (to return a reference for primitive values)
|
|
||||||
const string_type empty_str{};
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit iteration_proxy_value(IteratorType it) noexcept
|
|
||||||
: anchor(std::move(it))
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// dereference operator (needed for range-based for)
|
|
||||||
iteration_proxy_value& operator*()
|
|
||||||
{
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// increment operator (needed for range-based for)
|
|
||||||
iteration_proxy_value& operator++()
|
|
||||||
{
|
|
||||||
++anchor;
|
|
||||||
++array_index;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// equality operator (needed for InputIterator)
|
|
||||||
bool operator==(const iteration_proxy_value& o) const
|
|
||||||
{
|
|
||||||
return anchor == o.anchor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// inequality operator (needed for range-based for)
|
|
||||||
bool operator!=(const iteration_proxy_value& o) const
|
|
||||||
{
|
|
||||||
return anchor != o.anchor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return key of the iterator
|
|
||||||
const string_type& key() const
|
|
||||||
{
|
|
||||||
JSON_ASSERT(anchor.m_object != nullptr);
|
|
||||||
|
|
||||||
switch (anchor.m_object->type())
|
|
||||||
{
|
|
||||||
// use integer array index as key
|
|
||||||
case value_t::array:
|
|
||||||
{
|
|
||||||
if (array_index != array_index_last)
|
|
||||||
{
|
|
||||||
int_to_string( array_index_str, array_index );
|
|
||||||
array_index_last = array_index;
|
|
||||||
}
|
|
||||||
return array_index_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// use key from the object
|
|
||||||
case value_t::object:
|
|
||||||
return anchor.key();
|
|
||||||
|
|
||||||
// use an empty key for all primitive types
|
|
||||||
case value_t::null:
|
|
||||||
case value_t::string:
|
|
||||||
case value_t::boolean:
|
|
||||||
case value_t::number_integer:
|
|
||||||
case value_t::number_unsigned:
|
|
||||||
case value_t::number_float:
|
|
||||||
case value_t::binary:
|
|
||||||
case value_t::discarded:
|
|
||||||
default:
|
|
||||||
return empty_str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return value of the iterator
|
|
||||||
typename IteratorType::reference value() const
|
|
||||||
{
|
|
||||||
return anchor.value();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// proxy class for the items() function
|
|
||||||
template<typename IteratorType> class iteration_proxy
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
/// the container to iterate
|
|
||||||
typename IteratorType::reference container;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// construct iteration proxy from a container
|
|
||||||
explicit iteration_proxy(typename IteratorType::reference cont) noexcept
|
|
||||||
: container(cont) {}
|
|
||||||
|
|
||||||
/// return iterator begin (needed for range-based for)
|
|
||||||
iteration_proxy_value<IteratorType> begin() noexcept
|
|
||||||
{
|
|
||||||
return iteration_proxy_value<IteratorType>(container.begin());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return iterator end (needed for range-based for)
|
|
||||||
iteration_proxy_value<IteratorType> end() noexcept
|
|
||||||
{
|
|
||||||
return iteration_proxy_value<IteratorType>(container.end());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Structured Bindings Support
|
|
||||||
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
|
|
||||||
// And see https://github.com/nlohmann/json/pull/1391
|
|
||||||
template<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>
|
|
||||||
auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())
|
|
||||||
{
|
|
||||||
return i.key();
|
|
||||||
}
|
|
||||||
// Structured Bindings Support
|
|
||||||
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
|
|
||||||
// And see https://github.com/nlohmann/json/pull/1391
|
|
||||||
template<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>
|
|
||||||
auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())
|
|
||||||
{
|
|
||||||
return i.value();
|
|
||||||
}
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace nlohmann
|
|
||||||
|
|
||||||
// The Addition to the STD Namespace is required to add
|
|
||||||
// Structured Bindings Support to the iteration_proxy_value class
|
|
||||||
// For further reference see https://blog.tartanllama.xyz/structured-bindings/
|
|
||||||
// And see https://github.com/nlohmann/json/pull/1391
|
|
||||||
namespace std
|
|
||||||
{
|
|
||||||
#if defined(__clang__)
|
|
||||||
// Fix: https://github.com/nlohmann/json/issues/1401
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wmismatched-tags"
|
|
||||||
#endif
|
|
||||||
template<typename IteratorType>
|
|
||||||
class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>
|
|
||||||
: public std::integral_constant<std::size_t, 2> {};
|
|
||||||
|
|
||||||
template<std::size_t N, typename IteratorType>
|
|
||||||
class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using type = decltype(
|
|
||||||
get<N>(std::declval <
|
|
||||||
::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));
|
|
||||||
};
|
|
||||||
#if defined(__clang__)
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
} // namespace std
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <iterator> // random_access_iterator_tag
|
|
||||||
|
|
||||||
#include <nlohmann/detail/meta/void_t.hpp>
|
|
||||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
template<typename It, typename = void>
|
|
||||||
struct iterator_types {};
|
|
||||||
|
|
||||||
template<typename It>
|
|
||||||
struct iterator_types <
|
|
||||||
It,
|
|
||||||
void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
|
|
||||||
typename It::reference, typename It::iterator_category >>
|
|
||||||
{
|
|
||||||
using difference_type = typename It::difference_type;
|
|
||||||
using value_type = typename It::value_type;
|
|
||||||
using pointer = typename It::pointer;
|
|
||||||
using reference = typename It::reference;
|
|
||||||
using iterator_category = typename It::iterator_category;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is required as some compilers implement std::iterator_traits in a way that
|
|
||||||
// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.
|
|
||||||
template<typename T, typename = void>
|
|
||||||
struct iterator_traits
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>
|
|
||||||
: iterator_types<T>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>
|
|
||||||
{
|
|
||||||
using iterator_category = std::random_access_iterator_tag;
|
|
||||||
using value_type = T;
|
|
||||||
using difference_type = ptrdiff_t;
|
|
||||||
using pointer = T*;
|
|
||||||
using reference = T&;
|
|
||||||
};
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef> // ptrdiff_t
|
|
||||||
#include <iterator> // reverse_iterator
|
|
||||||
#include <utility> // declval
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
//////////////////////
|
|
||||||
// reverse_iterator //
|
|
||||||
//////////////////////
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief a template for a reverse iterator class
|
|
||||||
|
|
||||||
@tparam Base the base iterator type to reverse. Valid types are @ref
|
|
||||||
iterator (to create @ref reverse_iterator) and @ref const_iterator (to
|
|
||||||
create @ref const_reverse_iterator).
|
|
||||||
|
|
||||||
@requirement The class satisfies the following concept requirements:
|
|
||||||
-
|
|
||||||
[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
|
|
||||||
The iterator that can be moved can be moved in both directions (i.e.
|
|
||||||
incremented and decremented).
|
|
||||||
- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
|
|
||||||
It is possible to write to the pointed-to element (only if @a Base is
|
|
||||||
@ref iterator).
|
|
||||||
|
|
||||||
@since version 1.0.0
|
|
||||||
*/
|
|
||||||
template<typename Base>
|
|
||||||
class json_reverse_iterator : public std::reverse_iterator<Base>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using difference_type = std::ptrdiff_t;
|
|
||||||
/// shortcut to the reverse iterator adapter
|
|
||||||
using base_iterator = std::reverse_iterator<Base>;
|
|
||||||
/// the reference type for the pointed-to element
|
|
||||||
using reference = typename Base::reference;
|
|
||||||
|
|
||||||
/// create reverse iterator from iterator
|
|
||||||
explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
|
|
||||||
: base_iterator(it) {}
|
|
||||||
|
|
||||||
/// create reverse iterator from base class
|
|
||||||
explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
|
|
||||||
|
|
||||||
/// post-increment (it++)
|
|
||||||
json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type)
|
|
||||||
{
|
|
||||||
return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// pre-increment (++it)
|
|
||||||
json_reverse_iterator& operator++()
|
|
||||||
{
|
|
||||||
return static_cast<json_reverse_iterator&>(base_iterator::operator++());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// post-decrement (it--)
|
|
||||||
json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type)
|
|
||||||
{
|
|
||||||
return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// pre-decrement (--it)
|
|
||||||
json_reverse_iterator& operator--()
|
|
||||||
{
|
|
||||||
return static_cast<json_reverse_iterator&>(base_iterator::operator--());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// add to iterator
|
|
||||||
json_reverse_iterator& operator+=(difference_type i)
|
|
||||||
{
|
|
||||||
return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// add to iterator
|
|
||||||
json_reverse_iterator operator+(difference_type i) const
|
|
||||||
{
|
|
||||||
return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// subtract from iterator
|
|
||||||
json_reverse_iterator operator-(difference_type i) const
|
|
||||||
{
|
|
||||||
return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return difference
|
|
||||||
difference_type operator-(const json_reverse_iterator& other) const
|
|
||||||
{
|
|
||||||
return base_iterator(*this) - base_iterator(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// access to successor
|
|
||||||
reference operator[](difference_type n) const
|
|
||||||
{
|
|
||||||
return *(this->operator+(n));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return the key of an object iterator
|
|
||||||
auto key() const -> decltype(std::declval<Base>().key())
|
|
||||||
{
|
|
||||||
auto it = --this->base();
|
|
||||||
return it.key();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return the value of an iterator
|
|
||||||
reference value() const
|
|
||||||
{
|
|
||||||
auto it = --this->base();
|
|
||||||
return it.operator * ();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef> // ptrdiff_t
|
|
||||||
#include <limits> // numeric_limits
|
|
||||||
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
@brief an iterator for primitive JSON types
|
|
||||||
|
|
||||||
This class models an iterator for primitive JSON types (boolean, number,
|
|
||||||
string). It's only purpose is to allow the iterator/const_iterator classes
|
|
||||||
to "iterate" over primitive values. Internally, the iterator is modeled by
|
|
||||||
a `difference_type` variable. Value begin_value (`0`) models the begin,
|
|
||||||
end_value (`1`) models past the end.
|
|
||||||
*/
|
|
||||||
class primitive_iterator_t
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
using difference_type = std::ptrdiff_t;
|
|
||||||
static constexpr difference_type begin_value = 0;
|
|
||||||
static constexpr difference_type end_value = begin_value + 1;
|
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
|
||||||
/// iterator as signed integer type
|
|
||||||
difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
|
|
||||||
|
|
||||||
public:
|
|
||||||
constexpr difference_type get_value() const noexcept
|
|
||||||
{
|
|
||||||
return m_it;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// set iterator to a defined beginning
|
|
||||||
void set_begin() noexcept
|
|
||||||
{
|
|
||||||
m_it = begin_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// set iterator to a defined past the end
|
|
||||||
void set_end() noexcept
|
|
||||||
{
|
|
||||||
m_it = end_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return whether the iterator can be dereferenced
|
|
||||||
constexpr bool is_begin() const noexcept
|
|
||||||
{
|
|
||||||
return m_it == begin_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// return whether the iterator is at end
|
|
||||||
constexpr bool is_end() const noexcept
|
|
||||||
{
|
|
||||||
return m_it == end_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
|
|
||||||
{
|
|
||||||
return lhs.m_it == rhs.m_it;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
|
|
||||||
{
|
|
||||||
return lhs.m_it < rhs.m_it;
|
|
||||||
}
|
|
||||||
|
|
||||||
primitive_iterator_t operator+(difference_type n) noexcept
|
|
||||||
{
|
|
||||||
auto result = *this;
|
|
||||||
result += n;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
|
|
||||||
{
|
|
||||||
return lhs.m_it - rhs.m_it;
|
|
||||||
}
|
|
||||||
|
|
||||||
primitive_iterator_t& operator++() noexcept
|
|
||||||
{
|
|
||||||
++m_it;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type)
|
|
||||||
{
|
|
||||||
auto result = *this;
|
|
||||||
++m_it;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
primitive_iterator_t& operator--() noexcept
|
|
||||||
{
|
|
||||||
--m_it;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type)
|
|
||||||
{
|
|
||||||
auto result = *this;
|
|
||||||
--m_it;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
primitive_iterator_t& operator+=(difference_type n) noexcept
|
|
||||||
{
|
|
||||||
m_it += n;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
primitive_iterator_t& operator-=(difference_type n) noexcept
|
|
||||||
{
|
|
||||||
m_it -= n;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,826 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm> // all_of
|
|
||||||
#include <cctype> // isdigit
|
|
||||||
#include <limits> // max
|
|
||||||
#include <numeric> // accumulate
|
|
||||||
#include <string> // string
|
|
||||||
#include <utility> // move
|
|
||||||
#include <vector> // vector
|
|
||||||
|
|
||||||
#include <nlohmann/detail/exceptions.hpp>
|
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
|
||||||
#include <nlohmann/detail/string_escape.hpp>
|
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
|
|
||||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
class json_pointer
|
|
||||||
{
|
|
||||||
// allow basic_json to access private members
|
|
||||||
NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
|
||||||
friend class basic_json;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// @brief create JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/
|
|
||||||
explicit json_pointer(const std::string& s = "")
|
|
||||||
: reference_tokens(split(s))
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// @brief return a string representation of the JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/to_string/
|
|
||||||
std::string to_string() const
|
|
||||||
{
|
|
||||||
return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
|
|
||||||
std::string{},
|
|
||||||
[](const std::string & a, const std::string & b)
|
|
||||||
{
|
|
||||||
return a + "/" + detail::escape(b);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief return a string representation of the JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_string/
|
|
||||||
operator std::string() const
|
|
||||||
{
|
|
||||||
return to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief append another JSON pointer at the end of this JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
|
|
||||||
json_pointer& operator/=(const json_pointer& ptr)
|
|
||||||
{
|
|
||||||
reference_tokens.insert(reference_tokens.end(),
|
|
||||||
ptr.reference_tokens.begin(),
|
|
||||||
ptr.reference_tokens.end());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief append an unescaped reference token at the end of this JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
|
|
||||||
json_pointer& operator/=(std::string token)
|
|
||||||
{
|
|
||||||
push_back(std::move(token));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief append an array index at the end of this JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
|
|
||||||
json_pointer& operator/=(std::size_t array_idx)
|
|
||||||
{
|
|
||||||
return *this /= std::to_string(array_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
|
|
||||||
friend json_pointer operator/(const json_pointer& lhs,
|
|
||||||
const json_pointer& rhs)
|
|
||||||
{
|
|
||||||
return json_pointer(lhs) /= rhs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
|
|
||||||
friend json_pointer operator/(const json_pointer& lhs, std::string token) // NOLINT(performance-unnecessary-value-param)
|
|
||||||
{
|
|
||||||
return json_pointer(lhs) /= std::move(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
|
|
||||||
friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx)
|
|
||||||
{
|
|
||||||
return json_pointer(lhs) /= array_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief returns the parent of this JSON pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/
|
|
||||||
json_pointer parent_pointer() const
|
|
||||||
{
|
|
||||||
if (empty())
|
|
||||||
{
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
json_pointer res = *this;
|
|
||||||
res.pop_back();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief remove last reference token
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/pop_back/
|
|
||||||
void pop_back()
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(empty()))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
reference_tokens.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief return last reference token
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/back/
|
|
||||||
const std::string& back() const
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(empty()))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return reference_tokens.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief append an unescaped token at the end of the reference pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/push_back/
|
|
||||||
void push_back(const std::string& token)
|
|
||||||
{
|
|
||||||
reference_tokens.push_back(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief append an unescaped token at the end of the reference pointer
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/push_back/
|
|
||||||
void push_back(std::string&& token)
|
|
||||||
{
|
|
||||||
reference_tokens.push_back(std::move(token));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief return whether pointer points to the root document
|
|
||||||
/// @sa https://json.nlohmann.me/api/json_pointer/empty/
|
|
||||||
bool empty() const noexcept
|
|
||||||
{
|
|
||||||
return reference_tokens.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/*!
|
|
||||||
@param[in] s reference token to be converted into an array index
|
|
||||||
|
|
||||||
@return integer representation of @a s
|
|
||||||
|
|
||||||
@throw parse_error.106 if an array index begins with '0'
|
|
||||||
@throw parse_error.109 if an array index begins not with a digit
|
|
||||||
@throw out_of_range.404 if string @a s could not be converted to an integer
|
|
||||||
@throw out_of_range.410 if an array index exceeds size_type
|
|
||||||
*/
|
|
||||||
static typename BasicJsonType::size_type array_index(const std::string& s)
|
|
||||||
{
|
|
||||||
using size_type = typename BasicJsonType::size_type;
|
|
||||||
|
|
||||||
// error condition (cf. RFC 6901, Sect. 4)
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// error condition (cf. RFC 6901, Sect. 4)
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t processed_chars = 0;
|
|
||||||
unsigned long long res = 0; // NOLINT(runtime/int)
|
|
||||||
JSON_TRY
|
|
||||||
{
|
|
||||||
res = std::stoull(s, &processed_chars);
|
|
||||||
}
|
|
||||||
JSON_CATCH(std::out_of_range&)
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the string was completely read
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size()))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// only triggered on special platforms (like 32bit), see also
|
|
||||||
// https://github.com/nlohmann/json/pull/2203
|
|
||||||
if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int)
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<size_type>(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
|
||||||
json_pointer top() const
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(empty()))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
json_pointer result = *this;
|
|
||||||
result.reference_tokens = {reference_tokens[0]};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/*!
|
|
||||||
@brief create and return a reference to the pointed to value
|
|
||||||
|
|
||||||
@complexity Linear in the number of reference tokens.
|
|
||||||
|
|
||||||
@throw parse_error.109 if array index is not a number
|
|
||||||
@throw type_error.313 if value cannot be unflattened
|
|
||||||
*/
|
|
||||||
BasicJsonType& get_and_create(BasicJsonType& j) const
|
|
||||||
{
|
|
||||||
auto* result = &j;
|
|
||||||
|
|
||||||
// in case no reference tokens exist, return a reference to the JSON value
|
|
||||||
// j which will be overwritten by a primitive value
|
|
||||||
for (const auto& reference_token : reference_tokens)
|
|
||||||
{
|
|
||||||
switch (result->type())
|
|
||||||
{
|
|
||||||
case detail::value_t::null:
|
|
||||||
{
|
|
||||||
if (reference_token == "0")
|
|
||||||
{
|
|
||||||
// start a new array if reference token is 0
|
|
||||||
result = &result->operator[](0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// start a new object otherwise
|
|
||||||
result = &result->operator[](reference_token);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::object:
|
|
||||||
{
|
|
||||||
// create an entry in the object
|
|
||||||
result = &result->operator[](reference_token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::array:
|
|
||||||
{
|
|
||||||
// create an entry in the array
|
|
||||||
result = &result->operator[](array_index(reference_token));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The following code is only reached if there exists a reference
|
|
||||||
token _and_ the current value is primitive. In this case, we have
|
|
||||||
an error situation, because primitive values may only occur as
|
|
||||||
single value; that is, with an empty list of reference tokens.
|
|
||||||
*/
|
|
||||||
case detail::value_t::string:
|
|
||||||
case detail::value_t::boolean:
|
|
||||||
case detail::value_t::number_integer:
|
|
||||||
case detail::value_t::number_unsigned:
|
|
||||||
case detail::value_t::number_float:
|
|
||||||
case detail::value_t::binary:
|
|
||||||
case detail::value_t::discarded:
|
|
||||||
default:
|
|
||||||
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief return a reference to the pointed to value
|
|
||||||
|
|
||||||
@note This version does not throw if a value is not present, but tries to
|
|
||||||
create nested values instead. For instance, calling this function
|
|
||||||
with pointer `"/this/that"` on a null value is equivalent to calling
|
|
||||||
`operator[]("this").operator[]("that")` on that value, effectively
|
|
||||||
changing the null value to an object.
|
|
||||||
|
|
||||||
@param[in] ptr a JSON value
|
|
||||||
|
|
||||||
@return reference to the JSON value pointed to by the JSON pointer
|
|
||||||
|
|
||||||
@complexity Linear in the length of the JSON pointer.
|
|
||||||
|
|
||||||
@throw parse_error.106 if an array index begins with '0'
|
|
||||||
@throw parse_error.109 if an array index was not a number
|
|
||||||
@throw out_of_range.404 if the JSON pointer can not be resolved
|
|
||||||
*/
|
|
||||||
BasicJsonType& get_unchecked(BasicJsonType* ptr) const
|
|
||||||
{
|
|
||||||
for (const auto& reference_token : reference_tokens)
|
|
||||||
{
|
|
||||||
// convert null values to arrays or objects before continuing
|
|
||||||
if (ptr->is_null())
|
|
||||||
{
|
|
||||||
// check if reference token is a number
|
|
||||||
const bool nums =
|
|
||||||
std::all_of(reference_token.begin(), reference_token.end(),
|
|
||||||
[](const unsigned char x)
|
|
||||||
{
|
|
||||||
return std::isdigit(x);
|
|
||||||
});
|
|
||||||
|
|
||||||
// change value to array for numbers or "-" or to object otherwise
|
|
||||||
*ptr = (nums || reference_token == "-")
|
|
||||||
? detail::value_t::array
|
|
||||||
: detail::value_t::object;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (ptr->type())
|
|
||||||
{
|
|
||||||
case detail::value_t::object:
|
|
||||||
{
|
|
||||||
// use unchecked object access
|
|
||||||
ptr = &ptr->operator[](reference_token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::array:
|
|
||||||
{
|
|
||||||
if (reference_token == "-")
|
|
||||||
{
|
|
||||||
// explicitly treat "-" as index beyond the end
|
|
||||||
ptr = &ptr->operator[](ptr->m_value.array->size());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// convert array index to number; unchecked access
|
|
||||||
ptr = &ptr->operator[](array_index(reference_token));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::null:
|
|
||||||
case detail::value_t::string:
|
|
||||||
case detail::value_t::boolean:
|
|
||||||
case detail::value_t::number_integer:
|
|
||||||
case detail::value_t::number_unsigned:
|
|
||||||
case detail::value_t::number_float:
|
|
||||||
case detail::value_t::binary:
|
|
||||||
case detail::value_t::discarded:
|
|
||||||
default:
|
|
||||||
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@throw parse_error.106 if an array index begins with '0'
|
|
||||||
@throw parse_error.109 if an array index was not a number
|
|
||||||
@throw out_of_range.402 if the array index '-' is used
|
|
||||||
@throw out_of_range.404 if the JSON pointer can not be resolved
|
|
||||||
*/
|
|
||||||
BasicJsonType& get_checked(BasicJsonType* ptr) const
|
|
||||||
{
|
|
||||||
for (const auto& reference_token : reference_tokens)
|
|
||||||
{
|
|
||||||
switch (ptr->type())
|
|
||||||
{
|
|
||||||
case detail::value_t::object:
|
|
||||||
{
|
|
||||||
// note: at performs range check
|
|
||||||
ptr = &ptr->at(reference_token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::array:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
|
||||||
{
|
|
||||||
// "-" always fails the range check
|
|
||||||
JSON_THROW(detail::out_of_range::create(402,
|
|
||||||
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
|
|
||||||
") is out of range", *ptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: at performs range check
|
|
||||||
ptr = &ptr->at(array_index(reference_token));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::null:
|
|
||||||
case detail::value_t::string:
|
|
||||||
case detail::value_t::boolean:
|
|
||||||
case detail::value_t::number_integer:
|
|
||||||
case detail::value_t::number_unsigned:
|
|
||||||
case detail::value_t::number_float:
|
|
||||||
case detail::value_t::binary:
|
|
||||||
case detail::value_t::discarded:
|
|
||||||
default:
|
|
||||||
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief return a const reference to the pointed to value
|
|
||||||
|
|
||||||
@param[in] ptr a JSON value
|
|
||||||
|
|
||||||
@return const reference to the JSON value pointed to by the JSON
|
|
||||||
pointer
|
|
||||||
|
|
||||||
@throw parse_error.106 if an array index begins with '0'
|
|
||||||
@throw parse_error.109 if an array index was not a number
|
|
||||||
@throw out_of_range.402 if the array index '-' is used
|
|
||||||
@throw out_of_range.404 if the JSON pointer can not be resolved
|
|
||||||
*/
|
|
||||||
const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
|
|
||||||
{
|
|
||||||
for (const auto& reference_token : reference_tokens)
|
|
||||||
{
|
|
||||||
switch (ptr->type())
|
|
||||||
{
|
|
||||||
case detail::value_t::object:
|
|
||||||
{
|
|
||||||
// use unchecked object access
|
|
||||||
ptr = &ptr->operator[](reference_token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::array:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
|
||||||
{
|
|
||||||
// "-" cannot be used for const access
|
|
||||||
JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// use unchecked array access
|
|
||||||
ptr = &ptr->operator[](array_index(reference_token));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::null:
|
|
||||||
case detail::value_t::string:
|
|
||||||
case detail::value_t::boolean:
|
|
||||||
case detail::value_t::number_integer:
|
|
||||||
case detail::value_t::number_unsigned:
|
|
||||||
case detail::value_t::number_float:
|
|
||||||
case detail::value_t::binary:
|
|
||||||
case detail::value_t::discarded:
|
|
||||||
default:
|
|
||||||
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@throw parse_error.106 if an array index begins with '0'
|
|
||||||
@throw parse_error.109 if an array index was not a number
|
|
||||||
@throw out_of_range.402 if the array index '-' is used
|
|
||||||
@throw out_of_range.404 if the JSON pointer can not be resolved
|
|
||||||
*/
|
|
||||||
const BasicJsonType& get_checked(const BasicJsonType* ptr) const
|
|
||||||
{
|
|
||||||
for (const auto& reference_token : reference_tokens)
|
|
||||||
{
|
|
||||||
switch (ptr->type())
|
|
||||||
{
|
|
||||||
case detail::value_t::object:
|
|
||||||
{
|
|
||||||
// note: at performs range check
|
|
||||||
ptr = &ptr->at(reference_token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::array:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
|
||||||
{
|
|
||||||
// "-" always fails the range check
|
|
||||||
JSON_THROW(detail::out_of_range::create(402,
|
|
||||||
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
|
|
||||||
") is out of range", *ptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: at performs range check
|
|
||||||
ptr = &ptr->at(array_index(reference_token));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::null:
|
|
||||||
case detail::value_t::string:
|
|
||||||
case detail::value_t::boolean:
|
|
||||||
case detail::value_t::number_integer:
|
|
||||||
case detail::value_t::number_unsigned:
|
|
||||||
case detail::value_t::number_float:
|
|
||||||
case detail::value_t::binary:
|
|
||||||
case detail::value_t::discarded:
|
|
||||||
default:
|
|
||||||
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@throw parse_error.106 if an array index begins with '0'
|
|
||||||
@throw parse_error.109 if an array index was not a number
|
|
||||||
*/
|
|
||||||
bool contains(const BasicJsonType* ptr) const
|
|
||||||
{
|
|
||||||
for (const auto& reference_token : reference_tokens)
|
|
||||||
{
|
|
||||||
switch (ptr->type())
|
|
||||||
{
|
|
||||||
case detail::value_t::object:
|
|
||||||
{
|
|
||||||
if (!ptr->contains(reference_token))
|
|
||||||
{
|
|
||||||
// we did not find the key in the object
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr = &ptr->operator[](reference_token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::array:
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
|
||||||
{
|
|
||||||
// "-" always fails the range check
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
|
|
||||||
{
|
|
||||||
// invalid char
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))
|
|
||||||
{
|
|
||||||
// first char should be between '1' and '9'
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (std::size_t i = 1; i < reference_token.size(); i++)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))
|
|
||||||
{
|
|
||||||
// other char should be between '0' and '9'
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto idx = array_index(reference_token);
|
|
||||||
if (idx >= ptr->size())
|
|
||||||
{
|
|
||||||
// index out of range
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr = &ptr->operator[](idx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::null:
|
|
||||||
case detail::value_t::string:
|
|
||||||
case detail::value_t::boolean:
|
|
||||||
case detail::value_t::number_integer:
|
|
||||||
case detail::value_t::number_unsigned:
|
|
||||||
case detail::value_t::number_float:
|
|
||||||
case detail::value_t::binary:
|
|
||||||
case detail::value_t::discarded:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
// we do not expect primitive values if there is still a
|
|
||||||
// reference token to process
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no reference token left means we found a primitive value
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief split the string input to reference tokens
|
|
||||||
|
|
||||||
@note This function is only called by the json_pointer constructor.
|
|
||||||
All exceptions below are documented there.
|
|
||||||
|
|
||||||
@throw parse_error.107 if the pointer is not empty or begins with '/'
|
|
||||||
@throw parse_error.108 if character '~' is not followed by '0' or '1'
|
|
||||||
*/
|
|
||||||
static std::vector<std::string> split(const std::string& reference_string)
|
|
||||||
{
|
|
||||||
std::vector<std::string> result;
|
|
||||||
|
|
||||||
// special case: empty reference string -> no reference tokens
|
|
||||||
if (reference_string.empty())
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if nonempty reference string begins with slash
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract the reference tokens:
|
|
||||||
// - slash: position of the last read slash (or end of string)
|
|
||||||
// - start: position after the previous slash
|
|
||||||
for (
|
|
||||||
// search for the first slash after the first character
|
|
||||||
std::size_t slash = reference_string.find_first_of('/', 1),
|
|
||||||
// set the beginning of the first reference token
|
|
||||||
start = 1;
|
|
||||||
// we can stop if start == 0 (if slash == std::string::npos)
|
|
||||||
start != 0;
|
|
||||||
// set the beginning of the next reference token
|
|
||||||
// (will eventually be 0 if slash == std::string::npos)
|
|
||||||
start = (slash == std::string::npos) ? 0 : slash + 1,
|
|
||||||
// find next slash
|
|
||||||
slash = reference_string.find_first_of('/', start))
|
|
||||||
{
|
|
||||||
// use the text between the beginning of the reference token
|
|
||||||
// (start) and the last slash (slash).
|
|
||||||
auto reference_token = reference_string.substr(start, slash - start);
|
|
||||||
|
|
||||||
// check reference tokens are properly escaped
|
|
||||||
for (std::size_t pos = reference_token.find_first_of('~');
|
|
||||||
pos != std::string::npos;
|
|
||||||
pos = reference_token.find_first_of('~', pos + 1))
|
|
||||||
{
|
|
||||||
JSON_ASSERT(reference_token[pos] == '~');
|
|
||||||
|
|
||||||
// ~ must be followed by 0 or 1
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||
|
|
||||||
(reference_token[pos + 1] != '0' &&
|
|
||||||
reference_token[pos + 1] != '1')))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// finally, store the reference token
|
|
||||||
detail::unescape(reference_token);
|
|
||||||
result.push_back(reference_token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
/*!
|
|
||||||
@param[in] reference_string the reference string to the current value
|
|
||||||
@param[in] value the value to consider
|
|
||||||
@param[in,out] result the result object to insert values to
|
|
||||||
|
|
||||||
@note Empty objects or arrays are flattened to `null`.
|
|
||||||
*/
|
|
||||||
static void flatten(const std::string& reference_string,
|
|
||||||
const BasicJsonType& value,
|
|
||||||
BasicJsonType& result)
|
|
||||||
{
|
|
||||||
switch (value.type())
|
|
||||||
{
|
|
||||||
case detail::value_t::array:
|
|
||||||
{
|
|
||||||
if (value.m_value.array->empty())
|
|
||||||
{
|
|
||||||
// flatten empty array as null
|
|
||||||
result[reference_string] = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// iterate array and use index as reference string
|
|
||||||
for (std::size_t i = 0; i < value.m_value.array->size(); ++i)
|
|
||||||
{
|
|
||||||
flatten(reference_string + "/" + std::to_string(i),
|
|
||||||
value.m_value.array->operator[](i), result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::object:
|
|
||||||
{
|
|
||||||
if (value.m_value.object->empty())
|
|
||||||
{
|
|
||||||
// flatten empty object as null
|
|
||||||
result[reference_string] = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// iterate object and use keys as reference string
|
|
||||||
for (const auto& element : *value.m_value.object)
|
|
||||||
{
|
|
||||||
flatten(reference_string + "/" + detail::escape(element.first), element.second, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case detail::value_t::null:
|
|
||||||
case detail::value_t::string:
|
|
||||||
case detail::value_t::boolean:
|
|
||||||
case detail::value_t::number_integer:
|
|
||||||
case detail::value_t::number_unsigned:
|
|
||||||
case detail::value_t::number_float:
|
|
||||||
case detail::value_t::binary:
|
|
||||||
case detail::value_t::discarded:
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
// add primitive value with its reference string
|
|
||||||
result[reference_string] = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@param[in] value flattened JSON
|
|
||||||
|
|
||||||
@return unflattened JSON
|
|
||||||
|
|
||||||
@throw parse_error.109 if array index is not a number
|
|
||||||
@throw type_error.314 if value is not an object
|
|
||||||
@throw type_error.315 if object values are not primitive
|
|
||||||
@throw type_error.313 if value cannot be unflattened
|
|
||||||
*/
|
|
||||||
static BasicJsonType
|
|
||||||
unflatten(const BasicJsonType& value)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value));
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicJsonType result;
|
|
||||||
|
|
||||||
// iterate the JSON object values
|
|
||||||
for (const auto& element : *value.m_value.object)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
|
|
||||||
{
|
|
||||||
JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second));
|
|
||||||
}
|
|
||||||
|
|
||||||
// assign value to reference pointed to by JSON pointer; Note that if
|
|
||||||
// the JSON pointer is "" (i.e., points to the whole value), function
|
|
||||||
// get_and_create returns a reference to result itself. An assignment
|
|
||||||
// will then create a primitive value.
|
|
||||||
json_pointer(element.first).get_and_create(result) = element.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief compares two JSON pointers for equality
|
|
||||||
|
|
||||||
@param[in] lhs JSON pointer to compare
|
|
||||||
@param[in] rhs JSON pointer to compare
|
|
||||||
@return whether @a lhs is equal to @a rhs
|
|
||||||
|
|
||||||
@complexity Linear in the length of the JSON pointer
|
|
||||||
|
|
||||||
@exceptionsafety No-throw guarantee: this function never throws exceptions.
|
|
||||||
*/
|
|
||||||
friend bool operator==(json_pointer const& lhs,
|
|
||||||
json_pointer const& rhs) noexcept
|
|
||||||
{
|
|
||||||
return lhs.reference_tokens == rhs.reference_tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief compares two JSON pointers for inequality
|
|
||||||
|
|
||||||
@param[in] lhs JSON pointer to compare
|
|
||||||
@param[in] rhs JSON pointer to compare
|
|
||||||
@return whether @a lhs is not equal @a rhs
|
|
||||||
|
|
||||||
@complexity Linear in the length of the JSON pointer
|
|
||||||
|
|
||||||
@exceptionsafety No-throw guarantee: this function never throws exceptions.
|
|
||||||
*/
|
|
||||||
friend bool operator!=(json_pointer const& lhs,
|
|
||||||
json_pointer const& rhs) noexcept
|
|
||||||
{
|
|
||||||
return !(lhs == rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// the reference tokens
|
|
||||||
std::vector<std::string> reference_tokens;
|
|
||||||
};
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <initializer_list>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
|
||||||
|
|
||||||
namespace nlohmann
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
class json_ref
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using value_type = BasicJsonType;
|
|
||||||
|
|
||||||
json_ref(value_type&& value)
|
|
||||||
: owned_value(std::move(value))
|
|
||||||
{}
|
|
||||||
|
|
||||||
json_ref(const value_type& value)
|
|
||||||
: value_ref(&value)
|
|
||||||
{}
|
|
||||||
|
|
||||||
json_ref(std::initializer_list<json_ref> init)
|
|
||||||
: owned_value(init)
|
|
||||||
{}
|
|
||||||
|
|
||||||
template <
|
|
||||||
class... Args,
|
|
||||||
enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >
|
|
||||||
json_ref(Args && ... args)
|
|
||||||
: owned_value(std::forward<Args>(args)...)
|
|
||||||
{}
|
|
||||||
|
|
||||||
// class should be movable only
|
|
||||||
json_ref(json_ref&&) noexcept = default;
|
|
||||||
json_ref(const json_ref&) = delete;
|
|
||||||
json_ref& operator=(const json_ref&) = delete;
|
|
||||||
json_ref& operator=(json_ref&&) = delete;
|
|
||||||
~json_ref() = default;
|
|
||||||
|
|
||||||
value_type moved_or_copied() const
|
|
||||||
{
|
|
||||||
if (value_ref == nullptr)
|
|
||||||
{
|
|
||||||
return std::move(owned_value);
|
|
||||||
}
|
|
||||||
return *value_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
value_type const& operator*() const
|
|
||||||
{
|
|
||||||
return value_ref ? *value_ref : owned_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
value_type const* operator->() const
|
|
||||||
{
|
|
||||||
return &** this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
mutable value_type owned_value = nullptr;
|
|
||||||
value_type const* value_ref = nullptr;
|
|
||||||
};
|
|
||||||
} // namespace detail
|
|
||||||
} // namespace nlohmann
|
|
||||||
@@ -1,407 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utility> // declval, pair
|
|
||||||
#include <nlohmann/thirdparty/hedley/hedley.hpp>
|
|
||||||
#include <nlohmann/detail/meta/detected.hpp>
|
|
||||||
|
|
||||||
// This file contains all internal macro definitions
|
|
||||||
// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
|
|
||||||
|
|
||||||
// exclude unsupported compilers
|
|
||||||
#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)
|
|
||||||
#if defined(__clang__)
|
|
||||||
#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
|
|
||||||
#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
|
|
||||||
#endif
|
|
||||||
#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
|
|
||||||
#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800
|
|
||||||
#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// C++ language standard detection
|
|
||||||
// if the user manually specified the used c++ version this is skipped
|
|
||||||
#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
|
|
||||||
#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
|
|
||||||
#define JSON_HAS_CPP_20
|
|
||||||
#define JSON_HAS_CPP_17
|
|
||||||
#define JSON_HAS_CPP_14
|
|
||||||
#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
|
|
||||||
#define JSON_HAS_CPP_17
|
|
||||||
#define JSON_HAS_CPP_14
|
|
||||||
#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
|
|
||||||
#define JSON_HAS_CPP_14
|
|
||||||
#endif
|
|
||||||
// the cpp 11 flag is always specified because it is the minimal required version
|
|
||||||
#define JSON_HAS_CPP_11
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM)
|
|
||||||
#ifdef JSON_HAS_CPP_17
|
|
||||||
#if defined(__cpp_lib_filesystem)
|
|
||||||
#define JSON_HAS_FILESYSTEM 1
|
|
||||||
#elif defined(__cpp_lib_experimental_filesystem)
|
|
||||||
#define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
|
|
||||||
#elif !defined(__has_include)
|
|
||||||
#define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
|
|
||||||
#elif __has_include(<filesystem>)
|
|
||||||
#define JSON_HAS_FILESYSTEM 1
|
|
||||||
#elif __has_include(<experimental/filesystem>)
|
|
||||||
#define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/
|
|
||||||
#if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8
|
|
||||||
#undef JSON_HAS_FILESYSTEM
|
|
||||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support
|
|
||||||
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8
|
|
||||||
#undef JSON_HAS_FILESYSTEM
|
|
||||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support
|
|
||||||
#if defined(__clang_major__) && __clang_major__ < 7
|
|
||||||
#undef JSON_HAS_FILESYSTEM
|
|
||||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support
|
|
||||||
#if defined(_MSC_VER) && _MSC_VER < 1940
|
|
||||||
#undef JSON_HAS_FILESYSTEM
|
|
||||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// no filesystem support before iOS 13
|
|
||||||
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000
|
|
||||||
#undef JSON_HAS_FILESYSTEM
|
|
||||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// no filesystem support before macOS Catalina
|
|
||||||
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
|
|
||||||
#undef JSON_HAS_FILESYSTEM
|
|
||||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
#define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef JSON_HAS_FILESYSTEM
|
|
||||||
#define JSON_HAS_FILESYSTEM 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// disable documentation warnings on clang
|
|
||||||
#if defined(__clang__)
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wdocumentation"
|
|
||||||
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// allow disabling exceptions
|
|
||||||
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
|
|
||||||
#define JSON_THROW(exception) throw exception
|
|
||||||
#define JSON_TRY try
|
|
||||||
#define JSON_CATCH(exception) catch(exception)
|
|
||||||
#define JSON_INTERNAL_CATCH(exception) catch(exception)
|
|
||||||
#else
|
|
||||||
#include <cstdlib>
|
|
||||||
#define JSON_THROW(exception) std::abort()
|
|
||||||
#define JSON_TRY if(true)
|
|
||||||
#define JSON_CATCH(exception) if(false)
|
|
||||||
#define JSON_INTERNAL_CATCH(exception) if(false)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// override exception macros
|
|
||||||
#if defined(JSON_THROW_USER)
|
|
||||||
#undef JSON_THROW
|
|
||||||
#define JSON_THROW JSON_THROW_USER
|
|
||||||
#endif
|
|
||||||
#if defined(JSON_TRY_USER)
|
|
||||||
#undef JSON_TRY
|
|
||||||
#define JSON_TRY JSON_TRY_USER
|
|
||||||
#endif
|
|
||||||
#if defined(JSON_CATCH_USER)
|
|
||||||
#undef JSON_CATCH
|
|
||||||
#define JSON_CATCH JSON_CATCH_USER
|
|
||||||
#undef JSON_INTERNAL_CATCH
|
|
||||||
#define JSON_INTERNAL_CATCH JSON_CATCH_USER
|
|
||||||
#endif
|
|
||||||
#if defined(JSON_INTERNAL_CATCH_USER)
|
|
||||||
#undef JSON_INTERNAL_CATCH
|
|
||||||
#define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// allow overriding assert
|
|
||||||
#if !defined(JSON_ASSERT)
|
|
||||||
#include <cassert> // assert
|
|
||||||
#define JSON_ASSERT(x) assert(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// allow to access some private functions (needed by the test suite)
|
|
||||||
#if defined(JSON_TESTS_PRIVATE)
|
|
||||||
#define JSON_PRIVATE_UNLESS_TESTED public
|
|
||||||
#else
|
|
||||||
#define JSON_PRIVATE_UNLESS_TESTED private
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief macro to briefly define a mapping between an enum and JSON
|
|
||||||
@def NLOHMANN_JSON_SERIALIZE_ENUM
|
|
||||||
@since version 3.4.0
|
|
||||||
*/
|
|
||||||
#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \
|
|
||||||
template<typename BasicJsonType> \
|
|
||||||
inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \
|
|
||||||
{ \
|
|
||||||
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
|
|
||||||
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
|
|
||||||
auto it = std::find_if(std::begin(m), std::end(m), \
|
|
||||||
[e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
|
|
||||||
{ \
|
|
||||||
return ej_pair.first == e; \
|
|
||||||
}); \
|
|
||||||
j = ((it != std::end(m)) ? it : std::begin(m))->second; \
|
|
||||||
} \
|
|
||||||
template<typename BasicJsonType> \
|
|
||||||
inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
|
|
||||||
{ \
|
|
||||||
static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
|
|
||||||
static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
|
|
||||||
auto it = std::find_if(std::begin(m), std::end(m), \
|
|
||||||
[&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
|
|
||||||
{ \
|
|
||||||
return ej_pair.second == j; \
|
|
||||||
}); \
|
|
||||||
e = ((it != std::end(m)) ? it : std::begin(m))->first; \
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
|
||||||
// may be removed in the future once the class is split.
|
|
||||||
|
|
||||||
#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \
|
|
||||||
template<template<typename, typename, typename...> class ObjectType, \
|
|
||||||
template<typename, typename...> class ArrayType, \
|
|
||||||
class StringType, class BooleanType, class NumberIntegerType, \
|
|
||||||
class NumberUnsignedType, class NumberFloatType, \
|
|
||||||
template<typename> class AllocatorType, \
|
|
||||||
template<typename, typename = void> class JSONSerializer, \
|
|
||||||
class BinaryType>
|
|
||||||
|
|
||||||
#define NLOHMANN_BASIC_JSON_TPL \
|
|
||||||
basic_json<ObjectType, ArrayType, StringType, BooleanType, \
|
|
||||||
NumberIntegerType, NumberUnsignedType, NumberFloatType, \
|
|
||||||
AllocatorType, JSONSerializer, BinaryType>
|
|
||||||
|
|
||||||
// Macros to simplify conversion from/to types
|
|
||||||
|
|
||||||
#define NLOHMANN_JSON_EXPAND( x ) x
|
|
||||||
#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME
|
|
||||||
#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
|
|
||||||
NLOHMANN_JSON_PASTE64, \
|
|
||||||
NLOHMANN_JSON_PASTE63, \
|
|
||||||
NLOHMANN_JSON_PASTE62, \
|
|
||||||
NLOHMANN_JSON_PASTE61, \
|
|
||||||
NLOHMANN_JSON_PASTE60, \
|
|
||||||
NLOHMANN_JSON_PASTE59, \
|
|
||||||
NLOHMANN_JSON_PASTE58, \
|
|
||||||
NLOHMANN_JSON_PASTE57, \
|
|
||||||
NLOHMANN_JSON_PASTE56, \
|
|
||||||
NLOHMANN_JSON_PASTE55, \
|
|
||||||
NLOHMANN_JSON_PASTE54, \
|
|
||||||
NLOHMANN_JSON_PASTE53, \
|
|
||||||
NLOHMANN_JSON_PASTE52, \
|
|
||||||
NLOHMANN_JSON_PASTE51, \
|
|
||||||
NLOHMANN_JSON_PASTE50, \
|
|
||||||
NLOHMANN_JSON_PASTE49, \
|
|
||||||
NLOHMANN_JSON_PASTE48, \
|
|
||||||
NLOHMANN_JSON_PASTE47, \
|
|
||||||
NLOHMANN_JSON_PASTE46, \
|
|
||||||
NLOHMANN_JSON_PASTE45, \
|
|
||||||
NLOHMANN_JSON_PASTE44, \
|
|
||||||
NLOHMANN_JSON_PASTE43, \
|
|
||||||
NLOHMANN_JSON_PASTE42, \
|
|
||||||
NLOHMANN_JSON_PASTE41, \
|
|
||||||
NLOHMANN_JSON_PASTE40, \
|
|
||||||
NLOHMANN_JSON_PASTE39, \
|
|
||||||
NLOHMANN_JSON_PASTE38, \
|
|
||||||
NLOHMANN_JSON_PASTE37, \
|
|
||||||
NLOHMANN_JSON_PASTE36, \
|
|
||||||
NLOHMANN_JSON_PASTE35, \
|
|
||||||
NLOHMANN_JSON_PASTE34, \
|
|
||||||
NLOHMANN_JSON_PASTE33, \
|
|
||||||
NLOHMANN_JSON_PASTE32, \
|
|
||||||
NLOHMANN_JSON_PASTE31, \
|
|
||||||
NLOHMANN_JSON_PASTE30, \
|
|
||||||
NLOHMANN_JSON_PASTE29, \
|
|
||||||
NLOHMANN_JSON_PASTE28, \
|
|
||||||
NLOHMANN_JSON_PASTE27, \
|
|
||||||
NLOHMANN_JSON_PASTE26, \
|
|
||||||
NLOHMANN_JSON_PASTE25, \
|
|
||||||
NLOHMANN_JSON_PASTE24, \
|
|
||||||
NLOHMANN_JSON_PASTE23, \
|
|
||||||
NLOHMANN_JSON_PASTE22, \
|
|
||||||
NLOHMANN_JSON_PASTE21, \
|
|
||||||
NLOHMANN_JSON_PASTE20, \
|
|
||||||
NLOHMANN_JSON_PASTE19, \
|
|
||||||
NLOHMANN_JSON_PASTE18, \
|
|
||||||
NLOHMANN_JSON_PASTE17, \
|
|
||||||
NLOHMANN_JSON_PASTE16, \
|
|
||||||
NLOHMANN_JSON_PASTE15, \
|
|
||||||
NLOHMANN_JSON_PASTE14, \
|
|
||||||
NLOHMANN_JSON_PASTE13, \
|
|
||||||
NLOHMANN_JSON_PASTE12, \
|
|
||||||
NLOHMANN_JSON_PASTE11, \
|
|
||||||
NLOHMANN_JSON_PASTE10, \
|
|
||||||
NLOHMANN_JSON_PASTE9, \
|
|
||||||
NLOHMANN_JSON_PASTE8, \
|
|
||||||
NLOHMANN_JSON_PASTE7, \
|
|
||||||
NLOHMANN_JSON_PASTE6, \
|
|
||||||
NLOHMANN_JSON_PASTE5, \
|
|
||||||
NLOHMANN_JSON_PASTE4, \
|
|
||||||
NLOHMANN_JSON_PASTE3, \
|
|
||||||
NLOHMANN_JSON_PASTE2, \
|
|
||||||
NLOHMANN_JSON_PASTE1)(__VA_ARGS__))
|
|
||||||
#define NLOHMANN_JSON_PASTE2(func, v1) func(v1)
|
|
||||||
#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)
|
|
||||||
#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)
|
|
||||||
#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)
|
|
||||||
#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)
|
|
||||||
#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)
|
|
||||||
#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)
|
|
||||||
#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)
|
|
||||||
#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)
|
|
||||||
#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)
|
|
||||||
#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
|
|
||||||
#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)
|
|
||||||
#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)
|
|
||||||
#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)
|
|
||||||
#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)
|
|
||||||
#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)
|
|
||||||
#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)
|
|
||||||
#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)
|
|
||||||
#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)
|
|
||||||
#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
|
|
||||||
#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)
|
|
||||||
#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)
|
|
||||||
#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)
|
|
||||||
#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)
|
|
||||||
#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)
|
|
||||||
#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)
|
|
||||||
#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)
|
|
||||||
#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)
|
|
||||||
#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)
|
|
||||||
#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)
|
|
||||||
#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)
|
|
||||||
#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)
|
|
||||||
#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)
|
|
||||||
#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)
|
|
||||||
#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)
|
|
||||||
#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)
|
|
||||||
#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)
|
|
||||||
#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)
|
|
||||||
#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)
|
|
||||||
#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)
|
|
||||||
#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)
|
|
||||||
#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)
|
|
||||||
#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)
|
|
||||||
#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)
|
|
||||||
#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)
|
|
||||||
#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)
|
|
||||||
#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)
|
|
||||||
#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)
|
|
||||||
#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)
|
|
||||||
#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)
|
|
||||||
#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)
|
|
||||||
#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)
|
|
||||||
#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)
|
|
||||||
#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)
|
|
||||||
#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)
|
|
||||||
#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)
|
|
||||||
#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)
|
|
||||||
#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)
|
|
||||||
#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)
|
|
||||||
#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)
|
|
||||||
#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)
|
|
||||||
#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)
|
|
||||||
#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)
|
|
||||||
|
|
||||||
#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
|
|
||||||
#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief macro
|
|
||||||
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
|
|
||||||
@since version 3.9.0
|
|
||||||
*/
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \
|
|
||||||
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
|
||||||
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief macro
|
|
||||||
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
|
|
||||||
@since version 3.9.0
|
|
||||||
*/
|
|
||||||
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
|
|
||||||
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
|
|
||||||
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
|
|
||||||
|
|
||||||
|
|
||||||
// inspired from https://stackoverflow.com/a/26745591
|
|
||||||
// allows to call any std function as if (e.g. with begin):
|
|
||||||
// using std::begin; begin(x);
|
|
||||||
//
|
|
||||||
// it allows using the detected idiom to retrieve the return type
|
|
||||||
// of such an expression
|
|
||||||
#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \
|
|
||||||
namespace detail { \
|
|
||||||
using std::std_name; \
|
|
||||||
\
|
|
||||||
template<typename... T> \
|
|
||||||
using result_of_##std_name = decltype(std_name(std::declval<T>()...)); \
|
|
||||||
} \
|
|
||||||
\
|
|
||||||
namespace detail2 { \
|
|
||||||
struct std_name##_tag \
|
|
||||||
{ \
|
|
||||||
}; \
|
|
||||||
\
|
|
||||||
template<typename... T> \
|
|
||||||
std_name##_tag std_name(T&&...); \
|
|
||||||
\
|
|
||||||
template<typename... T> \
|
|
||||||
using result_of_##std_name = decltype(std_name(std::declval<T>()...)); \
|
|
||||||
\
|
|
||||||
template<typename... T> \
|
|
||||||
struct would_call_std_##std_name \
|
|
||||||
{ \
|
|
||||||
static constexpr auto const value = ::nlohmann::detail:: \
|
|
||||||
is_detected_exact<std_name##_tag, result_of_##std_name, T...>::value; \
|
|
||||||
}; \
|
|
||||||
} /* namespace detail2 */ \
|
|
||||||
\
|
|
||||||
template<typename... T> \
|
|
||||||
struct would_call_std_##std_name : detail2::would_call_std_##std_name<T...> \
|
|
||||||
{ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef JSON_USE_IMPLICIT_CONVERSIONS
|
|
||||||
#define JSON_USE_IMPLICIT_CONVERSIONS 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
||||||
#define JSON_EXPLICIT
|
|
||||||
#else
|
|
||||||
#define JSON_EXPLICIT explicit
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef JSON_DIAGNOSTICS
|
|
||||||
#define JSON_DIAGNOSTICS 0
|
|
||||||
#endif
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// restore clang diagnostic settings
|
|
||||||
#if defined(__clang__)
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// clean up
|
|
||||||
#undef JSON_ASSERT
|
|
||||||
#undef JSON_INTERNAL_CATCH
|
|
||||||
#undef JSON_CATCH
|
|
||||||
#undef JSON_THROW
|
|
||||||
#undef JSON_TRY
|
|
||||||
#undef JSON_PRIVATE_UNLESS_TESTED
|
|
||||||
#undef JSON_HAS_CPP_11
|
|
||||||
#undef JSON_HAS_CPP_14
|
|
||||||
#undef JSON_HAS_CPP_17
|
|
||||||
#undef JSON_HAS_CPP_20
|
|
||||||
#undef JSON_HAS_FILESYSTEM
|
|
||||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
|
|
||||||
#undef NLOHMANN_BASIC_JSON_TPL
|
|
||||||
#undef JSON_EXPLICIT
|
|
||||||
#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL
|
|
||||||
|
|
||||||
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user