Rewrote the server in cpp with the frontend in svelte
This commit is contained in:
commit
03b22ebb61
171
.gitignore
vendored
Normal file
171
.gitignore
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/clion,cmake,c++
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=clion,cmake,c++
|
||||
|
||||
### C++ ###
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
### CLion ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### CLion Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
# https://plugins.jetbrains.com/plugin/7973-sonarlint
|
||||
.idea/**/sonarlint/
|
||||
|
||||
# SonarQube Plugin
|
||||
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
|
||||
.idea/**/sonarIssues.xml
|
||||
|
||||
# Markdown Navigator plugin
|
||||
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
|
||||
.idea/**/markdown-navigator.xml
|
||||
.idea/**/markdown-navigator-enh.xml
|
||||
.idea/**/markdown-navigator/
|
||||
|
||||
# Cache file creation bug
|
||||
# See https://youtrack.jetbrains.com/issue/JBR-2257
|
||||
.idea/$CACHE_FILE$
|
||||
|
||||
# CodeStream plugin
|
||||
# https://plugins.jetbrains.com/plugin/12206-codestream
|
||||
.idea/codestream.xml
|
||||
|
||||
# Azure Toolkit for IntelliJ plugin
|
||||
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
|
||||
.idea/**/azureSettings.xml
|
||||
|
||||
### CMake ###
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
|
||||
### CMake Patch ###
|
||||
# External projects
|
||||
*-prefix/
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/clion,cmake,c++
|
||||
|
||||
/mrpc
|
||||
|
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
2
.idea/fileserver.iml
Normal file
2
.idea/fileserver.iml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
14
.idea/misc.xml
Normal file
14
.idea/misc.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
<component name="CidrRootsConfiguration">
|
||||
<libraryRoots>
|
||||
<file path="$PROJECT_DIR$/argon2" />
|
||||
<file path="$PROJECT_DIR$/lib" />
|
||||
<file path="$PROJECT_DIR$/lib/Botan-3.2.0" />
|
||||
<file path="$PROJECT_DIR$/lib/plog-1.1.10" />
|
||||
<file path="$PROJECT_DIR$/lib/restbed-4.8" />
|
||||
<file path="$PROJECT_DIR$/phc-winner-argon2-20190702" />
|
||||
</libraryRoots>
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/fileserver.iml" filepath="$PROJECT_DIR$/.idea/fileserver.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
67
CMakeLists.txt
Normal file
67
CMakeLists.txt
Normal file
@ -0,0 +1,67 @@
|
||||
cmake_minimum_required(VERSION 3.26)
|
||||
project(fileserver)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
|
||||
add_subdirectory(lib EXCLUDE_FROM_ALL)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
add_custom_command(
|
||||
COMMAND ./mrpc ARGS -n src/server/mrpc/fileserver -s cpp fileserver.rs
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
MAIN_DEPENDENCY fileserver.rs
|
||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/server/mrpc/fileserver.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/server/mrpc/fileserver.hxx
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
COMMAND sh ARGS -c 'xxd -i -n index_html ${CMAKE_CURRENT_SOURCE_DIR}/frontend/dist/index.html > index_html.h'
|
||||
MAIN_DEPENDENCY frontend/dist/index.html
|
||||
OUTPUT index_html.h
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
COMMAND sh ARGS -c 'xxd -i -n favicon_svg ${CMAKE_CURRENT_SOURCE_DIR}/frontend/dist/favicon.svg > favicon_svg.h'
|
||||
MAIN_DEPENDENCY frontend/dist/favicon.svg
|
||||
OUTPUT favicon_svg.h
|
||||
)
|
||||
|
||||
add_executable(fileserver
|
||||
src/server/mrpc/fileserver.hxx
|
||||
src/server/mrpc/fileserver.cxx
|
||||
|
||||
src/util/logging.hxx
|
||||
src/util/crash.hxx
|
||||
src/util/timed_mutex.hxx
|
||||
src/util/stb.cxx
|
||||
|
||||
src/data/data.hxx
|
||||
src/data/data_internal.hxx
|
||||
src/data/data.cxx
|
||||
src/data/data_load.cxx
|
||||
src/data/data_save.cxx
|
||||
src/data/data_validate.cxx
|
||||
|
||||
src/server/server.hxx
|
||||
src/server/server_internal.hxx
|
||||
src/server/server.cxx
|
||||
src/server/admin.cxx
|
||||
src/server/auth.cxx
|
||||
src/server/mail.cxx
|
||||
src/server/fs.cxx
|
||||
src/server/download.cxx
|
||||
src/server/upload.cxx
|
||||
|
||||
src/main.cxx
|
||||
|
||||
index_html.h
|
||||
favicon_svg.h
|
||||
)
|
||||
|
||||
target_include_directories(fileserver PRIVATE include ${CMAKE_CURRENT_BINARY_DIR})
|
||||
target_link_libraries(fileserver PRIVATE fileserver_libs Threads::Threads)
|
||||
target_compile_options(fileserver PRIVATE -msse2)
|
||||
target_link_options(fileserver PRIVATE -static)
|
||||
|
||||
install(TARGETS fileserver)
|
98
fileserver.rs
Normal file
98
fileserver.rs
Normal file
@ -0,0 +1,98 @@
|
||||
/* Missing:
|
||||
download
|
||||
download_zip
|
||||
upload
|
||||
*/
|
||||
|
||||
struct Response<T> {
|
||||
e: Option<String>,
|
||||
o: Option<T>
|
||||
}
|
||||
|
||||
struct LoginResponse {
|
||||
otp_needed: bool,
|
||||
token: Option<String>
|
||||
}
|
||||
|
||||
struct Session {
|
||||
name: String,
|
||||
tfa_enabled: bool,
|
||||
admin: bool,
|
||||
sudo: bool
|
||||
}
|
||||
|
||||
trait Auth {
|
||||
fn signup(username: String, password: String) -> Option<String>;
|
||||
fn login(username: String, password: String, otp: Option<String>) -> Response<LoginResponse>;
|
||||
fn send_recovery_key(username: String);
|
||||
fn reset_password(key: String, password: String) -> Option<String>;
|
||||
fn change_password(token: String, old_pw: String, new_pw: String) -> Option<String>;
|
||||
fn logout(token: String);
|
||||
fn logout_all(token: String) -> Option<String>;
|
||||
fn tfa_setup_mail(token: String) -> Option<String>;
|
||||
fn tfa_setup_totp(token: String) -> Response<String>;
|
||||
fn tfa_complete(token: String, otp: String) -> Option<String>;
|
||||
fn tfa_disable(token: String) -> Option<String>;
|
||||
fn delete_user(token: String) -> Option<String>;
|
||||
fn session_info(token: String) -> Response<Session>;
|
||||
}
|
||||
|
||||
struct UserInfo {
|
||||
id: u64,
|
||||
name: String,
|
||||
tfa: bool,
|
||||
admin: bool,
|
||||
enabled: bool
|
||||
}
|
||||
|
||||
trait Admin {
|
||||
fn list_users(token: String) -> Response<[UserInfo]>;
|
||||
fn delete_user(token: String, user: u64) -> Option<String>;
|
||||
fn logout(token: String, user: u64) -> Option<String>;
|
||||
fn disable_tfa(token: String, user: u64) -> Option<String>;
|
||||
fn set_admin(token: String, user: u64, admin: bool) -> Option<String>;
|
||||
fn set_enabled(token: String, user: u64, enabled: bool) -> Option<String>;
|
||||
fn sudo(token: String, user: u64) -> Option<String>;
|
||||
fn unsudo(token: String) -> Option<String>;
|
||||
fn shutdown(token: String) -> Option<String>;
|
||||
}
|
||||
|
||||
struct Node {
|
||||
id: u64,
|
||||
name: String,
|
||||
file: bool,
|
||||
preview: bool,
|
||||
parent: Option<u64>,
|
||||
size: Option<u64>,
|
||||
children: Option<[Node]>
|
||||
}
|
||||
|
||||
struct CreateNodeInfo {
|
||||
id: u64,
|
||||
exists: bool,
|
||||
file: bool
|
||||
}
|
||||
|
||||
struct ZipInfo {
|
||||
done: bool,
|
||||
progress: u64,
|
||||
total: u64
|
||||
}
|
||||
|
||||
struct PathSegment {
|
||||
name: String,
|
||||
id: Option<u64>
|
||||
}
|
||||
|
||||
trait FS {
|
||||
fn get_node(token: String, node: u64) -> Response<Node>;
|
||||
fn get_path(token: String, node: u64) -> Response<[PathSegment]>;
|
||||
fn get_nodes_size(token: String, nodes: [u64]) -> Response<u64>;
|
||||
fn create_node(token: String, file: bool, parent: u64, name: String) -> Response<CreateNodeInfo>;
|
||||
fn move_nodes(token: String, nodes: [u64], parent: u64) -> Option<String>;
|
||||
fn delete_nodes(token: String, nodes: [u64]) -> Iterator<String>;
|
||||
//fn create_zip(token: String, nodes: [u64]) -> Response<u64>;
|
||||
//fn zip_progress(token: String, zip_id: u64) -> Response<ZipInfo>;
|
||||
fn download_preview(token: String, node: u64) -> Response<String>;
|
||||
fn get_mime(token: String, node: u64) -> Response<String>;
|
||||
}
|
24
frontend/.gitignore
vendored
Normal file
24
frontend/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
3
frontend/README.md
Normal file
3
frontend/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
https://github.com/kpulkit29/svelte-tree-viewer
|
||||
|
||||
|
13
frontend/index.html
Normal file
13
frontend/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>MFileserver</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
3097
frontend/package-lock.json
generated
Normal file
3097
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
39
frontend/package.json
Normal file
39
frontend/package.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^2.4.2",
|
||||
"@tsconfig/svelte": "^5.0.0",
|
||||
"@types/node": "^20.8.6",
|
||||
"@types/qrcode-svg": "^1.1.2",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"flowbite": "^1.8.1",
|
||||
"flowbite-svelte": "^0.44.18",
|
||||
"postcss": "^8.4.24",
|
||||
"postcss-load-config": "^4.0.1",
|
||||
"svelte": "^4.0.5",
|
||||
"svelte-check": "^3.4.6",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"tslib": "^2.6.0",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.4.5",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-singlefile": "^0.13.5",
|
||||
"vite-plugin-tailwind-purgecss": "^0.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/fetch-event-source": "^2.0.1",
|
||||
"carbon-icons-svelte": "^12.3.0",
|
||||
"filesize": "^10.1.0",
|
||||
"qrcode-svg": "^1.1.0",
|
||||
"tailwind-merge": "^1.14.0"
|
||||
}
|
||||
}
|
13
frontend/postcss.config.cjs
Normal file
13
frontend/postcss.config.cjs
Normal file
@ -0,0 +1,13 @@
|
||||
const tailwindcss = require("tailwindcss");
|
||||
const autoprefixer = require("autoprefixer");
|
||||
|
||||
const config = {
|
||||
plugins: [
|
||||
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
|
||||
tailwindcss(),
|
||||
//But others, like autoprefixer, need to run after,
|
||||
autoprefixer,
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = config;
|
1
frontend/public/favicon.svg
Normal file
1
frontend/public/favicon.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"><path d="M28 20h-2v2h2v6H4v-6h2v-2H4a2.002 2.002 0 0 0-2 2v6a2.002 2.002 0 0 0 2 2h24a2.002 2.002 0 0 0 2-2v-6a2.002 2.002 0 0 0-2-2z" fill="currentColor"></path><circle cx="7" cy="25" r="1" fill="currentColor"></circle><path d="M22.707 7.293l-5-5A1 1 0 0 0 17 2h-6a2.002 2.002 0 0 0-2 2v16a2.002 2.002 0 0 0 2 2h10a2.002 2.002 0 0 0 2-2V8a1 1 0 0 0-.293-.707zM20.586 8H17V4.414zM11 20V4h4v4a2.002 2.002 0 0 0 2 2h4v10z" fill="currentColor"></path></svg>
|
After Width: | Height: | Size: 557 B |
91
frontend/src/App.svelte
Normal file
91
frontend/src/App.svelte
Normal file
@ -0,0 +1,91 @@
|
||||
<script lang="ts">
|
||||
import {changeStateFunction, error_banner, info_banner, rpc, session, show_working, state, StateE, token, workingWrapperO} from './store';
|
||||
import {Banner, Navbar, Spinner} from 'flowbite-svelte';
|
||||
import {FileStorage} from 'carbon-icons-svelte';
|
||||
import LinkButton from './components/LinkButton.svelte';
|
||||
import Login from './pages/Login.svelte';
|
||||
import Signup from './pages/Signup.svelte';
|
||||
import ResetPassword from './pages/ResetPassword.svelte';
|
||||
import Profile from './pages/Profile.svelte';
|
||||
import TfaSetup from './pages/TfaSetup.svelte';
|
||||
import Admin from './pages/Admin.svelte';
|
||||
import View from './pages/View.svelte';
|
||||
|
||||
const s = session.s;
|
||||
|
||||
function homeClick() {
|
||||
if ($token == null)
|
||||
$state.s = StateE.LOGIN;
|
||||
else
|
||||
$state = { s: StateE.VIEW, view_node: 0 };
|
||||
}
|
||||
|
||||
async function leaveSudo() {
|
||||
await workingWrapperO(() => rpc.Admin_unsudo($token ?? ''));
|
||||
await session.update($token);
|
||||
state.set({s: StateE.ADMIN, view_node: 0});
|
||||
}
|
||||
|
||||
function logout() {
|
||||
rpc.Auth_logout($token ?? '');
|
||||
token.set(null);
|
||||
}
|
||||
|
||||
homeClick();
|
||||
</script>
|
||||
|
||||
<main class="h-screen w-screen p-4 flex flex-col">
|
||||
{#if $error_banner.length > 0}
|
||||
<Banner dismissable position="absolute" divClass="z-10 flex justify-between p-4 !bg-red-50" on:close={() => error_banner.set('')}>
|
||||
{$error_banner}
|
||||
</Banner>
|
||||
{/if}
|
||||
{#if $info_banner.length > 0}
|
||||
<Banner dismissable position="absolute" on:close={() => info_banner.set('')}>
|
||||
{$info_banner}
|
||||
</Banner>
|
||||
{/if}
|
||||
{#if $show_working}
|
||||
<Banner position="absolute" dismissable={false}><Spinner size="5" class="mr-2" />Working</Banner>
|
||||
{/if}
|
||||
<Navbar class="flex-grow-0">
|
||||
<button on:click={homeClick} id="home-button" class="flex items-center">
|
||||
<FileStorage width="1.5em" height="1.5em"/>
|
||||
<span id="navbar-text">MFileserver</span>
|
||||
</button>
|
||||
|
||||
{#if $token != null}
|
||||
<div class="flex md:order-2">
|
||||
{#if $s?.sudo} <LinkButton on:click={leaveSudo}>Leave sudo</LinkButton> {/if}
|
||||
{#if $s?.admin} <LinkButton on:click={changeStateFunction(StateE.ADMIN)}>Admin</LinkButton> {/if}
|
||||
<LinkButton on:click={changeStateFunction(StateE.VIEW, 0)}>Files</LinkButton>
|
||||
<LinkButton on:click={changeStateFunction(StateE.PROFILE)}>Profile</LinkButton>
|
||||
<LinkButton on:click={logout}>Logout</LinkButton>
|
||||
</div>
|
||||
{/if}
|
||||
</Navbar>
|
||||
<span class="grid justify-items-center mt-10">
|
||||
{#if $state.s === StateE.LOGIN } <Login/>
|
||||
{:else if $state.s === StateE.SIGNUP} <Signup/>
|
||||
{:else if $state.s === StateE.RESET_PASSWORD} <ResetPassword/>
|
||||
{:else if $state.s === StateE.PROFILE} <Profile/>
|
||||
{:else if $state.s === StateE.TFA_SETUP} <TfaSetup/>
|
||||
{:else if $state.s === StateE.ADMIN} <Admin/>
|
||||
{:else if $state.s === StateE.VIEW} <View/>
|
||||
{:else} <span>You are in state {$state.s}, which should not be possible, please report this.</span>
|
||||
{/if}
|
||||
</span>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
#navbar-text {
|
||||
margin-left: 0.5em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#home-button {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
311
frontend/src/api.ts
Normal file
311
frontend/src/api.ts
Normal file
@ -0,0 +1,311 @@
|
||||
import { fetchEventSource } from '@microsoft/fetch-event-source';
|
||||
|
||||
|
||||
export interface ResponseE {
|
||||
e: string,
|
||||
o: null
|
||||
}
|
||||
interface ResponseO<T> {
|
||||
e: null,
|
||||
o: T
|
||||
}
|
||||
export type Response<T> = ResponseE | ResponseO<T>;
|
||||
|
||||
export interface LoginResponse {
|
||||
otp_needed: boolean;
|
||||
token: (string|null);
|
||||
}
|
||||
|
||||
export interface Session {
|
||||
name: string;
|
||||
tfa_enabled: boolean;
|
||||
admin: boolean;
|
||||
sudo: boolean;
|
||||
}
|
||||
|
||||
export interface UserInfo {
|
||||
id: number;
|
||||
name: string;
|
||||
tfa: boolean;
|
||||
admin: boolean;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export interface Node {
|
||||
id: number;
|
||||
name: string;
|
||||
file: boolean;
|
||||
preview: boolean;
|
||||
parent: (number|null);
|
||||
size: (number|null);
|
||||
children: (Node[]|null);
|
||||
}
|
||||
|
||||
export interface CreateNodeInfo {
|
||||
id: number;
|
||||
exists: boolean;
|
||||
file: boolean;
|
||||
}
|
||||
|
||||
export interface ZipInfo {
|
||||
done: boolean;
|
||||
progress: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface PathSegment {
|
||||
name: string;
|
||||
id: (number|null);
|
||||
}
|
||||
|
||||
|
||||
export class MRPCConnector {
|
||||
url: string;
|
||||
|
||||
private __create_msg(service: string, method: string, data: any) {
|
||||
return {service, method, data};
|
||||
}
|
||||
|
||||
public constructor(url: string) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
|
||||
public Auth_signup(username: string, password: string): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Auth', 'signup', {username,password});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Auth_login(username: string, password: string, otp: (string|null)): Promise<Response<LoginResponse>> {
|
||||
const __msg = this.__create_msg('Auth', 'login', {username,password,otp});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Auth_send_recovery_key(username: string): Promise<void> {
|
||||
const __msg = this.__create_msg('Auth', 'send_recovery_key', {username});
|
||||
return fetch(this.url, {method: 'POST', body: JSON.stringify(__msg)}).then(__r => {});
|
||||
}
|
||||
|
||||
public Auth_reset_password(key: string, password: string): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Auth', 'reset_password', {key,password});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Auth_change_password(token: string, old_pw: string, new_pw: string): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Auth', 'change_password', {token,old_pw,new_pw});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Auth_logout(token: string): Promise<void> {
|
||||
const __msg = this.__create_msg('Auth', 'logout', {token});
|
||||
return fetch(this.url, {method: 'POST', body: JSON.stringify(__msg)}).then(__r => {});
|
||||
}
|
||||
|
||||
public Auth_logout_all(token: string): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Auth', 'logout_all', {token});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Auth_tfa_setup_mail(token: string): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Auth', 'tfa_setup_mail', {token});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Auth_tfa_setup_totp(token: string): Promise<Response<string>> {
|
||||
const __msg = this.__create_msg('Auth', 'tfa_setup_totp', {token});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Auth_tfa_complete(token: string, otp: string): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Auth', 'tfa_complete', {token,otp});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Auth_tfa_disable(token: string): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Auth', 'tfa_disable', {token});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Auth_delete_user(token: string): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Auth', 'delete_user', {token});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Auth_session_info(token: string): Promise<Response<Session>> {
|
||||
const __msg = this.__create_msg('Auth', 'session_info', {token});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Admin_list_users(token: string): Promise<Response<UserInfo[]>> {
|
||||
const __msg = this.__create_msg('Admin', 'list_users', {token});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Admin_delete_user(token: string, user: number): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Admin', 'delete_user', {token,user});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Admin_logout(token: string, user: number): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Admin', 'logout', {token,user});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Admin_disable_tfa(token: string, user: number): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Admin', 'disable_tfa', {token,user});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Admin_set_admin(token: string, user: number, admin: boolean): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Admin', 'set_admin', {token,user,admin});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Admin_set_enabled(token: string, user: number, enabled: boolean): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Admin', 'set_enabled', {token,user,enabled});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Admin_sudo(token: string, user: number): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Admin', 'sudo', {token,user});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Admin_unsudo(token: string): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Admin', 'unsudo', {token});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public Admin_shutdown(token: string): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('Admin', 'shutdown', {token});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public FS_get_node(token: string, node: number): Promise<Response<Node>> {
|
||||
const __msg = this.__create_msg('FS', 'get_node', {token,node});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public FS_get_path(token: string, node: number): Promise<Response<PathSegment[]>> {
|
||||
const __msg = this.__create_msg('FS', 'get_path', {token,node});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public FS_get_nodes_size(token: string, nodes: number[]): Promise<Response<number>> {
|
||||
const __msg = this.__create_msg('FS', 'get_nodes_size', {token,nodes});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public FS_create_node(token: string, file: boolean, parent: number, name: string): Promise<Response<CreateNodeInfo>> {
|
||||
const __msg = this.__create_msg('FS', 'create_node', {token,file,parent,name});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public FS_move_nodes(token: string, nodes: number[], parent: number): Promise<(string|null)> {
|
||||
const __msg = this.__create_msg('FS', 'move_nodes', {token,nodes,parent});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public FS_delete_nodes(token: string, nodes: number[], __cbk: (v: string|null) => void) {
|
||||
const __msg = this.__create_msg('FS', 'delete_nodes', {token,nodes});
|
||||
fetchEventSource(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg),
|
||||
onmessage: __e => __cbk(JSON.parse(__e.data)),
|
||||
onerror: __e => {throw __e;},
|
||||
onclose: () => __cbk(null)
|
||||
});
|
||||
}
|
||||
|
||||
public FS_download_preview(token: string, node: number): Promise<Response<string>> {
|
||||
const __msg = this.__create_msg('FS', 'download_preview', {token,node});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
public FS_get_mime(token: string, node: number): Promise<Response<string>> {
|
||||
const __msg = this.__create_msg('FS', 'get_mime', {token,node});
|
||||
return fetch(this.url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(__msg)
|
||||
}).then((__r) => __r.json());
|
||||
}
|
||||
|
||||
}
|
20
frontend/src/app.pcss
Normal file
20
frontend/src/app.pcss
Normal file
@ -0,0 +1,20 @@
|
||||
@tailwind base;
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.h-screen-90 {
|
||||
height: 90vh;
|
||||
}
|
||||
|
||||
@tailwind components;
|
||||
|
||||
@tailwind utilities;
|
53
frontend/src/components/DeleteModal.svelte
Normal file
53
frontend/src/components/DeleteModal.svelte
Normal file
@ -0,0 +1,53 @@
|
||||
<script lang="ts">
|
||||
import {rpc, show_working, state, token, type UploadFile} from '../store';
|
||||
import {Button, ButtonGroup, Modal} from 'flowbite-svelte';
|
||||
import {afterUpdate} from 'svelte';
|
||||
|
||||
let show_confirm = false;
|
||||
let show_modal = false;
|
||||
let pre_element: HTMLElement|null = null;
|
||||
let text = '';
|
||||
let nodes: number[] = [];
|
||||
|
||||
async function real_delete() {
|
||||
show_confirm = false;
|
||||
show_modal = true;
|
||||
text = '';
|
||||
show_working.set(true);
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
rpc.FS_delete_nodes($token ?? '', nodes, (v) => {
|
||||
if (v == null)
|
||||
resolve();
|
||||
else {
|
||||
text += v;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
show_working.set(false);
|
||||
show_modal = false;
|
||||
state.update(v => v);
|
||||
}
|
||||
|
||||
export const del = async (n: number[]) => {
|
||||
nodes = n;
|
||||
show_confirm = true;
|
||||
};
|
||||
|
||||
afterUpdate(() => {
|
||||
if (pre_element)
|
||||
pre_element.scroll({ top: pre_element.scrollHeight, behavior: 'instant' });
|
||||
});
|
||||
</script>
|
||||
|
||||
<Modal bind:open={show_confirm} dismissable={false} title="Do you really want to delete these files?">
|
||||
<ButtonGroup class="w-full flex flex-nowrap">
|
||||
<Button class="flex-1" color="green" on:click={() => show_confirm = false}>No</Button>
|
||||
<Button class="flex-1" color="red" on:click={real_delete}>Yes</Button>
|
||||
</ButtonGroup>
|
||||
</Modal>
|
||||
|
||||
<Modal bind:open={show_modal} dismissable={false} size="xl" title="Deleting" bodyClass="h-full p-0 space-y-0" class="h-screen-90">
|
||||
<pre bind:this={pre_element} class="bg-gray-200 text-gray-600 px-4 py-2 h-full overflow-y-auto overscroll-contain rounded-b">{text}</pre>
|
||||
</Modal>
|
146
frontend/src/components/DirViewer.svelte
Normal file
146
frontend/src/components/DirViewer.svelte
Normal file
@ -0,0 +1,146 @@
|
||||
<script context="module" lang="ts">
|
||||
import {writable} from 'svelte/store';
|
||||
const show_preview = writable<boolean>(false);
|
||||
</script>
|
||||
<script lang="ts">
|
||||
import {Checkbox, Dropdown, DropdownItem, Spinner, Table, TableBody, TableBodyCell, TableBodyRow, TableHead, TableHeadCell, Tooltip} from 'flowbite-svelte';
|
||||
import {Folder, FolderParent, DocumentBlank, ChevronSortDown} from 'carbon-icons-svelte';
|
||||
import {filesize} from 'filesize';
|
||||
import {api, changeStateFunction, download, StateE, token, rpc} from '../store';
|
||||
import LinkButton from './LinkButton.svelte';
|
||||
import DeleteModal from './DeleteModal.svelte';
|
||||
|
||||
export let node: api.Node;
|
||||
|
||||
let selected: number[] = [];
|
||||
let nodes: api.Node[], dirs: api.Node[], files: api.Node[], previews: {[key: number]: string|null} = {};
|
||||
let total_size: number;
|
||||
$: { nodes = node.children!; selectNone(); }
|
||||
$: dirs = nodes.filter(v => !v.file).sort((a, b) => a.name.localeCompare(b.name));
|
||||
$: files = nodes.filter(v => v.file).sort((a, b) => a.name.localeCompare(b.name));
|
||||
$: total_size = files.map(v => v.size).reduce<number>((a, b) => (a + b!), 0);
|
||||
$: {
|
||||
if ($show_preview) {
|
||||
for (const file of files) {
|
||||
if (!file.preview) continue;
|
||||
getPreview(file.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getPreview(node: number) {
|
||||
const resp = await rpc.FS_download_preview($token ?? '', node);
|
||||
if (resp.o == null)
|
||||
return;
|
||||
previews[node] = 'data:image/png;base64,' + resp.o;
|
||||
previews = previews;
|
||||
}
|
||||
|
||||
let ctx_node: api.Node;
|
||||
let ctx_hidden = true;
|
||||
let ctx_x = 0, ctx_y = 0;
|
||||
let ctx_style: string;
|
||||
$: ctx_style = `top: ${ctx_y}px; left: ${ctx_x}px; position: fixed;`;
|
||||
|
||||
function onCtxMenu(node: api.Node, e: MouseEvent) {
|
||||
e.preventDefault();
|
||||
if (!ctx_hidden)
|
||||
return ctx_hidden = true;
|
||||
ctx_x = e.pageX;
|
||||
ctx_y = e.pageY;
|
||||
ctx_node = node;
|
||||
ctx_hidden = false;
|
||||
}
|
||||
|
||||
const selectAll = () => selected = nodes.map(v => v.id);
|
||||
const selectFolders = () => selected = dirs.map(v => v.id);
|
||||
const selectFiles = () => selected = files.map(v => v.id);
|
||||
const selectNone = () => selected = [];
|
||||
const downloadSelected = () => download($token ?? '', nodes.filter(v => selected.includes(v.id)));
|
||||
const deleteSelected = () => del(selected);
|
||||
|
||||
|
||||
const onCtxDownload = () => download($token ?? '', [ctx_node]);
|
||||
|
||||
let del: (nodes: number[]) => Promise<void>;
|
||||
const onCtxDelete = () => del([ctx_node.id]);
|
||||
|
||||
const onShowPreview = (e: Event) => { show_preview.set((e.target as HTMLInputElement).checked); }
|
||||
</script>
|
||||
|
||||
<svelte:body on:click={() => (ctx_hidden = true)} />
|
||||
|
||||
<DeleteModal bind:del={del} />
|
||||
|
||||
<Table hoverable>
|
||||
<TableHead theadClass="text-xs">
|
||||
<TableHeadCell class="p-2 pl-4 w-0 h-0">
|
||||
<ChevronSortDown id="dropdown-button" />
|
||||
</TableHeadCell>
|
||||
<TableHeadCell class="p-2 w-0"><Checkbox checked={$show_preview} on:change={onShowPreview} /><Tooltip>Show image previews</Tooltip></TableHeadCell>
|
||||
<TableHeadCell>Name</TableHeadCell>
|
||||
<TableHeadCell>Size</TableHeadCell>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{#if node.parent !== null}
|
||||
<TableBodyRow>
|
||||
<TableBodyCell class="!p-4"></TableBodyCell>
|
||||
<TableBodyCell class="px-2 w-0"><FolderParent /></TableBodyCell>
|
||||
<TableBodyCell class="pl-0"><LinkButton on:click={changeStateFunction(StateE.VIEW, node.parent ?? 0)}>..</LinkButton></TableBodyCell>
|
||||
<TableBodyCell></TableBodyCell>
|
||||
</TableBodyRow>
|
||||
{/if}
|
||||
{#each dirs as node}
|
||||
<TableBodyRow on:contextmenu={onCtxMenu.bind(null, node)}>
|
||||
<TableBodyCell class="p-2 pl-4 w-0 h-0"><Checkbox bind:group={selected} value={node.id}/></TableBodyCell>
|
||||
<TableBodyCell class="px-2 w-0"><Folder /></TableBodyCell>
|
||||
<TableBodyCell class="pl-0"><LinkButton on:click={changeStateFunction(StateE.VIEW, node.id)}>{node.name}</LinkButton></TableBodyCell>
|
||||
<TableBodyCell></TableBodyCell>
|
||||
</TableBodyRow>
|
||||
{/each}
|
||||
{#each files as node}
|
||||
<TableBodyRow on:contextmenu={onCtxMenu.bind(null, node)}>
|
||||
<TableBodyCell class="p-2 pl-4 w-0 h-0"><Checkbox bind:group={selected} value={node.id}/></TableBodyCell>
|
||||
<TableBodyCell class="px-2 min-w-0">
|
||||
{#if $show_preview && node.preview}
|
||||
{#if previews[node.id] !== undefined}
|
||||
<img class="w-screen max-w-xs" alt="preview" src={previews[node.id]} />
|
||||
{:else}
|
||||
<Spinner size="4"/>
|
||||
{/if}
|
||||
{:else}
|
||||
<DocumentBlank />
|
||||
{/if}
|
||||
</TableBodyCell>
|
||||
<TableBodyCell class="pl-0"><LinkButton on:click={changeStateFunction(StateE.VIEW, node.id)}>{node.name}</LinkButton></TableBodyCell>
|
||||
<TableBodyCell>{filesize(node.size ?? 0, {base: 2, standard: 'jedec'})}</TableBodyCell>
|
||||
</TableBodyRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
<tfoot class="text-gray-700 bg-gray-50">
|
||||
<tr>
|
||||
<td class="px-6 py-3" colspan="3">
|
||||
{#if selected.length > 0}
|
||||
<LinkButton on:click={downloadSelected}>Download</LinkButton>
|
||||
<LinkButton color="red" on:click={deleteSelected}>Delete</LinkButton>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="px-6 py-3">{filesize(total_size, {base: 2, standard: 'jedec'})}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</Table>
|
||||
|
||||
<Dropdown triggeredBy="#dropdown-button" trigger="hover" placement="left">
|
||||
<DropdownItem on:click={selectAll}>Select all</DropdownItem>
|
||||
<DropdownItem on:click={selectFolders}>Select folders</DropdownItem>
|
||||
<DropdownItem on:click={selectFiles}>Select files</DropdownItem>
|
||||
<DropdownItem on:click={selectNone}>Select none</DropdownItem>
|
||||
</Dropdown>
|
||||
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div style={ctx_style} hidden={ctx_hidden} class="z-50 shadow-md rounded-lg border-gray-100 bg-white" on:contextmenu={() => (ctx_hidden = true)}>
|
||||
<ul class="py-1">
|
||||
<li><button class="font-medium py-2 px-4 text-sm hover:bg-gray-100 w-full text-left" on:click={onCtxDownload}>Download</button></li>
|
||||
<li><button class="font-medium py-2 px-4 text-sm hover:bg-gray-100 text-red-400 w-full text-left" on:click={onCtxDelete}>Delete</button></li>
|
||||
</ul>
|
||||
</div>
|
57
frontend/src/components/FileViewer.svelte
Normal file
57
frontend/src/components/FileViewer.svelte
Normal file
@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
import {Button, Spinner} from 'flowbite-svelte';
|
||||
import {Download} from 'carbon-icons-svelte';
|
||||
import {api, rpc, token, workingWrapperR} from '../store';
|
||||
import {onDestroy} from 'svelte';
|
||||
|
||||
export let node: api.Node;
|
||||
|
||||
let src = '';
|
||||
let loading = false;
|
||||
|
||||
let mime: string|null;
|
||||
let image: boolean, video: boolean, audio: boolean, pdf: boolean, can_display: boolean;
|
||||
$: workingWrapperR<string>(() => rpc.FS_get_mime($token ?? '', node.id)).then(v => mime = v);
|
||||
$: image = mime?.startsWith('image/') ?? false;
|
||||
$: video = mime?.startsWith('video/') ?? false;
|
||||
$: audio = mime?.startsWith('audio/') ?? false;
|
||||
$: pdf = mime === 'application/pdf';
|
||||
$: can_display = image || video || audio || pdf;
|
||||
|
||||
async function load() {
|
||||
loading = true;
|
||||
if (src.startsWith('blob'))
|
||||
URL.revokeObjectURL(src);
|
||||
const resp = await fetch('/download', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: `token=${$token ?? ''}&node=${node.id}`
|
||||
});
|
||||
if (resp.status != 200)
|
||||
return;
|
||||
src = URL.createObjectURL(await resp.blob());
|
||||
loading = false;
|
||||
}
|
||||
|
||||
$: if (image || pdf) load();
|
||||
|
||||
onDestroy(() => { if (src.startsWith('blob')) URL.revokeObjectURL(src); });
|
||||
</script>
|
||||
|
||||
<Button class="w-full mb-6"><Download />Download</Button>
|
||||
{#if can_display && !loading && src === ''}
|
||||
<Button class="w-full" outline on:click={load}>Load</Button>
|
||||
{:else if loading}
|
||||
<Spinner class="w-full" />
|
||||
{:else if can_display && src !== ''}
|
||||
{#if image}
|
||||
<img class="w-full" alt="img" src={src} />
|
||||
{:else if video}
|
||||
<!-- svelte-ignore a11y-media-has-caption -->
|
||||
<video class="w-full" src={src} controls autoplay />
|
||||
{:else if audio}
|
||||
<audio class="w-full" src={src} controls autoplay />
|
||||
{:else if pdf}
|
||||
<embed class="w-full" style="height: 75vh" src={src} type="application/pdf" />
|
||||
{/if}
|
||||
{/if}
|
22
frontend/src/components/LinkButton.svelte
Normal file
22
frontend/src/components/LinkButton.svelte
Normal file
@ -0,0 +1,22 @@
|
||||
<script>
|
||||
import {twMerge} from "tailwind-merge";
|
||||
|
||||
export let color = 'primary';
|
||||
</script>
|
||||
|
||||
<button class={twMerge('link-button transition-colors', `text-${color}-600`, `hover:text-${color}-400`, $$props.class)} on:click>
|
||||
<slot>
|
||||
<span class="text-primary-600 hover:text-primary-400"></span>
|
||||
<span class="text-amber-600 hover:text-amber-400"></span>
|
||||
<span class="text-red-600 hover:text-red-400"></span>
|
||||
</slot>
|
||||
</button>
|
||||
|
||||
<style>
|
||||
.link-button {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0 0.25em;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
145
frontend/src/components/UploadModal.svelte
Normal file
145
frontend/src/components/UploadModal.svelte
Normal file
@ -0,0 +1,145 @@
|
||||
<script lang="ts">
|
||||
import {state, token, type UploadFile} from '../store';
|
||||
import {Button, ButtonGroup, Modal, Progressbar} from 'flowbite-svelte';
|
||||
import {filesize} from 'filesize';
|
||||
|
||||
interface MyFile extends UploadFile {
|
||||
waiting: boolean,
|
||||
done: boolean,
|
||||
current: number,
|
||||
total: number,
|
||||
progress: number
|
||||
}
|
||||
|
||||
let aborting = false;
|
||||
let done = false;
|
||||
let show_modal = false;
|
||||
let files: MyFile[] = [];
|
||||
let current = 0, total = 0;
|
||||
let progress: number;
|
||||
$: progress = total == 0 ? 0 : (current / total * 100);
|
||||
let not_done_files: MyFile[], done_files: MyFile[];
|
||||
$: not_done_files = files.filter(f => !f.done);
|
||||
$: done_files = files.filter(f => f.done);
|
||||
|
||||
function close() {
|
||||
show_modal = false;
|
||||
state.update(v => v);
|
||||
}
|
||||
|
||||
async function realUpload(file: MyFile) {
|
||||
if (file.done)
|
||||
return;
|
||||
file.waiting = false;
|
||||
let load_progress = 0;
|
||||
await new Promise((resolve) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.onloadend = resolve;
|
||||
xhr.onerror = resolve;
|
||||
xhr.upload.onprogress = ev => {
|
||||
current += ev.loaded - load_progress;
|
||||
load_progress = ev.loaded;
|
||||
file.current = ev.loaded;
|
||||
file.progress = file.current / file.total * 100;
|
||||
files = files;
|
||||
if (file.current == file.total)
|
||||
resolve(null);
|
||||
};
|
||||
xhr.open('POST', '/upload', true);
|
||||
xhr.setRequestHeader('X-Node', file.id.toString());
|
||||
xhr.setRequestHeader('X-Token', $token ?? '');
|
||||
xhr.send(file.file);
|
||||
});
|
||||
current += file.total - load_progress;
|
||||
file.done = true;
|
||||
files = files;
|
||||
}
|
||||
|
||||
export const upload = async (fs: UploadFile[]) => {
|
||||
aborting = false;
|
||||
done = false;
|
||||
current = 0;
|
||||
total = 0;
|
||||
show_modal = true;
|
||||
|
||||
fs.forEach(f => (total += f.file.size));
|
||||
files = fs.map(f => {
|
||||
return {
|
||||
...f,
|
||||
waiting: f.file.size != 0,
|
||||
done: f.file.size == 0,
|
||||
current: 0,
|
||||
total: f.file.size,
|
||||
progress: 0
|
||||
};
|
||||
});
|
||||
|
||||
const in_flight = new Set();
|
||||
for (const file of files) {
|
||||
if (in_flight.size >= 15) {
|
||||
await Promise.race(in_flight);
|
||||
}
|
||||
if (aborting)
|
||||
break;
|
||||
const p = realUpload(file);
|
||||
in_flight.add(p);
|
||||
const _ = (async () => { await p; in_flight.delete(p); })();
|
||||
}
|
||||
|
||||
await Promise.all(in_flight);
|
||||
|
||||
done = true;
|
||||
if (!aborting && !files.some(v => v.overwrite))
|
||||
close();
|
||||
};
|
||||
</script>
|
||||
|
||||
<Modal bind:open={show_modal} dismissable={false} size="xl" title="Uploading">
|
||||
<div>
|
||||
<div class="mb-1 flex justify-between">
|
||||
<span>{filesize(current, {base: 2, standard: 'jedec'})} / {filesize(total, {base: 2, standard: 'jedec'})}</span>
|
||||
<span>{progress.toFixed(2)}%</span>
|
||||
</div>
|
||||
<Progressbar class="mt-0" size="h-4" bind:progress={progress} />
|
||||
</div>
|
||||
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>File</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each not_done_files as file (file.id)}
|
||||
<tr>
|
||||
<td>{file.full_name}</td>
|
||||
<td>
|
||||
{#if file.waiting}
|
||||
Waiting
|
||||
{:else}
|
||||
<div class="flex flex-nowrap flex-row">
|
||||
<span class="mr-4">{filesize(file.current, {base: 2, standard: 'jedec'})} / {filesize(file.total, {base: 2, standard: 'jedec'})}</span>
|
||||
<Progressbar class="flex-1" size="h-4" bind:progress={file.progress} labelInside />
|
||||
</div>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
{#each done_files as file (file.id)}
|
||||
<tr>
|
||||
<td>{file.full_name}</td>
|
||||
<td>Done {#if file.overwrite}(Overwrote old file){/if}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<span slot="footer">
|
||||
{#if done}
|
||||
<Button class="w-full" on:click={close}>Close</Button>
|
||||
{:else if !aborting}
|
||||
<Button class="w-full" color="red" on:click={() => (aborting = true)}>Abort</Button>
|
||||
{/if}
|
||||
</span>
|
||||
</Modal>
|
13
frontend/src/main.ts
Normal file
13
frontend/src/main.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import "./app.pcss";
|
||||
import App from "./App.svelte";
|
||||
import {state, StateE, token} from './store';
|
||||
|
||||
token.subscribe(v => {
|
||||
if (v == null) state.set({s: StateE.LOGIN, view_node: 0});
|
||||
});
|
||||
|
||||
const app = new App({
|
||||
target: document.getElementById("app") as any,
|
||||
});
|
||||
|
||||
export default app;
|
85
frontend/src/pages/Admin.svelte
Normal file
85
frontend/src/pages/Admin.svelte
Normal file
@ -0,0 +1,85 @@
|
||||
<script lang="ts">
|
||||
import {api, rpc, session, state, StateE, token, workingWrapperO, workingWrapperR} from '../store';
|
||||
import {Checkbox, Table, TableBody, TableBodyCell, TableBodyRow, TableHead, TableHeadCell} from 'flowbite-svelte';
|
||||
import {Checkmark, Error} from 'carbon-icons-svelte';
|
||||
import LinkButton from '../components/LinkButton.svelte';
|
||||
|
||||
let users: api.UserInfo[] = [];
|
||||
|
||||
async function fetchUsers() {
|
||||
const resp = await workingWrapperR<api.UserInfo[]>(() => rpc.Admin_list_users($token ?? ''));
|
||||
if (resp != null)
|
||||
users = resp;
|
||||
}
|
||||
|
||||
async function changeEnabled(user: number, target: boolean) {
|
||||
await workingWrapperO(() => rpc.Admin_set_enabled($token ?? '', user, target));
|
||||
await fetchUsers();
|
||||
}
|
||||
|
||||
async function changeAdmin(user: number, target: boolean) {
|
||||
await workingWrapperO(() => rpc.Admin_set_admin($token ?? '', user, target));
|
||||
await fetchUsers();
|
||||
}
|
||||
|
||||
async function sudo(user: number) {
|
||||
if (await workingWrapperO(() => rpc.Admin_sudo($token ?? '', user))) {
|
||||
await session.update($token);
|
||||
state.set({s: StateE.VIEW, view_node: 0});
|
||||
}
|
||||
}
|
||||
|
||||
async function logout(user: number) {
|
||||
await workingWrapperO(() => rpc.Admin_logout($token ?? '', user));
|
||||
await fetchUsers();
|
||||
}
|
||||
|
||||
async function removeTfa(user: number) {
|
||||
await workingWrapperO(() => rpc.Admin_disable_tfa($token ?? '', user));
|
||||
await fetchUsers();
|
||||
}
|
||||
|
||||
async function deleteUser(user: number) {
|
||||
await workingWrapperO(() => rpc.Admin_delete_user($token ?? '', user));
|
||||
await fetchUsers();
|
||||
}
|
||||
|
||||
async function shutdown() {
|
||||
if (confirm('Do you really want to shutdown the server?')) {
|
||||
await rpc.Admin_shutdown($token ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
fetchUsers();
|
||||
</script>
|
||||
|
||||
<Table hoverable divClass="w-full max-w-4xl relative">
|
||||
<TableHead>
|
||||
<TableHeadCell>Name</TableHeadCell>
|
||||
<TableHeadCell>Tfa</TableHeadCell>
|
||||
<TableHeadCell>Enabled</TableHeadCell>
|
||||
<TableHeadCell>Admin</TableHeadCell>
|
||||
<TableHeadCell class="sr-only">Actions</TableHeadCell>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{#each users as user (user.id)}
|
||||
<TableBodyRow>
|
||||
<TableBodyCell>{user.name}</TableBodyCell>
|
||||
<TableBodyCell>{#if user.tfa}<Checkmark/>{:else}<Error/>{/if}</TableBodyCell>
|
||||
<TableBodyCell>
|
||||
<Checkbox checked={user.enabled} on:change={changeEnabled.bind(null, user.id, !user.enabled)}></Checkbox>
|
||||
</TableBodyCell>
|
||||
<TableBodyCell>
|
||||
<Checkbox checked={user.admin} on:change={changeAdmin.bind(null, user.id, !user.admin)}></Checkbox>
|
||||
</TableBodyCell>
|
||||
<TableBodyCell class="flex">
|
||||
<LinkButton class="flex-auto" on:click={sudo.bind(null, user.id)}>Sudo</LinkButton>
|
||||
<LinkButton class="flex-auto" on:click={logout.bind(null, user.id)}>Logout</LinkButton>
|
||||
{#if user.tfa}<LinkButton class="flex-auto" color="amber" on:click={removeTfa.bind(null, user.id)}>Remove tfa</LinkButton>{/if}
|
||||
<LinkButton class="flex-auto" color="red" on:click={deleteUser.bind(null, user.id)}>Delete</LinkButton>
|
||||
</TableBodyCell>
|
||||
</TableBodyRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<LinkButton class="w-full max-w-4xl relative mt-8" color="red" on:click={shutdown}>Shutdown</LinkButton>
|
49
frontend/src/pages/Login.svelte
Normal file
49
frontend/src/pages/Login.svelte
Normal file
@ -0,0 +1,49 @@
|
||||
<script lang="ts">
|
||||
import {Button, ButtonGroup, Card, Input, InputAddon} from 'flowbite-svelte';
|
||||
import {Email, Password} from 'carbon-icons-svelte';
|
||||
import {changeStateFunction, rpc, state, StateE, token, workingWrapperR, api} from '../store';
|
||||
|
||||
let ask_tfa = false;
|
||||
let username = '', password = '', tfa = '';
|
||||
|
||||
async function login() {
|
||||
const resp = await workingWrapperR<api.LoginResponse>(() => rpc.Auth_login(username, password, ask_tfa ? tfa : null));
|
||||
if (!resp) return;
|
||||
if (resp.otp_needed) {
|
||||
ask_tfa = true;
|
||||
return;
|
||||
}
|
||||
token.set(resp.token);
|
||||
state.set({s: StateE.VIEW, view_node: 0});
|
||||
}
|
||||
|
||||
function keyUp(e: KeyboardEvent) {
|
||||
if (e.key == 'Enter') login();
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card class="w-full max-w-lg">
|
||||
{#if ask_tfa}
|
||||
<h3>Two factor authentication</h3>
|
||||
<ButtonGroup class="w-full mb-4">
|
||||
<InputAddon><Password /></InputAddon>
|
||||
<Input type="text" placeholder="Code" bind:value={tfa} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<Button class="w-full" color="primary" on:click={login}>Login</Button>
|
||||
{:else}
|
||||
<h3 class="mb-6">Sign in</h3>
|
||||
<ButtonGroup class="w-full mb-2">
|
||||
<InputAddon><Email /></InputAddon>
|
||||
<Input type="email" placeholder="Email" bind:value={username} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="w-full mb-4">
|
||||
<InputAddon><Password /></InputAddon>
|
||||
<Input type="password" placeholder="Password" bind:value={password} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="w-full flex flex-nowrap">
|
||||
<Button class="flex-1 flex-grow" color="primary" outline on:click={changeStateFunction(StateE.SIGNUP)}>Signup</Button>
|
||||
<Button class="flex-1 flex-grow" color="primary" on:click={login}>Login</Button>
|
||||
<Button class="flex-1 flex-grow" color="primary" outline on:click={changeStateFunction(StateE.RESET_PASSWORD)}>Forget password</Button>
|
||||
</ButtonGroup>
|
||||
{/if}
|
||||
</Card>
|
82
frontend/src/pages/Profile.svelte
Normal file
82
frontend/src/pages/Profile.svelte
Normal file
@ -0,0 +1,82 @@
|
||||
<script lang="ts">
|
||||
import {changeStateFunction, error_banner, rpc, session, StateE, token, workingWrapperO} from '../store';
|
||||
import {Accordion, AccordionItem, Button, ButtonGroup, Input, InputAddon} from 'flowbite-svelte';
|
||||
import {Password} from 'carbon-icons-svelte';
|
||||
import {info_banner} from '../store.js';
|
||||
|
||||
const s = session.s;
|
||||
const tfa_enabled: boolean = $s?.tfa_enabled ?? false;
|
||||
const change_pw_data = {o: '', n: '', n2: ''}
|
||||
|
||||
async function changePw() {
|
||||
const { o: old, n: password, n2: password2 } = change_pw_data;
|
||||
if (password != password2) {
|
||||
error_banner.set('Password doesn\'t match');
|
||||
return;
|
||||
}
|
||||
|
||||
const resp = await workingWrapperO(() => rpc.Auth_change_password($token ?? '', old, password));
|
||||
if (resp) {
|
||||
info_banner.set('Changed password');
|
||||
change_pw_data.o = '';
|
||||
change_pw_data.n = '';
|
||||
change_pw_data.n2 = '';
|
||||
}
|
||||
}
|
||||
|
||||
async function disableTfa() {
|
||||
await workingWrapperO(() => rpc.Auth_tfa_disable($token ?? ''));
|
||||
token.set(null);
|
||||
}
|
||||
|
||||
async function logoutAll() {
|
||||
await workingWrapperO(() => rpc.Auth_logout_all($token ?? ''));
|
||||
token.set(null);
|
||||
}
|
||||
|
||||
async function deleteAccount() {
|
||||
if (confirm("Do your really want to delete your account?")) {
|
||||
await workingWrapperO(() => rpc.Auth_delete_user($token ?? ''));
|
||||
token.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
function keyUp(e: KeyboardEvent) {
|
||||
if (e.key == 'Enter') changePw();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<Accordion class="w-full max-w-lg text-">
|
||||
<AccordionItem>
|
||||
<span slot="header">Change password</span>
|
||||
<ButtonGroup class="w-full mb-4">
|
||||
<InputAddon><Password /></InputAddon>
|
||||
<Input type="password" placeholder="Old password" bind:value={change_pw_data.o} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="w-full mb-2">
|
||||
<InputAddon><Password /></InputAddon>
|
||||
<Input type="password" placeholder="New password" bind:value={change_pw_data.n} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="w-full mb-4">
|
||||
<InputAddon><Password /></InputAddon>
|
||||
<Input type="password" placeholder="Repeat new password" bind:value={change_pw_data.n2} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<Button class="w-full" color="primary" on:click={changePw}>Change password</Button>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<span slot="header">Two factor authentication: <span class={tfa_enabled ? 'text-green-600' : 'text-red-600'}>{tfa_enabled ? 'enabled': 'disabled'}</span></span>
|
||||
{#if tfa_enabled}
|
||||
<Button class="w-full" color="red" on:click={disableTfa}>Disable</Button>
|
||||
{:else}
|
||||
<Button class="w-full" color="green" on:click={changeStateFunction(StateE.TFA_SETUP)}>Enable</Button>
|
||||
{/if}
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<span slot="header">Account actions</span>
|
||||
<ButtonGroup class="w-full">
|
||||
<Button class="w-full" color="red" on:click={logoutAll}>Logout everywhere</Button>
|
||||
<Button class="w-full" color="red" on:click={deleteAccount}>Delete account</Button>
|
||||
</ButtonGroup>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
70
frontend/src/pages/ResetPassword.svelte
Normal file
70
frontend/src/pages/ResetPassword.svelte
Normal file
@ -0,0 +1,70 @@
|
||||
<script lang="ts">
|
||||
import {Button, ButtonGroup, Card, Input, InputAddon} from 'flowbite-svelte';
|
||||
import {Email, EmailNew, Password} from 'carbon-icons-svelte';
|
||||
import {changeStateFunction, error_banner, info_banner, rpc, state, StateE, workingWrapper, workingWrapperO} from '../store';
|
||||
|
||||
let enter_key = false;
|
||||
let username = '', key = '', password = '', password2 = '';
|
||||
|
||||
async function sendKey() {
|
||||
if (await workingWrapper(() => rpc.Auth_send_recovery_key(username)) == null)
|
||||
return;
|
||||
info_banner.set('A message has been sent');
|
||||
enter_key = true;
|
||||
}
|
||||
|
||||
async function changePw() {
|
||||
if (password != password2) {
|
||||
error_banner.set('Password doesn\'t match');
|
||||
return;
|
||||
}
|
||||
|
||||
if (await workingWrapperO(() => rpc.Auth_reset_password(key, password)))
|
||||
$state.s = StateE.LOGIN;
|
||||
}
|
||||
|
||||
function keyUp(e: KeyboardEvent) {
|
||||
if (e.key == 'Enter') {
|
||||
if (enter_key)
|
||||
changePw();
|
||||
else
|
||||
sendKey();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card class="w-full max-w-lg">
|
||||
<h3 class="mb-6">Reset password</h3>
|
||||
{#if enter_key}
|
||||
<ButtonGroup class="w-full mb-4">
|
||||
<InputAddon><EmailNew /></InputAddon>
|
||||
<Input type="text" placeholder="Recovery key" bind:value={key} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="w-full mb-2">
|
||||
<InputAddon><Password /></InputAddon>
|
||||
<Input type="password" placeholder="Password" bind:value={password} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="w-full mb-4">
|
||||
<InputAddon><Password /></InputAddon>
|
||||
<Input type="password" placeholder="Repeat password" bind:value={password2} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="w-full flex flex-nowrap">
|
||||
<Button class="flex-1 flex-grow" color="primary" outline on:click={changeStateFunction(StateE.LOGIN)}>Login</Button>
|
||||
<Button class="flex-1 flex-grow" color="primary" on:click={changePw}>Change password</Button>
|
||||
</ButtonGroup>
|
||||
{:else}
|
||||
<ButtonGroup class="w-full mb-4">
|
||||
<InputAddon><Email /></InputAddon>
|
||||
<Input type="email" placeholder="Email" bind:value={username} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="w-full flex flex-nowrap">
|
||||
<Button class="flex-1 flex-grow" color="primary" outline on:click={changeStateFunction(StateE.LOGIN)}>Login</Button>
|
||||
<Button class="flex-1 flex-grow" color="primary" on:click={sendKey}>Send recovery key</Button>
|
||||
<Button class="flex-1 flex-grow" color="primary" outline on:click={() => (enter_key = true)}>Enter key</Button>
|
||||
</ButtonGroup>
|
||||
{/if}
|
||||
</Card>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
54
frontend/src/pages/Signup.svelte
Normal file
54
frontend/src/pages/Signup.svelte
Normal file
@ -0,0 +1,54 @@
|
||||
<script lang="ts">
|
||||
import {Button, ButtonGroup, Card, Input, InputAddon} from 'flowbite-svelte';
|
||||
import {Email, Password} from 'carbon-icons-svelte';
|
||||
import {changeStateFunction, error_banner, info_banner, rpc, state, StateE, workingWrapperO} from '../store';
|
||||
|
||||
let username = '', username2 = '', password = '', password2 = '';
|
||||
|
||||
async function signup() {
|
||||
error_banner.set('');
|
||||
if (username != username2) {
|
||||
error_banner.set('Email doesn\'t match');
|
||||
return;
|
||||
}
|
||||
if (password != password2) {
|
||||
error_banner.set('Password doesn\'t match');
|
||||
return;
|
||||
}
|
||||
|
||||
const resp = await workingWrapperO(() => rpc.Auth_signup(username, password));
|
||||
|
||||
if (resp) {
|
||||
info_banner.set('Account created, please wait till an administrator approves it');
|
||||
$state.s = StateE.LOGIN;
|
||||
}
|
||||
}
|
||||
|
||||
function keyUp(e: KeyboardEvent) {
|
||||
if (e.key == 'Enter') signup();
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card class="w-full max-w-lg">
|
||||
<h3 class="mb-6">Sign in</h3>
|
||||
<ButtonGroup class="w-full mb-2">
|
||||
<InputAddon><Email /></InputAddon>
|
||||
<Input type="email" placeholder="Email" bind:value={username} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="w-full mb-4">
|
||||
<InputAddon><Email /></InputAddon>
|
||||
<Input type="email" placeholder="Repeat email" bind:value={username2} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="w-full mb-2">
|
||||
<InputAddon><Password /></InputAddon>
|
||||
<Input type="password" placeholder="Password" bind:value={password} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="w-full mb-4">
|
||||
<InputAddon><Password /></InputAddon>
|
||||
<Input type="password" placeholder="Repeat password" bind:value={password2} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="w-full flex flex-nowrap">
|
||||
<Button class="flex-1 flex-grow" color="primary" outline on:click={changeStateFunction(StateE.LOGIN)}>Login</Button>
|
||||
<Button class="flex-1 flex-grow" color="primary" on:click={signup}>Singup</Button>
|
||||
</ButtonGroup>
|
||||
</Card>
|
74
frontend/src/pages/TfaSetup.svelte
Normal file
74
frontend/src/pages/TfaSetup.svelte
Normal file
@ -0,0 +1,74 @@
|
||||
<script lang="ts">
|
||||
import {Button, ButtonGroup, Card, Input, InputAddon, StepIndicator, Tooltip} from 'flowbite-svelte';
|
||||
import {Password} from 'carbon-icons-svelte';
|
||||
import {info_banner, rpc, session, state, StateE, token, workingWrapperO, workingWrapperR} from '../store';
|
||||
import QRCode from 'qrcode-svg';
|
||||
|
||||
const s = session.s;
|
||||
|
||||
let step = 1;
|
||||
let code = '', secret_qr_code = '';
|
||||
let secret: string | null = null;
|
||||
let show_manual = false;
|
||||
|
||||
async function startSetup(mail: boolean) {
|
||||
if (mail) {
|
||||
const resp = await workingWrapperO(() => rpc.Auth_tfa_setup_mail($token ?? ''));
|
||||
if (resp) {
|
||||
secret = null;
|
||||
step = 2;
|
||||
}
|
||||
} else {
|
||||
const resp = await workingWrapperR<string>(() => rpc.Auth_tfa_setup_totp($token ?? ''));
|
||||
if (resp != null) {
|
||||
secret = resp.replaceAll('=', '');
|
||||
secret_qr_code = new QRCode({
|
||||
content: `otpauth://totp/MFileserver:${$s?.name ?? ''}?secret=${secret}&issuer=MFileserver`,
|
||||
container: 'none',
|
||||
padding: 0
|
||||
}).svg();
|
||||
step = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function completeSetup() {
|
||||
if (await workingWrapperO(() => rpc.Auth_tfa_complete($token ?? '', code))) {
|
||||
info_banner.set("Successfully set up two factor authentication");
|
||||
$state.s = StateE.LOGIN;
|
||||
token.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
function keyUp(e: KeyboardEvent) {
|
||||
if (e.key == 'Enter') completeSetup();
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card class="w-full max-w-lg">
|
||||
<StepIndicator steps={["Select type", "Finish setup"]} currentStep={step} size="h-2" class="mb-8"/>
|
||||
{#if step === 1}
|
||||
<ButtonGroup>
|
||||
<Button class="flex-1 flex-grow" color="primary" outline on:click={startSetup.bind(null, true)}>Mail</Button>
|
||||
<Tooltip>Receive a code via email when you want to log in</Tooltip>
|
||||
<Button class="flex-1 flex-grow" color="primary" outline on:click={startSetup.bind(null, false)}>Authenticator</Button>
|
||||
<Tooltip>Use a program like "Google Authenticator" to get a code when you want to log in</Tooltip>
|
||||
</ButtonGroup>
|
||||
{:else if step === 2}
|
||||
{#if secret == null}
|
||||
<p>A code has been sent to your mailbox</p>
|
||||
{:else}
|
||||
<svg class="w-full mb-4" viewBox="0 0 256 256" height="100%">{@html secret_qr_code}</svg>
|
||||
{#if show_manual}
|
||||
<pre class="w-full mb-4 bg-gray-100">{secret}</pre>
|
||||
{:else}
|
||||
<button class="w-full mb-4 bg-gray-200 rounded-lg" on:click={() => (show_manual = true)}>Click for manual input code</button>
|
||||
{/if}
|
||||
{/if}
|
||||
<ButtonGroup class="w-full mb-4">
|
||||
<InputAddon><Password /></InputAddon>
|
||||
<Input type="text" placeholder="Code" bind:value={code} on:keyup={keyUp}></Input>
|
||||
</ButtonGroup>
|
||||
<Button class="w-full" on:click={completeSetup}>Complete setup</Button>
|
||||
{/if}
|
||||
</Card>
|
157
frontend/src/pages/View.svelte
Normal file
157
frontend/src/pages/View.svelte
Normal file
@ -0,0 +1,157 @@
|
||||
<script lang="ts">
|
||||
import {Breadcrumb, Dropzone, Modal, Progressbar} from 'flowbite-svelte';
|
||||
import {derived} from 'svelte/store';
|
||||
import {CloudUpload} from 'carbon-icons-svelte';
|
||||
import {api, changeStateFunction, rpc, state, StateE, token, type UploadFile, workingWrapperR} from '../store';
|
||||
import LinkButton from '../components/LinkButton.svelte';
|
||||
import DirViewer from '../components/DirViewer.svelte';
|
||||
import UploadModal from '../components/UploadModal.svelte';
|
||||
import FileViewer from '../components/FileViewer.svelte';
|
||||
|
||||
interface Data {
|
||||
node: api.Node|null,
|
||||
segments: api.PathSegment[]
|
||||
}
|
||||
|
||||
let data = derived<typeof state, Data>(
|
||||
state,
|
||||
($state, set) => {
|
||||
(async () => {
|
||||
let node = await workingWrapperR<api.Node>(() => rpc.FS_get_node($token ?? '', $state.view_node));
|
||||
if (!node)
|
||||
return $state.view_node = 0;
|
||||
let segments = await workingWrapperR<api.PathSegment[]>(() => rpc.FS_get_path($token ?? '', node!.id));
|
||||
if (!segments)
|
||||
return $state.view_node = 0;
|
||||
set({node: node as Data['node'], segments });
|
||||
})();
|
||||
},
|
||||
{ node: null, segments: [] }
|
||||
);
|
||||
|
||||
const getFile = async (entry: FileSystemEntry) => new Promise<File>((o, e) => (entry as FileSystemFileEntry).file(o, e));
|
||||
|
||||
async function handleEntry(parent: number, parent_name: string, entry: FileSystemEntry): Promise<UploadFile[]> {
|
||||
if (entry.isFile) {
|
||||
try {
|
||||
return [{
|
||||
id: parent,
|
||||
name: entry.name,
|
||||
full_name: parent_name + entry.name,
|
||||
file: await getFile(entry),
|
||||
overwrite: false
|
||||
}];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
const resp = await workingWrapperR<api.CreateNodeInfo>(() => rpc.FS_create_node($token ?? '', false, parent, entry.name));
|
||||
if (!resp) return [];
|
||||
if (resp.file) return [];
|
||||
const reader = (entry as FileSystemDirectoryEntry).createReader();
|
||||
const files: UploadFile[] = [];
|
||||
const name = parent_name + entry.name + '/';
|
||||
while (true) {
|
||||
try {
|
||||
const entries: FileSystemEntry[] = await new Promise((o, e) => reader.readEntries(o, e));
|
||||
if (entries.length == 0) break;
|
||||
for (const e of entries)
|
||||
files.push(...await handleEntry(resp.id, name, e));
|
||||
} catch (e) { break; }
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
async function onChange(e: Event) {
|
||||
const input = e.target as HTMLInputElement;
|
||||
if (!input.files)
|
||||
return
|
||||
console.log(input.files);
|
||||
const files: UploadFile[] = [];
|
||||
for (const f of input.files)
|
||||
files.push({
|
||||
id: $state.view_node,
|
||||
name: f.name,
|
||||
full_name: f.name,
|
||||
file: f,
|
||||
overwrite: false
|
||||
});
|
||||
await upload(files);
|
||||
}
|
||||
|
||||
async function onDrop(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
if (!e.dataTransfer)
|
||||
return;
|
||||
const files: UploadFile[] = [];
|
||||
for (const i of e.dataTransfer.items) {
|
||||
const entry = i.webkitGetAsEntry();
|
||||
if (!entry)
|
||||
console.error("Failed to get entry for: ", i);
|
||||
else
|
||||
files.push(...await handleEntry($state.view_node, '', entry));
|
||||
}
|
||||
await upload(files);
|
||||
}
|
||||
|
||||
|
||||
const upload_progress_data = { total: 0, current: 0 }
|
||||
let upload_progress: number;
|
||||
$: upload_progress = upload_progress_data.total == 0 ? 0 : (upload_progress_data.current / upload_progress_data.total * 100);
|
||||
let real_upload: (files: UploadFile[]) => Promise<void>;
|
||||
async function upload(files: UploadFile[]) {
|
||||
upload_progress_data.total = files.length;
|
||||
upload_progress_data.current = 0;
|
||||
const upload_files: UploadFile[] = [];
|
||||
for (const file of files) {
|
||||
const resp = await workingWrapperR<api.CreateNodeInfo>(() => rpc.FS_create_node($token ?? '', true, file.id, file.name));
|
||||
if (resp && resp.file)
|
||||
upload_files.push({ ...file, id: resp.id, overwrite: resp.exists });
|
||||
upload_progress_data.current++;
|
||||
}
|
||||
await real_upload(upload_files);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full max-w-4xl">
|
||||
<div class="w-full flex mb-2 h-16">
|
||||
<Breadcrumb>
|
||||
{#each $data.segments as segment, i}
|
||||
{#if i > 0}<li class="inline-flex items-center">/</li>{/if}
|
||||
<li class="inline-flex items-center">
|
||||
{#if segment.id !== null}
|
||||
<LinkButton on:click={changeStateFunction(StateE.VIEW, segment.id)}>{segment.id === 0 ? 'Files' : segment.name}</LinkButton>
|
||||
{:else}
|
||||
<span style="padding: 0 0.25em;">{segment.name}</span>
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</Breadcrumb>
|
||||
<span class="flex-1"></span>
|
||||
{#if $data.node?.file === false}
|
||||
<Dropzone class="h-full w-64 cursor-pointer" on:drop={onDrop} on:dragover={e => e.preventDefault()} on:change={onChange} multiple>
|
||||
<span class="flex flex-row">
|
||||
<CloudUpload class="fill-gray-500 h-12 cursor-pointer" width="40%"/>
|
||||
<span class="inline text-gray-600 w-48 cursor-pointer">Click here or drag<br>to upload files</span>
|
||||
</span>
|
||||
</Dropzone>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $data.node === null}
|
||||
<!-- Waiting for data -->
|
||||
{:else if $data.node.file}
|
||||
<FileViewer node={$data.node} />
|
||||
{:else}
|
||||
<DirViewer node={$data.node} />
|
||||
{/if}
|
||||
</div>
|
||||
<UploadModal bind:upload={real_upload}/>
|
||||
{#if upload_progress_data.current !== upload_progress_data.total}
|
||||
<Modal open dismissable={false} title="Creating files">
|
||||
<div class="mb-1 flex justify-between">
|
||||
<span>{upload_progress_data.current} / {upload_progress_data.total}</span>
|
||||
<span>{upload_progress.toFixed(2)}%</span>
|
||||
</div>
|
||||
<Progressbar class="!mt-0" size="h-4" bind:progress={upload_progress} />
|
||||
</Modal>
|
||||
{/if}
|
113
frontend/src/store.ts
Normal file
113
frontend/src/store.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import {MRPCConnector, type Session, type Response} from './api';
|
||||
import {get, type Writable, writable} from 'svelte/store';
|
||||
import {filesize} from 'filesize';
|
||||
|
||||
export * as api from './api';
|
||||
|
||||
export enum StateE { LOGIN, SIGNUP, RESET_PASSWORD, PROFILE, TFA_SETUP, ADMIN, VIEW }
|
||||
export interface State {
|
||||
s: StateE,
|
||||
view_node: number
|
||||
}
|
||||
export interface UploadFile {
|
||||
id: number,
|
||||
name: string,
|
||||
full_name: string,
|
||||
file: File,
|
||||
overwrite: boolean
|
||||
}
|
||||
|
||||
export const show_working = writable<boolean>(false);
|
||||
export const info_banner = writable<string>('');
|
||||
export const error_banner = writable<string>('');
|
||||
|
||||
export const state = writable<State>({s: StateE.LOGIN, view_node: 0});
|
||||
|
||||
export const rpc = new MRPCConnector('/mrpc');
|
||||
|
||||
export const token = writable<string|null>(localStorage.getItem('token'));
|
||||
export const session: { s: Writable<Session|null>, update: (token: string|null) => Promise<void> } = {
|
||||
s: writable(null),
|
||||
update: async (t: string|null) => {
|
||||
console.log('S');
|
||||
if (t == null) {
|
||||
session.s.set(null);
|
||||
return;
|
||||
}
|
||||
const s = await rpc.Auth_session_info(t)
|
||||
if (s.e)
|
||||
token.set(null);
|
||||
else
|
||||
session.s.set(s.o);
|
||||
}
|
||||
};
|
||||
token.subscribe((t) => session.update(t));
|
||||
|
||||
token.subscribe(v => {
|
||||
if (v == null)
|
||||
localStorage.removeItem('token');
|
||||
else
|
||||
localStorage.setItem('token', v);
|
||||
})
|
||||
|
||||
|
||||
export function changeStateFunction(target: StateE, node?: number): () => void {
|
||||
return () => {
|
||||
const new_node = node ?? get(state).view_node;
|
||||
state.set({s: target, view_node: new_node});
|
||||
}
|
||||
}
|
||||
|
||||
export async function workingWrapper<T>(fn: () => Promise<T>): Promise<T|null> {
|
||||
let r = null;
|
||||
error_banner.set('');
|
||||
show_working.set(true);
|
||||
try {
|
||||
r = await fn();
|
||||
} catch (e) {
|
||||
error_banner.set(`Error while making request: ${e}`);
|
||||
}
|
||||
show_working.set(false);
|
||||
return r;
|
||||
}
|
||||
|
||||
export async function workingWrapperO(fn: () => Promise<string|null>): Promise<boolean> {
|
||||
const resp = await workingWrapper(fn);
|
||||
if (resp)
|
||||
error_banner.set(resp);
|
||||
if (resp === 'Unauthorized')
|
||||
token.set(null);
|
||||
return resp == null;
|
||||
}
|
||||
|
||||
export async function workingWrapperR<T>(fn: () => Promise<Response<T>>): Promise<T|null> {
|
||||
const resp = await workingWrapper(fn);
|
||||
if (!resp)
|
||||
return null;
|
||||
if (resp.e === 'Unauthorized')
|
||||
token.set(null);
|
||||
else if (resp.e != null)
|
||||
error_banner.set(resp.e);
|
||||
return resp.o;
|
||||
}
|
||||
|
||||
export async function download<T extends {id:number, file:boolean}>(token: string, nodes: T[]) {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.target = '_blank';
|
||||
form.innerHTML = `<input type="hidden" name="token" value="${token}">`;
|
||||
if (nodes.length == 1 && nodes[0].file) {
|
||||
form.action = '/download';
|
||||
form.innerHTML += `<input type="hidden" name="node" value="${nodes[0].id}}">`;
|
||||
} else {
|
||||
form.action = '/download_multi';
|
||||
form.innerHTML += `<input type="hidden" name="nodes" value="${nodes.map(n => n.id).join('.')}">`;
|
||||
const resp = await workingWrapperR<number>(() => rpc.FS_get_nodes_size(token, nodes.map(v => v.id)));
|
||||
if (!resp)
|
||||
return;
|
||||
info_banner.set(`Estimated size: ${filesize(resp, {base: 2, standard: 'jedec'})}`);
|
||||
}
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
document.body.removeChild(form);
|
||||
}
|
2
frontend/src/vite-env.d.ts
vendored
Normal file
2
frontend/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/// <reference types="svelte" />
|
||||
/// <reference types="vite/client" />
|
7
frontend/svelte.config.js
Normal file
7
frontend/svelte.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
|
||||
|
||||
export default {
|
||||
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
|
||||
// for more information about preprocessors
|
||||
preprocess: [vitePreprocess({})],
|
||||
};
|
28
frontend/tailwind.config.cjs
Normal file
28
frontend/tailwind.config.cjs
Normal file
@ -0,0 +1,28 @@
|
||||
/** @type {import('tailwindcss').Config}*/
|
||||
const config = {
|
||||
darkMode: 'class',
|
||||
content: ["./src/**/*.{html,js,svelte,ts}", './node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}'],
|
||||
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#f7fee7',
|
||||
100: '#ecfccb',
|
||||
200: '#d9f99d',
|
||||
300: '#bef264',
|
||||
400: '#a3e635',
|
||||
500: '#84cc16',
|
||||
600: '#65a30d',
|
||||
700: '#4d7c0f',
|
||||
800: '#3f6212',
|
||||
900: '#365314'
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
plugins: [],
|
||||
};
|
||||
|
||||
module.exports = config;
|
20
frontend/tsconfig.json
Normal file
20
frontend/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"resolveJsonModule": true,
|
||||
/**
|
||||
* Typecheck JS in `.svelte` and `.js` files by default.
|
||||
* Disable checkJs if you'd like to use dynamic types in JS.
|
||||
* Note that setting allowJs false does not prevent the use
|
||||
* of JS in `.svelte` files.
|
||||
*/
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
9
frontend/tsconfig.node.json
Normal file
9
frontend/tsconfig.node.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler"
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
59
frontend/vite.config.ts
Normal file
59
frontend/vite.config.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import {defineConfig, PluginOption} from 'vite'
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||
import {viteSingleFile} from 'vite-plugin-singlefile';
|
||||
import {createHtmlPlugin} from 'vite-plugin-html';
|
||||
import purgeCss from 'vite-plugin-tailwind-purgecss';
|
||||
import {NormalizedInputOptions, PluginContext} from 'rollup';
|
||||
import * as fs from 'fs';
|
||||
import * as child_process from 'child_process';
|
||||
|
||||
const response_replacement =
|
||||
'interface ResponseE {\n' +
|
||||
' e: string,\n' +
|
||||
' o: null\n' +
|
||||
'}\n' +
|
||||
'interface ResponseO<T> {\n' +
|
||||
' e: null,\n' +
|
||||
' o: T\n' +
|
||||
'}\n' +
|
||||
'export type Response<T> = ResponseE | ResponseO<T>;'
|
||||
|
||||
function checkMrpc(this: PluginContext, options: NormalizedInputOptions) {
|
||||
const src_ts = fs.statSync('../fileserver.rs').mtimeMs;
|
||||
const update = !fs.existsSync('src/api.ts') || fs.statSync('src/api.ts').mtimeMs <= src_ts;
|
||||
if (!update)
|
||||
return;
|
||||
child_process.spawnSync(
|
||||
'../mrpc',
|
||||
['-n', 'src/api', '-c', 'ts', '../fileserver.rs'],
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
let api_content = fs.readFileSync('src/api.ts', 'utf8');
|
||||
api_content = api_content.replace(
|
||||
'interface Response<T> {\n e: (string|null);\n o: (T|null);\n}',
|
||||
response_replacement
|
||||
);
|
||||
fs.writeFileSync('src/api.ts', api_content, 'utf8');
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
{ name: 'mrpc', buildStart: checkMrpc },
|
||||
svelte(),
|
||||
purgeCss(),
|
||||
viteSingleFile({removeViteModuleLoader: true}),
|
||||
createHtmlPlugin({minify: true})
|
||||
],
|
||||
build: {
|
||||
minify: false
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
proxy: {
|
||||
'/mrpc': 'http://127.0.0.1:2121',
|
||||
'/download': 'http://127.0.0.1:2121',
|
||||
'/download_multi': 'http://127.0.0.1:2121',
|
||||
'/upload': 'http://127.0.0.1:2121'
|
||||
}
|
||||
}
|
||||
})
|
300
include/botan_asio/asio_async_ops.h
Normal file
300
include/botan_asio/asio_async_ops.h
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Helpers for TLS ASIO Stream
|
||||
* (C) 2018-2020 Jack Lloyd
|
||||
* 2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
|
||||
*
|
||||
* Botan is released under the Simplified BSD License (see license.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOTAN_ASIO_ASYNC_OPS_H_
|
||||
#define BOTAN_ASIO_ASYNC_OPS_H_
|
||||
|
||||
#include <botan_all.h>
|
||||
#include <asio.hpp>
|
||||
#include <asio/yield.hpp>
|
||||
#include "asio_error.h"
|
||||
|
||||
namespace Botan::TLS::detail {
|
||||
|
||||
/**
|
||||
* Base class for asynchronous stream operations.
|
||||
*
|
||||
* Asynchronous operations, used for example to implement an interface for boost::asio::async_read_some and
|
||||
* boost::asio::async_write_some, are based on boost::asio::coroutines.
|
||||
* Derived operations should implement a call operator and invoke it with the correct parameters upon construction. The
|
||||
* call operator needs to make sure that the user-provided handler is not called directly. Typically, yield / reenter is
|
||||
* used for this in the following fashion:
|
||||
*
|
||||
* ```
|
||||
* void operator()(boost::system::error_code ec, std::size_t bytes_transferred, bool isContinuation = true)
|
||||
* {
|
||||
* reenter(this)
|
||||
* {
|
||||
* // operation specific logic, repeatedly interacting with the stream_core and the next_layer (socket)
|
||||
*
|
||||
* // make sure intermediate initiating function is called
|
||||
* if(!isContinuation)
|
||||
* {
|
||||
* yield next_layer.async_operation(empty_buffer, this);
|
||||
* }
|
||||
*
|
||||
* // call the completion handler
|
||||
* complete_now(error_code, bytes_transferred);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Once the operation is completed and ready to call the completion handler it checks if an intermediate initiating
|
||||
* function has been called using the `isContinuation` parameter. If not, it will call an asynchronous operation, such
|
||||
* as `async_read_some`, with and empty buffer, set the object itself as the handler, and `yield`. As a result, the call
|
||||
* operator will be invoked again, this time as a continuation, and will jump to the location where it yielded before
|
||||
* using `reenter`. It is now safe to call the handler function via `complete_now`.
|
||||
*
|
||||
* \tparam Handler Type of the completion handler
|
||||
* \tparam Executor1 Type of the asio executor (usually derived from the lower layer)
|
||||
* \tparam Allocator Type of the allocator to be used
|
||||
*/
|
||||
template <class Handler, class Executor1, class Allocator>
|
||||
class AsyncBase : public boost::asio::coroutine {
|
||||
public:
|
||||
using allocator_type = boost::asio::associated_allocator_t<Handler, Allocator>;
|
||||
using executor_type = boost::asio::associated_executor_t<Handler, Executor1>;
|
||||
|
||||
allocator_type get_allocator() const noexcept { return boost::asio::get_associated_allocator(m_handler); }
|
||||
|
||||
executor_type get_executor() const noexcept {
|
||||
return boost::asio::get_associated_executor(m_handler, m_work_guard_1.get_executor());
|
||||
}
|
||||
|
||||
protected:
|
||||
template <class HandlerT>
|
||||
AsyncBase(HandlerT&& handler, const Executor1& executor) :
|
||||
m_handler(std::forward<HandlerT>(handler)), m_work_guard_1(executor) {}
|
||||
|
||||
/**
|
||||
* Call the completion handler.
|
||||
*
|
||||
* This function should only be called after an intermediate initiating function has been called.
|
||||
*
|
||||
* @param args Arguments forwarded to the completion handler function.
|
||||
*/
|
||||
template <class... Args>
|
||||
void complete_now(Args&&... args) {
|
||||
m_work_guard_1.reset();
|
||||
m_handler(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
Handler m_handler;
|
||||
boost::asio::executor_work_guard<Executor1> m_work_guard_1;
|
||||
};
|
||||
|
||||
template <class Handler, class Stream, class MutableBufferSequence, class Allocator = std::allocator<void>>
|
||||
class AsyncReadOperation : public AsyncBase<Handler, typename Stream::executor_type, Allocator> {
|
||||
public:
|
||||
/**
|
||||
* Construct and invoke an AsyncReadOperation.
|
||||
*
|
||||
* @param handler Handler function to be called upon completion.
|
||||
* @param stream The stream from which the data will be read
|
||||
* @param buffers The buffers into which the data will be read.
|
||||
* @param ec Optional error code; used to report an error to the handler function.
|
||||
*/
|
||||
template <class HandlerT>
|
||||
AsyncReadOperation(HandlerT&& handler,
|
||||
Stream& stream,
|
||||
const MutableBufferSequence& buffers,
|
||||
const boost::system::error_code& ec = {}) :
|
||||
AsyncBase<Handler, typename Stream::executor_type, Allocator>(std::forward<HandlerT>(handler),
|
||||
stream.get_executor()),
|
||||
m_stream(stream),
|
||||
m_buffers(buffers),
|
||||
m_decodedBytes(0) {
|
||||
this->operator()(ec, std::size_t(0), false);
|
||||
}
|
||||
|
||||
AsyncReadOperation(AsyncReadOperation&&) = default;
|
||||
|
||||
void operator()(boost::system::error_code ec, std::size_t bytes_transferred, bool isContinuation = true) {
|
||||
reenter(this) {
|
||||
if(bytes_transferred > 0 && !ec) {
|
||||
// We have received encrypted data from the network, now hand it to TLS::Channel for decryption.
|
||||
boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytes_transferred};
|
||||
m_stream.process_encrypted_data(read_buffer, ec);
|
||||
}
|
||||
|
||||
if(m_stream.shutdown_received()) {
|
||||
// we just received a 'close_notify' from the peer and don't expect any more data
|
||||
ec = boost::asio::error::eof;
|
||||
} else if(ec == boost::asio::error::eof) {
|
||||
// we did not expect this disconnection from the peer
|
||||
ec.assign(StreamError::StreamTruncated, std::generic_category());
|
||||
}
|
||||
|
||||
if(!m_stream.has_received_data() && !ec && boost::asio::buffer_size(m_buffers) > 0) {
|
||||
// The channel did not decrypt a complete record yet, we need more data from the socket.
|
||||
m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_stream.has_received_data() && !ec) {
|
||||
// The channel has decrypted a TLS record, now copy it to the output buffers.
|
||||
m_decodedBytes = m_stream.copy_received_data(m_buffers);
|
||||
}
|
||||
|
||||
if(!isContinuation) {
|
||||
// Make sure the handler is not called without an intermediate initiating function.
|
||||
// "Reading" into a zero-byte buffer will complete immediately.
|
||||
m_ec = ec;
|
||||
yield m_stream.next_layer().async_read_some(boost::asio::mutable_buffer(), std::move(*this));
|
||||
ec = m_ec;
|
||||
}
|
||||
|
||||
this->complete_now(ec, m_decodedBytes);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Stream& m_stream;
|
||||
MutableBufferSequence m_buffers;
|
||||
std::size_t m_decodedBytes;
|
||||
boost::system::error_code m_ec;
|
||||
};
|
||||
|
||||
template <typename Handler, class Stream, class Allocator = std::allocator<void>>
|
||||
class AsyncWriteOperation : public AsyncBase<Handler, typename Stream::executor_type, Allocator> {
|
||||
public:
|
||||
/**
|
||||
* Construct and invoke an AsyncWriteOperation.
|
||||
*
|
||||
* @param handler Handler function to be called upon completion.
|
||||
* @param stream The stream from which the data will be read
|
||||
* @param plainBytesTransferred Number of bytes to be reported to the user-provided handler function as
|
||||
* bytes_transferred. This needs to be provided since the amount of plaintext data
|
||||
* consumed from the input buffer can differ from the amount of encrypted data written
|
||||
* to the next layer.
|
||||
* @param ec Optional error code; used to report an error to the handler function.
|
||||
*/
|
||||
template <class HandlerT>
|
||||
AsyncWriteOperation(HandlerT&& handler,
|
||||
Stream& stream,
|
||||
std::size_t plainBytesTransferred,
|
||||
const boost::system::error_code& ec = {}) :
|
||||
AsyncBase<Handler, typename Stream::executor_type, Allocator>(std::forward<HandlerT>(handler),
|
||||
stream.get_executor()),
|
||||
m_stream(stream),
|
||||
m_plainBytesTransferred(plainBytesTransferred) {
|
||||
this->operator()(ec, std::size_t(0), false);
|
||||
}
|
||||
|
||||
AsyncWriteOperation(AsyncWriteOperation&&) = default;
|
||||
|
||||
void operator()(boost::system::error_code ec, std::size_t bytes_transferred, bool isContinuation = true) {
|
||||
reenter(this) {
|
||||
// mark the number of encrypted bytes sent to the network as "consumed"
|
||||
// Note: bytes_transferred will be zero on first call
|
||||
m_stream.consume_send_buffer(bytes_transferred);
|
||||
|
||||
if(m_stream.has_data_to_send() && !ec) {
|
||||
m_stream.next_layer().async_write_some(m_stream.send_buffer(), std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
if(ec == boost::asio::error::eof && !m_stream.shutdown_received()) {
|
||||
// transport layer was closed by peer without receiving 'close_notify'
|
||||
ec.assign(StreamError::StreamTruncated, std::generic_category());
|
||||
}
|
||||
|
||||
if(!isContinuation) {
|
||||
// Make sure the handler is not called without an intermediate initiating function.
|
||||
// "Writing" to a zero-byte buffer will complete immediately.
|
||||
m_ec = ec;
|
||||
yield m_stream.next_layer().async_write_some(boost::asio::const_buffer(), std::move(*this));
|
||||
ec = m_ec;
|
||||
}
|
||||
|
||||
// The size of the sent TLS record can differ from the size of the payload due to TLS encryption. We need to
|
||||
// tell the handler how many bytes of the original data we already processed.
|
||||
this->complete_now(ec, m_plainBytesTransferred);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Stream& m_stream;
|
||||
std::size_t m_plainBytesTransferred;
|
||||
boost::system::error_code m_ec;
|
||||
};
|
||||
|
||||
template <class Handler, class Stream, class Allocator = std::allocator<void>>
|
||||
class AsyncHandshakeOperation : public AsyncBase<Handler, typename Stream::executor_type, Allocator> {
|
||||
public:
|
||||
/**
|
||||
* Construct and invoke an AsyncHandshakeOperation.
|
||||
*
|
||||
* @param handler Handler function to be called upon completion.
|
||||
* @param stream The stream from which the data will be read
|
||||
* @param ec Optional error code; used to report an error to the handler function.
|
||||
*/
|
||||
template <class HandlerT>
|
||||
AsyncHandshakeOperation(HandlerT&& handler, Stream& stream, const boost::system::error_code& ec = {}) :
|
||||
AsyncBase<Handler, typename Stream::executor_type, Allocator>(std::forward<HandlerT>(handler),
|
||||
stream.get_executor()),
|
||||
m_stream(stream) {
|
||||
this->operator()(ec, std::size_t(0), false);
|
||||
}
|
||||
|
||||
AsyncHandshakeOperation(AsyncHandshakeOperation&&) = default;
|
||||
|
||||
void operator()(boost::system::error_code ec, std::size_t bytesTransferred, bool isContinuation = true) {
|
||||
reenter(this) {
|
||||
if(ec == boost::asio::error::eof) {
|
||||
ec.assign(StreamError::StreamTruncated, std::generic_category());
|
||||
}
|
||||
|
||||
if(bytesTransferred > 0 && !ec) {
|
||||
// Provide encrypted TLS data received from the network to TLS::Channel for decryption
|
||||
boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytesTransferred};
|
||||
m_stream.process_encrypted_data(read_buffer, ec);
|
||||
}
|
||||
|
||||
if(m_stream.has_data_to_send() && !ec) {
|
||||
// Write encrypted TLS data provided by the TLS::Channel on the wire
|
||||
|
||||
// Note: we construct `AsyncWriteOperation` with 0 as its last parameter (`plainBytesTransferred`). This
|
||||
// operation will eventually call `*this` as its own handler, passing the 0 back to this call operator.
|
||||
// This is necessary because the check of `bytesTransferred > 0` assumes that `bytesTransferred` bytes
|
||||
// were just read and are available in input_buffer for further processing.
|
||||
AsyncWriteOperation<AsyncHandshakeOperation<typename std::decay<Handler>::type, Stream, Allocator>,
|
||||
Stream,
|
||||
Allocator>
|
||||
op{std::move(*this), m_stream, 0};
|
||||
return;
|
||||
}
|
||||
|
||||
if(!m_stream.native_handle()->is_active() && !ec) {
|
||||
// Read more encrypted TLS data from the network
|
||||
m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isContinuation) {
|
||||
// Make sure the handler is not called without an intermediate initiating function.
|
||||
// "Reading" into a zero-byte buffer will complete immediately.
|
||||
m_ec = ec;
|
||||
yield m_stream.next_layer().async_read_some(boost::asio::mutable_buffer(), std::move(*this));
|
||||
ec = m_ec;
|
||||
}
|
||||
|
||||
this->complete_now(ec);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Stream& m_stream;
|
||||
boost::system::error_code m_ec;
|
||||
};
|
||||
|
||||
} // namespace Botan::TLS::detail
|
||||
|
||||
#include <asio/unyield.hpp>
|
||||
|
||||
#endif // BOTAN_ASIO_ASYNC_OPS_H_
|
90
include/botan_asio/asio_context.h
Normal file
90
include/botan_asio/asio_context.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* TLS Context
|
||||
* (C) 2018-2020 Jack Lloyd
|
||||
* 2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
|
||||
*
|
||||
* Botan is released under the Simplified BSD License (see license.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOTAN_ASIO_TLS_CONTEXT_H_
|
||||
#define BOTAN_ASIO_TLS_CONTEXT_H_
|
||||
|
||||
#include <botan_all.h>
|
||||
#include <functional>
|
||||
|
||||
namespace Botan::TLS {
|
||||
|
||||
namespace detail {
|
||||
template <typename FunT>
|
||||
struct fn_signature_helper : public std::false_type {};
|
||||
|
||||
template <typename R, typename D, typename... Args>
|
||||
struct fn_signature_helper<R (D::*)(Args...)> {
|
||||
using type = std::function<R(Args...)>;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* A helper class to initialize and configure Botan::TLS::Stream
|
||||
*/
|
||||
class Context {
|
||||
public:
|
||||
// statically extract the function signature type from Callbacks::tls_verify_cert_chain
|
||||
// and reuse it as an std::function<> for the verify callback signature
|
||||
/**
|
||||
* The signature of the callback function should correspond to the signature of
|
||||
* Callbacks::tls_verify_cert_chain
|
||||
*/
|
||||
using Verify_Callback = detail::fn_signature_helper<decltype(&Callbacks::tls_verify_cert_chain)>::type;
|
||||
|
||||
Context(std::shared_ptr<Credentials_Manager> credentials_manager,
|
||||
std::shared_ptr<RandomNumberGenerator> rng,
|
||||
std::shared_ptr<Session_Manager> session_manager,
|
||||
std::shared_ptr<const Policy> policy,
|
||||
Server_Information server_info = Server_Information()) :
|
||||
m_credentials_manager(credentials_manager),
|
||||
m_rng(rng),
|
||||
m_session_manager(session_manager),
|
||||
m_policy(policy),
|
||||
m_server_info(std::move(server_info)) {}
|
||||
|
||||
virtual ~Context() = default;
|
||||
|
||||
Context(Context&&) = default;
|
||||
Context(const Context&) = delete;
|
||||
Context& operator=(const Context&) = delete;
|
||||
Context& operator=(Context&&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Override the tls_verify_cert_chain callback
|
||||
*
|
||||
* This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
|
||||
* used in the handshake.
|
||||
* Using this function is equivalent to setting the callback via @see Botan::TLS::Stream::set_verify_callback
|
||||
*
|
||||
* @note This function should only be called before initiating the TLS handshake
|
||||
*/
|
||||
void set_verify_callback(Verify_Callback callback) { m_verify_callback = std::move(callback); }
|
||||
|
||||
bool has_verify_callback() const { return static_cast<bool>(m_verify_callback); }
|
||||
|
||||
const Verify_Callback& get_verify_callback() const { return m_verify_callback; }
|
||||
|
||||
void set_server_info(Server_Information server_info) { m_server_info = std::move(server_info); }
|
||||
|
||||
protected:
|
||||
template <class S, class C>
|
||||
friend class Stream;
|
||||
|
||||
std::shared_ptr<Credentials_Manager> m_credentials_manager;
|
||||
std::shared_ptr<RandomNumberGenerator> m_rng;
|
||||
std::shared_ptr<Session_Manager> m_session_manager;
|
||||
std::shared_ptr<const Policy> m_policy;
|
||||
|
||||
Server_Information m_server_info;
|
||||
Verify_Callback m_verify_callback;
|
||||
};
|
||||
|
||||
} // namespace Botan::TLS
|
||||
|
||||
#endif // BOTAN_ASIO_TLS_CONTEXT_H_
|
125
include/botan_asio/asio_error.h
Normal file
125
include/botan_asio/asio_error.h
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* TLS Stream Errors
|
||||
* (C) 2018-2020 Jack Lloyd
|
||||
* 2018-2020 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
|
||||
*
|
||||
* Botan is released under the Simplified BSD License (see license.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOTAN_ASIO_ERROR_H_
|
||||
#define BOTAN_ASIO_ERROR_H_
|
||||
|
||||
#include <botan_all.h>
|
||||
#include <asio/error_code.hpp>
|
||||
|
||||
namespace boost{
|
||||
namespace asio = ::asio;
|
||||
namespace system {
|
||||
template <typename T>
|
||||
struct is_error_code_enum { static const bool value = false; };
|
||||
|
||||
typedef asio::error_category error_category;
|
||||
typedef asio::error_code error_code;
|
||||
}
|
||||
namespace beast {
|
||||
using flat_buffer = asio::streambuf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace Botan {
|
||||
namespace TLS {
|
||||
|
||||
enum StreamError { StreamTruncated = 1 };
|
||||
|
||||
//! @brief An error category for errors from the TLS::Stream
|
||||
struct StreamCategory : public boost::system::error_category {
|
||||
virtual ~StreamCategory() = default;
|
||||
|
||||
const char* name() const noexcept override { return "Botan TLS Stream"; }
|
||||
|
||||
std::string message(int value) const override {
|
||||
if(value == StreamTruncated) {
|
||||
return "stream truncated";
|
||||
} else {
|
||||
return "generic error";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline const StreamCategory& botan_stream_category() {
|
||||
static StreamCategory category;
|
||||
return category;
|
||||
}
|
||||
|
||||
inline boost::system::error_code make_error_code(Botan::TLS::StreamError e) {
|
||||
return boost::system::error_code(static_cast<int>(e), Botan::TLS::botan_stream_category());
|
||||
}
|
||||
|
||||
//! @brief An error category for TLS alerts
|
||||
struct BotanAlertCategory : boost::system::error_category {
|
||||
virtual ~BotanAlertCategory() = default;
|
||||
|
||||
const char* name() const noexcept override { return "Botan TLS Alert"; }
|
||||
|
||||
std::string message(int ev) const override {
|
||||
Botan::TLS::Alert alert(static_cast<Botan::TLS::Alert::Type>(ev));
|
||||
return alert.type_string();
|
||||
}
|
||||
};
|
||||
|
||||
inline const BotanAlertCategory& botan_alert_category() noexcept {
|
||||
static BotanAlertCategory category;
|
||||
return category;
|
||||
}
|
||||
|
||||
inline boost::system::error_code make_error_code(Botan::TLS::Alert::Type c) {
|
||||
return boost::system::error_code(static_cast<int>(c), Botan::TLS::botan_alert_category());
|
||||
}
|
||||
|
||||
} // namespace TLS
|
||||
|
||||
//! @brief An error category for errors from Botan (other than TLS alerts)
|
||||
struct BotanErrorCategory : boost::system::error_category {
|
||||
virtual ~BotanErrorCategory() = default;
|
||||
|
||||
const char* name() const noexcept override { return "Botan"; }
|
||||
|
||||
std::string message(int ev) const override { return Botan::to_string(static_cast<Botan::ErrorType>(ev)); }
|
||||
};
|
||||
|
||||
inline const BotanErrorCategory& botan_category() noexcept {
|
||||
static BotanErrorCategory category;
|
||||
return category;
|
||||
}
|
||||
|
||||
inline boost::system::error_code make_error_code(Botan::ErrorType e) {
|
||||
return boost::system::error_code(static_cast<int>(e), Botan::botan_category());
|
||||
}
|
||||
|
||||
} // namespace Botan
|
||||
|
||||
/*
|
||||
* Add a template specialization of `is_error_code_enum` for each kind of error to allow automatic conversion to an
|
||||
* error code.
|
||||
*/
|
||||
namespace boost::system {
|
||||
|
||||
template <>
|
||||
struct is_error_code_enum<Botan::TLS::Alert::Type> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct is_error_code_enum<Botan::TLS::StreamError> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct is_error_code_enum<Botan::ErrorType> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
} // namespace boost::system
|
||||
|
||||
#endif // BOTAN_ASIO_ERROR_H_
|
740
include/botan_asio/asio_stream.h
Normal file
740
include/botan_asio/asio_stream.h
Normal file
@ -0,0 +1,740 @@
|
||||
/*
|
||||
* TLS ASIO Stream
|
||||
* (C) 2018-2021 Jack Lloyd
|
||||
* 2018-2021 Hannes Rantzsch, Tim Oesterreich, Rene Meusel
|
||||
*
|
||||
* Botan is released under the Simplified BSD License (see license.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOTAN_ASIO_STREAM_H_
|
||||
#define BOTAN_ASIO_STREAM_H_
|
||||
|
||||
#include <botan_all.h>
|
||||
#include <asio.hpp>
|
||||
|
||||
#include "asio_async_ops.h"
|
||||
#include "asio_context.h"
|
||||
#include "asio_error.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Botan::TLS {
|
||||
|
||||
/**
|
||||
* @brief boost::asio compatible SSL/TLS stream
|
||||
*
|
||||
* @tparam StreamLayer type of the next layer, usually a network socket
|
||||
* @tparam ChannelT type of the native_handle, defaults to TLS::Channel, only needed for testing purposes
|
||||
*/
|
||||
template <class StreamLayer, class ChannelT = Channel>
|
||||
class Stream {
|
||||
public:
|
||||
//! \name construction
|
||||
//! @{
|
||||
|
||||
/**
|
||||
* @brief Construct a new Stream
|
||||
*
|
||||
* @param context The context parameter is used to set up the underlying native handle. Using code is
|
||||
* responsible for lifetime management of the context and must ensure that it is available for the
|
||||
* lifetime of the stream.
|
||||
* @param args Arguments to be forwarded to the construction of the next layer.
|
||||
*/
|
||||
template <typename... Args>
|
||||
explicit Stream(std::shared_ptr<Context> context, Args&&... args) :
|
||||
m_context(context),
|
||||
m_nextLayer(std::forward<Args>(args)...),
|
||||
m_core(std::make_shared<StreamCore>(context)),
|
||||
m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0'),
|
||||
m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size()) {}
|
||||
|
||||
/**
|
||||
* @brief Construct a new Stream
|
||||
*
|
||||
* Convenience overload for boost::asio::ssl::stream compatibility.
|
||||
*
|
||||
* @param arg This argument is forwarded to the construction of the next layer.
|
||||
* @param context The context parameter is used to set up the underlying native handle. Using code is
|
||||
* responsible for lifetime management of the context and must ensure that is available for the
|
||||
* lifetime of the stream.
|
||||
*/
|
||||
template <typename Arg>
|
||||
explicit Stream(Arg&& arg, std::shared_ptr<Context> context) :
|
||||
m_context(context),
|
||||
m_nextLayer(std::forward<Arg>(arg)),
|
||||
m_core(std::make_shared<StreamCore>(context)),
|
||||
m_input_buffer_space(MAX_CIPHERTEXT_SIZE, '\0'),
|
||||
m_input_buffer(m_input_buffer_space.data(), m_input_buffer_space.size()) {}
|
||||
|
||||
virtual ~Stream() = default;
|
||||
|
||||
Stream(Stream&& other) = default;
|
||||
Stream& operator=(Stream&& other) = default;
|
||||
|
||||
Stream(const Stream& other) = delete;
|
||||
Stream& operator=(const Stream& other) = delete;
|
||||
|
||||
//! @}
|
||||
//! \name boost::asio accessor methods
|
||||
//! @{
|
||||
|
||||
using next_layer_type = typename std::remove_reference<StreamLayer>::type;
|
||||
|
||||
const next_layer_type& next_layer() const { return m_nextLayer; }
|
||||
|
||||
next_layer_type& next_layer() { return m_nextLayer; }
|
||||
|
||||
#if VERSION >= 107000
|
||||
/*
|
||||
* From Boost 1.70 onwards Beast types no longer provide public access to the member function `lowest_layer()`.
|
||||
* Instead, the new free-standing functions in Beast need to be used.
|
||||
* See also: https://github.com/boostorg/beast/commit/6a658b5c3a36f8d58334f8b6582c01c3e87768ae
|
||||
*/
|
||||
using lowest_layer_type = typename boost::beast::lowest_layer_type<StreamLayer>;
|
||||
|
||||
lowest_layer_type& lowest_layer() { return boost::beast::get_lowest_layer(m_nextLayer); }
|
||||
|
||||
const lowest_layer_type& lowest_layer() const { return boost::beast::get_lowest_layer(m_nextLayer); }
|
||||
#else
|
||||
using lowest_layer_type = typename next_layer_type::lowest_layer_type;
|
||||
|
||||
lowest_layer_type& lowest_layer() { return m_nextLayer.lowest_layer(); }
|
||||
|
||||
const lowest_layer_type& lowest_layer() const { return m_nextLayer.lowest_layer(); }
|
||||
#endif
|
||||
|
||||
using executor_type = typename next_layer_type::executor_type;
|
||||
|
||||
executor_type get_executor() noexcept { return m_nextLayer.get_executor(); }
|
||||
|
||||
using native_handle_type = typename std::add_pointer<ChannelT>::type;
|
||||
|
||||
native_handle_type native_handle() {
|
||||
if(m_native_handle == nullptr) {
|
||||
throw Invalid_State("Invalid handshake state");
|
||||
}
|
||||
return m_native_handle.get();
|
||||
}
|
||||
|
||||
//! @}
|
||||
//! \name configuration and callback setters
|
||||
//! @{
|
||||
|
||||
/**
|
||||
* @brief Override the tls_verify_cert_chain callback
|
||||
*
|
||||
* This changes the verify_callback in the stream's TLS::Context, and hence the tls_verify_cert_chain callback
|
||||
* used in the handshake.
|
||||
* Using this function is equivalent to setting the callback via @see Botan::TLS::Context::set_verify_callback
|
||||
*
|
||||
* @note This function should only be called before initiating the TLS handshake
|
||||
*/
|
||||
void set_verify_callback(Context::Verify_Callback callback) {
|
||||
m_context->set_verify_callback(std::move(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compatibility overload of @ref set_verify_callback
|
||||
*
|
||||
* @param callback the callback implementation
|
||||
* @param ec This parameter is unused.
|
||||
*/
|
||||
void set_verify_callback(Context::Verify_Callback callback, boost::system::error_code& ec) {
|
||||
BOTAN_UNUSED(ec);
|
||||
m_context->set_verify_callback(std::move(callback));
|
||||
}
|
||||
|
||||
//! @throws Not_Implemented
|
||||
void set_verify_depth(int depth) {
|
||||
BOTAN_UNUSED(depth);
|
||||
throw Not_Implemented("set_verify_depth is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Not Implemented.
|
||||
* @param depth the desired verification depth
|
||||
* @param ec Will be set to `Botan::ErrorType::NotImplemented`
|
||||
*/
|
||||
void set_verify_depth(int depth, boost::system::error_code& ec) {
|
||||
BOTAN_UNUSED(depth);
|
||||
ec.assign((int)ErrorType::NotImplemented, std::generic_category());
|
||||
}
|
||||
|
||||
//! @throws Not_Implemented
|
||||
template <typename verify_mode>
|
||||
void set_verify_mode(verify_mode v) {
|
||||
BOTAN_UNUSED(v);
|
||||
throw Not_Implemented("set_verify_mode is not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Not Implemented.
|
||||
* @param v the desired verify mode
|
||||
* @param ec Will be set to `Botan::ErrorType::NotImplemented`
|
||||
*/
|
||||
template <typename verify_mode>
|
||||
void set_verify_mode(verify_mode v, boost::system::error_code& ec) {
|
||||
BOTAN_UNUSED(v);
|
||||
ec.assign((int)ErrorType::NotImplemented, std::generic_category());
|
||||
}
|
||||
|
||||
//! @}
|
||||
//! \name handshake methods
|
||||
//! @{
|
||||
|
||||
/**
|
||||
* @brief Performs SSL handshaking.
|
||||
*
|
||||
* The function call will block until handshaking is complete or an error occurs.
|
||||
*
|
||||
* @param side The type of handshaking to be performed, i.e. as a client or as a server.
|
||||
* @throws boost::system::system_error if error occured
|
||||
*/
|
||||
void handshake(Connection_Side side) {
|
||||
boost::system::error_code ec;
|
||||
handshake(side, ec);
|
||||
boost::asio::detail::throw_error(ec, "handshake");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs SSL handshaking.
|
||||
*
|
||||
* The function call will block until handshaking is complete or an error occurs.
|
||||
*
|
||||
* @param side The type of handshaking to be performed, i.e. as a client or as a server.
|
||||
* @param ec Set to indicate what error occurred, if any.
|
||||
*/
|
||||
void handshake(Connection_Side side, boost::system::error_code& ec) {
|
||||
setup_native_handle(side, ec);
|
||||
|
||||
if(side == Connection_Side::Client) {
|
||||
// send client hello, which was written to the send buffer on client instantiation
|
||||
send_pending_encrypted_data(ec);
|
||||
}
|
||||
|
||||
while(!native_handle()->is_active() && !ec) {
|
||||
boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
|
||||
if(ec) {
|
||||
return;
|
||||
}
|
||||
|
||||
process_encrypted_data(read_buffer, ec);
|
||||
|
||||
send_pending_encrypted_data(ec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Starts an asynchronous SSL handshake.
|
||||
*
|
||||
* This function call always returns immediately.
|
||||
*
|
||||
* @param side The type of handshaking to be performed, i.e. as a client or as a server.
|
||||
* @param completion_token The completion handler to be called when the handshake operation completes.
|
||||
* The completion signature of the handler must be: void(boost::system::error_code).
|
||||
*/
|
||||
template <typename CompletionToken>
|
||||
auto async_handshake(Botan::TLS::Connection_Side side, CompletionToken&& completion_token) {
|
||||
return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code)>(
|
||||
[this](auto&& completion_handler, TLS::Connection_Side connection_side) {
|
||||
using completion_handler_t = std::decay_t<decltype(completion_handler)>;
|
||||
|
||||
ASIO_HANDSHAKE_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
|
||||
|
||||
boost::system::error_code ec;
|
||||
setup_native_handle(connection_side, ec);
|
||||
|
||||
detail::AsyncHandshakeOperation<completion_handler_t, Stream> op{
|
||||
std::forward<completion_handler_t>(completion_handler), *this, ec};
|
||||
},
|
||||
completion_token,
|
||||
side);
|
||||
}
|
||||
|
||||
//! @throws Not_Implemented
|
||||
template <typename ConstBufferSequence, typename BufferedHandshakeHandler>
|
||||
ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, void(boost::system::error_code, std::size_t))
|
||||
async_handshake(Connection_Side side, const ConstBufferSequence& buffers, BufferedHandshakeHandler&& handler) {
|
||||
BOTAN_UNUSED(side, buffers, handler);
|
||||
ASIO_HANDSHAKE_HANDLER_CHECK(BufferedHandshakeHandler, handler) type_check;
|
||||
throw Not_Implemented("buffered async handshake is not implemented");
|
||||
}
|
||||
|
||||
//! @}
|
||||
//! \name shutdown methods
|
||||
//! @{
|
||||
|
||||
/**
|
||||
* @brief Shut down SSL on the stream.
|
||||
*
|
||||
* This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
|
||||
* or an error occurs. Note that this will not close the lowest layer.
|
||||
*
|
||||
* Note that this can be used in reaction of a received shutdown alert from the peer.
|
||||
*
|
||||
* @param ec Set to indicate what error occured, if any.
|
||||
*/
|
||||
void shutdown(boost::system::error_code& ec) {
|
||||
try_with_error_code([&] { native_handle()->close(); }, ec);
|
||||
|
||||
send_pending_encrypted_data(ec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shut down SSL on the stream.
|
||||
*
|
||||
* This function is used to shut down SSL on the stream. The function call will block until SSL has been shut down
|
||||
* or an error occurs. Note that this will not close the lowest layer.
|
||||
*
|
||||
* Note that this can be used in reaction of a received shutdown alert from the peer.
|
||||
*
|
||||
* @throws boost::system::system_error if error occured
|
||||
*/
|
||||
void shutdown() {
|
||||
boost::system::error_code ec;
|
||||
shutdown(ec);
|
||||
boost::asio::detail::throw_error(ec, "shutdown");
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Internal wrapper type to adapt the expected signature of `async_shutdown` to the completion handler
|
||||
* signature of `AsyncWriteOperation`.
|
||||
*
|
||||
* This is boilerplate to ignore the `size_t` parameter that is passed to the completion handler of
|
||||
* `AsyncWriteOperation`. Note that it needs to retain the wrapped handler's executor.
|
||||
*/
|
||||
template <typename Handler, typename Executor>
|
||||
struct Wrapper {
|
||||
void operator()(boost::system::error_code ec, std::size_t) { handler(ec); }
|
||||
|
||||
using executor_type = boost::asio::associated_executor_t<Handler, Executor>;
|
||||
|
||||
executor_type get_executor() const noexcept {
|
||||
return boost::asio::get_associated_executor(handler, io_executor);
|
||||
}
|
||||
|
||||
using allocator_type = boost::asio::associated_allocator_t<Handler>;
|
||||
|
||||
allocator_type get_allocator() const noexcept { return boost::asio::get_associated_allocator(handler); }
|
||||
|
||||
Handler handler;
|
||||
Executor io_executor;
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Asynchronously shut down SSL on the stream.
|
||||
*
|
||||
* This function call always returns immediately.
|
||||
*
|
||||
* Note that this can be used in reaction of a received shutdown alert from the peer.
|
||||
*
|
||||
* @param completion_token The completion handler to be called when the shutdown operation completes.
|
||||
* The completion signature of the handler must be: void(boost::system::error_code).
|
||||
*/
|
||||
template <typename CompletionToken>
|
||||
auto async_shutdown(CompletionToken&& completion_token) {
|
||||
return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code)>(
|
||||
[this](auto&& completion_handler) {
|
||||
using completion_handler_t = std::decay_t<decltype(completion_handler)>;
|
||||
|
||||
ASIO_SHUTDOWN_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
|
||||
|
||||
boost::system::error_code ec;
|
||||
try_with_error_code([&] { native_handle()->close(); }, ec);
|
||||
|
||||
using write_handler_t = Wrapper<completion_handler_t, typename Stream::executor_type>;
|
||||
|
||||
TLS::detail::AsyncWriteOperation<write_handler_t, Stream> op{
|
||||
write_handler_t{std::forward<completion_handler_t>(completion_handler), get_executor()},
|
||||
*this,
|
||||
boost::asio::buffer_size(send_buffer()),
|
||||
ec};
|
||||
},
|
||||
completion_token);
|
||||
}
|
||||
|
||||
//! @}
|
||||
//! \name I/O methods
|
||||
//! @{
|
||||
|
||||
/**
|
||||
* @brief Read some data from the stream.
|
||||
*
|
||||
* The function call will block until one or more bytes of data has been read successfully, or until an error
|
||||
* occurs.
|
||||
*
|
||||
* @param buffers The buffers into which the data will be read.
|
||||
* @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
|
||||
* has closed the connection but did not properly shut down the SSL connection.
|
||||
* @return The number of bytes read. Returns 0 if an error occurred.
|
||||
*/
|
||||
template <typename MutableBufferSequence>
|
||||
std::size_t read_some(const MutableBufferSequence& buffers, boost::system::error_code& ec) {
|
||||
if(has_received_data()) {
|
||||
return copy_received_data(buffers);
|
||||
}
|
||||
|
||||
boost::asio::const_buffer read_buffer{input_buffer().data(), m_nextLayer.read_some(input_buffer(), ec)};
|
||||
if(ec) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
process_encrypted_data(read_buffer, ec);
|
||||
|
||||
if(ec) // something went wrong in process_encrypted_data()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(shutdown_received()) {
|
||||
// we just received a 'close_notify' from the peer and don't expect any more data
|
||||
ec = boost::asio::error::eof;
|
||||
} else if(ec == boost::asio::error::eof) {
|
||||
// we did not expect this disconnection from the peer
|
||||
ec.assign(StreamError::StreamTruncated, std::generic_category());
|
||||
}
|
||||
|
||||
return !ec ? copy_received_data(buffers) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read some data from the stream.
|
||||
*
|
||||
* The function call will block until one or more bytes of data has been read successfully, or until an error
|
||||
* occurs.
|
||||
*
|
||||
* @param buffers The buffers into which the data will be read.
|
||||
* @return The number of bytes read. Returns 0 if an error occurred.
|
||||
* @throws boost::system::system_error if error occured
|
||||
*/
|
||||
template <typename MutableBufferSequence>
|
||||
std::size_t read_some(const MutableBufferSequence& buffers) {
|
||||
boost::system::error_code ec;
|
||||
const auto n = read_some(buffers, ec);
|
||||
boost::asio::detail::throw_error(ec, "read_some");
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write some data to the stream.
|
||||
*
|
||||
* The function call will block until one or more bytes of data has been written successfully, or until an error
|
||||
* occurs.
|
||||
*
|
||||
* @param buffers The data to be written.
|
||||
* @param ec Set to indicate what error occurred, if any.
|
||||
* @return The number of bytes processed from the input buffers.
|
||||
*/
|
||||
template <typename ConstBufferSequence>
|
||||
std::size_t write_some(const ConstBufferSequence& buffers, boost::system::error_code& ec) {
|
||||
tls_encrypt(buffers, ec);
|
||||
send_pending_encrypted_data(ec);
|
||||
return !ec ? boost::asio::buffer_size(buffers) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write some data to the stream.
|
||||
*
|
||||
* The function call will block until one or more bytes of data has been written successfully, or until an error
|
||||
* occurs.
|
||||
*
|
||||
* @param buffers The data to be written.
|
||||
* @return The number of bytes written.
|
||||
* @throws boost::system::system_error if error occured
|
||||
*/
|
||||
template <typename ConstBufferSequence>
|
||||
std::size_t write_some(const ConstBufferSequence& buffers) {
|
||||
boost::system::error_code ec;
|
||||
const auto n = write_some(buffers, ec);
|
||||
boost::asio::detail::throw_error(ec, "write_some");
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start an asynchronous write. The function call always returns immediately.
|
||||
*
|
||||
* @param buffers The data to be written.
|
||||
* @param completion_token The completion handler to be called when the write operation completes. Copies of the
|
||||
* handler will be made as required. The completion signature of the handler must be:
|
||||
* void(boost::system::error_code, std::size_t).
|
||||
*/
|
||||
template <typename ConstBufferSequence, typename CompletionToken>
|
||||
auto async_write_some(const ConstBufferSequence& buffers, CompletionToken&& completion_token) {
|
||||
return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code, std::size_t)>(
|
||||
[this](auto&& completion_handler, const auto& bufs) {
|
||||
using completion_handler_t = std::decay_t<decltype(completion_handler)>;
|
||||
|
||||
ASIO_WRITE_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
|
||||
|
||||
boost::system::error_code ec;
|
||||
tls_encrypt(bufs, ec);
|
||||
|
||||
if(ec) {
|
||||
// we cannot be sure how many bytes were committed here so clear the send_buffer and let the
|
||||
// AsyncWriteOperation call the handler with the error_code set
|
||||
consume_send_buffer(m_core->send_buffer.size());
|
||||
}
|
||||
|
||||
detail::AsyncWriteOperation<completion_handler_t, Stream> op{
|
||||
std::forward<completion_handler_t>(completion_handler),
|
||||
*this,
|
||||
ec ? 0 : boost::asio::buffer_size(bufs),
|
||||
ec};
|
||||
},
|
||||
completion_token,
|
||||
buffers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start an asynchronous read. The function call always returns immediately.
|
||||
*
|
||||
* @param buffers The buffers into which the data will be read. Although the buffers object may be copied as
|
||||
* necessary, ownership of the underlying buffers is retained by the caller, which must guarantee
|
||||
* that they remain valid until the handler is called.
|
||||
* @param completion_token The completion handler to be called when the read operation completes. The completion
|
||||
* signature of the handler must be: void(boost::system::error_code, std::size_t).
|
||||
*/
|
||||
template <typename MutableBufferSequence, typename CompletionToken>
|
||||
auto async_read_some(const MutableBufferSequence& buffers, CompletionToken&& completion_token) {
|
||||
return boost::asio::async_initiate<CompletionToken, void(boost::system::error_code, std::size_t)>(
|
||||
[this](auto&& completion_handler, const auto& bufs) {
|
||||
using completion_handler_t = std::decay_t<decltype(completion_handler)>;
|
||||
|
||||
ASIO_READ_HANDLER_CHECK(completion_handler_t, completion_handler) type_check;
|
||||
|
||||
detail::AsyncReadOperation<completion_handler_t, Stream, MutableBufferSequence> op{
|
||||
std::forward<completion_handler_t>(completion_handler), *this, bufs};
|
||||
},
|
||||
completion_token,
|
||||
buffers);
|
||||
}
|
||||
|
||||
//! @}
|
||||
|
||||
//! @brief Indicates whether a close_notify alert has been received from the peer.
|
||||
//!
|
||||
//! Note that we cannot m_core.is_closed_for_reading() because this wants to
|
||||
//! explicitly check that the peer sent close_notify.
|
||||
bool shutdown_received() const { return m_core->shutdown_received; }
|
||||
|
||||
protected:
|
||||
template <class H, class S, class M, class A>
|
||||
friend class detail::AsyncReadOperation;
|
||||
template <class H, class S, class A>
|
||||
friend class detail::AsyncWriteOperation;
|
||||
template <class H, class S, class A>
|
||||
friend class detail::AsyncHandshakeOperation;
|
||||
|
||||
/**
|
||||
* @brief Helper class that implements TLS::Callbacks
|
||||
*
|
||||
* This class is provided to the stream's native_handle (TLS::Channel) and implements the callback
|
||||
* functions triggered by the native_handle.
|
||||
*/
|
||||
class StreamCore : public TLS::Callbacks {
|
||||
public:
|
||||
StreamCore(std::weak_ptr<Botan::TLS::Context> context) : shutdown_received(false), m_context(context) {}
|
||||
|
||||
~StreamCore() override = default;
|
||||
|
||||
void tls_emit_data(std::span<const uint8_t> data) override {
|
||||
send_buffer.commit(boost::asio::buffer_copy(send_buffer.prepare(data.size()),
|
||||
boost::asio::buffer(data.data(), data.size())));
|
||||
}
|
||||
|
||||
void tls_record_received(uint64_t, std::span<const uint8_t> data) override {
|
||||
receive_buffer.commit(boost::asio::buffer_copy(receive_buffer.prepare(data.size()),
|
||||
boost::asio::const_buffer(data.data(), data.size())));
|
||||
}
|
||||
|
||||
bool tls_peer_closed_connection() override {
|
||||
// Instruct the TLS implementation to reply with our close_notify to obtain
|
||||
// the same behaviour for TLS 1.2 and TLS 1.3.
|
||||
return true;
|
||||
}
|
||||
|
||||
void tls_alert(TLS::Alert alert) override {
|
||||
if(alert.type() == TLS::AlertType::CloseNotify) {
|
||||
shutdown_received = true;
|
||||
// Channel::process_alert will automatically write the corresponding close_notify response to the
|
||||
// send_buffer and close the native_handle after this function returns.
|
||||
}
|
||||
}
|
||||
|
||||
std::chrono::milliseconds tls_verify_cert_chain_ocsp_timeout() const override {
|
||||
return std::chrono::milliseconds(1000);
|
||||
}
|
||||
|
||||
void tls_verify_cert_chain(const std::vector<X509_Certificate>& cert_chain,
|
||||
const std::vector<std::optional<OCSP::Response>>& ocsp_responses,
|
||||
const std::vector<Certificate_Store*>& trusted_roots,
|
||||
Usage_Type usage,
|
||||
std::string_view hostname,
|
||||
const TLS::Policy& policy) override {
|
||||
auto ctx = m_context.lock();
|
||||
|
||||
if(ctx && ctx->has_verify_callback()) {
|
||||
ctx->get_verify_callback()(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
|
||||
} else {
|
||||
Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
|
||||
}
|
||||
}
|
||||
|
||||
bool shutdown_received;
|
||||
boost::beast::flat_buffer receive_buffer;
|
||||
boost::beast::flat_buffer send_buffer;
|
||||
|
||||
private:
|
||||
std::weak_ptr<TLS::Context> m_context;
|
||||
};
|
||||
|
||||
const boost::asio::mutable_buffer& input_buffer() { return m_input_buffer; }
|
||||
|
||||
boost::asio::const_buffer send_buffer() const { return m_core->send_buffer.data(); }
|
||||
|
||||
//! @brief Check if decrypted data is available in the receive buffer
|
||||
bool has_received_data() const { return m_core->receive_buffer.size() > 0; }
|
||||
|
||||
//! @brief Copy decrypted data into the user-provided buffer
|
||||
template <typename MutableBufferSequence>
|
||||
std::size_t copy_received_data(MutableBufferSequence buffers) {
|
||||
// Note: It would be nice to avoid this buffer copy. This could be achieved by equipping the StreamCore with
|
||||
// the user's desired target buffer once a read is started, and reading directly into that buffer in tls_record
|
||||
// received. However, we need to deal with the case that the receive buffer provided by the caller is smaller
|
||||
// than the decrypted record, so this optimization might not be worth the additional complexity.
|
||||
const auto copiedBytes = boost::asio::buffer_copy(buffers, m_core->receive_buffer.data());
|
||||
m_core->receive_buffer.consume(copiedBytes);
|
||||
return copiedBytes;
|
||||
}
|
||||
|
||||
//! @brief Check if encrypted data is available in the send buffer
|
||||
bool has_data_to_send() const { return m_core->send_buffer.size() > 0; }
|
||||
|
||||
//! @brief Mark bytes in the send buffer as consumed, removing them from the buffer
|
||||
void consume_send_buffer(std::size_t bytesConsumed) { m_core->send_buffer.consume(bytesConsumed); }
|
||||
|
||||
/**
|
||||
* @brief Create the native handle.
|
||||
*
|
||||
* Depending on the desired connection side, this function will create a TLS::Client or a
|
||||
* TLS::Server.
|
||||
*
|
||||
* @param side The desired connection side (client or server)
|
||||
* @param ec Set to indicate what error occurred, if any.
|
||||
*/
|
||||
void setup_native_handle(Connection_Side side, boost::system::error_code& ec) {
|
||||
BOTAN_UNUSED(side); // workaround: GCC 9 produces a warning claiming side is unused
|
||||
|
||||
// Do not attempt to instantiate the native_handle when a custom (mocked) channel type template parameter has
|
||||
// been specified. This allows mocking the native_handle in test code.
|
||||
if constexpr(std::is_same<ChannelT, Channel>::value) {
|
||||
try_with_error_code(
|
||||
[&] {
|
||||
if(side == Connection_Side::Client) {
|
||||
m_native_handle = std::unique_ptr<Client>(
|
||||
new Client(m_core,
|
||||
m_context->m_session_manager,
|
||||
m_context->m_credentials_manager,
|
||||
m_context->m_policy,
|
||||
m_context->m_rng,
|
||||
m_context->m_server_info,
|
||||
m_context->m_policy->latest_supported_version(false /* no DTLS */)));
|
||||
} else {
|
||||
m_native_handle = std::unique_ptr<Server>(new Server(m_core,
|
||||
m_context->m_session_manager,
|
||||
m_context->m_credentials_manager,
|
||||
m_context->m_policy,
|
||||
m_context->m_rng,
|
||||
false /* no DTLS */));
|
||||
}
|
||||
},
|
||||
ec);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Synchronously write encrypted data from the send buffer to the next layer.
|
||||
*
|
||||
* If this function is called with an error code other than 'Success', it will do nothing and return 0.
|
||||
*
|
||||
* @param ec Set to indicate what error occurred, if any. Specifically, StreamTruncated will be set if the peer
|
||||
* has closed the connection but did not properly shut down the SSL connection.
|
||||
* @return The number of bytes written.
|
||||
*/
|
||||
size_t send_pending_encrypted_data(boost::system::error_code& ec) {
|
||||
if(ec) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto writtenBytes = boost::asio::write(m_nextLayer, send_buffer(), ec);
|
||||
consume_send_buffer(writtenBytes);
|
||||
|
||||
if(ec == boost::asio::error::eof && !shutdown_received()) {
|
||||
// transport layer was closed by peer without receiving 'close_notify'
|
||||
ec.assign(StreamError::StreamTruncated, std::generic_category());
|
||||
}
|
||||
|
||||
return writtenBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pass plaintext data to the native handle for processing.
|
||||
*
|
||||
* The native handle will then create TLS records and hand them back to the Stream via the tls_emit_data callback.
|
||||
*/
|
||||
template <typename ConstBufferSequence>
|
||||
void tls_encrypt(const ConstBufferSequence& buffers, boost::system::error_code& ec) {
|
||||
// NOTE: This is not asynchronous: it encrypts the data synchronously.
|
||||
// The data encrypted by native_handle()->send() is synchronously stored in the send_buffer of m_core,
|
||||
// but is not actually written to the wire, yet.
|
||||
for(auto it = boost::asio::buffer_sequence_begin(buffers);
|
||||
!ec && it != boost::asio::buffer_sequence_end(buffers);
|
||||
it++) {
|
||||
const boost::asio::const_buffer buffer = *it;
|
||||
try_with_error_code(
|
||||
[&] {
|
||||
native_handle()->send({static_cast<const uint8_t*>(buffer.data()), buffer.size()});
|
||||
},
|
||||
ec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pass encrypted data to the native handle for processing.
|
||||
*
|
||||
* If an exception occurs while processing the data, an error code will be set.
|
||||
*
|
||||
* @param read_buffer Input buffer containing the encrypted data.
|
||||
* @param ec Set to indicate what error occurred, if any.
|
||||
*/
|
||||
void process_encrypted_data(const boost::asio::const_buffer& read_buffer, boost::system::error_code& ec) {
|
||||
try_with_error_code(
|
||||
[&] {
|
||||
native_handle()->received_data({static_cast<const uint8_t*>(read_buffer.data()), read_buffer.size()});
|
||||
},
|
||||
ec);
|
||||
}
|
||||
|
||||
//! @brief Catch exceptions and set an error_code
|
||||
template <typename Fun>
|
||||
void try_with_error_code(Fun f, boost::system::error_code& ec) {
|
||||
f();
|
||||
}
|
||||
|
||||
std::shared_ptr<Context> m_context;
|
||||
StreamLayer m_nextLayer;
|
||||
|
||||
std::shared_ptr<StreamCore> m_core;
|
||||
std::unique_ptr<ChannelT> m_native_handle;
|
||||
|
||||
// Buffer space used to read input intended for the core
|
||||
std::vector<uint8_t> m_input_buffer_space;
|
||||
const boost::asio::mutable_buffer m_input_buffer;
|
||||
};
|
||||
|
||||
} // namespace Botan::TLS
|
||||
|
||||
#endif // BOTAN_ASIO_STREAM_H_
|
226
include/inipp.h
Normal file
226
include/inipp.h
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2020 Matthias C. M. Troffaes
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
|
||||
namespace inipp {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// trim functions based on http://stackoverflow.com/a/217605
|
||||
|
||||
template <class CharT>
|
||||
inline void ltrim(std::basic_string<CharT> & s, const std::locale & loc) {
|
||||
s.erase(s.begin(),
|
||||
std::find_if(s.begin(), s.end(),
|
||||
[&loc](CharT ch) { return !std::isspace(ch, loc); }));
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
inline void rtrim(std::basic_string<CharT> & s, const std::locale & loc) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(),
|
||||
[&loc](CharT ch) { return !std::isspace(ch, loc); }).base(),
|
||||
s.end());
|
||||
}
|
||||
|
||||
// string replacement function based on http://stackoverflow.com/a/3418285
|
||||
|
||||
template <class CharT>
|
||||
inline bool replace(std::basic_string<CharT> & str, const std::basic_string<CharT> & from, const std::basic_string<CharT> & to) {
|
||||
auto changed = false;
|
||||
size_t start_pos = 0;
|
||||
while ((start_pos = str.find(from, start_pos)) != std::basic_string<CharT>::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length();
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename CharT, typename T>
|
||||
inline bool extract(const std::basic_string<CharT> & value, T & dst) {
|
||||
CharT c;
|
||||
std::basic_istringstream<CharT> is{ value };
|
||||
T result;
|
||||
if ((is >> std::boolalpha >> result) && !(is >> c)) {
|
||||
dst = result;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
inline bool extract(const std::basic_string<CharT> & value, std::basic_string<CharT> & dst) {
|
||||
dst = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class CharT>
|
||||
class Ini
|
||||
{
|
||||
public:
|
||||
typedef std::basic_string<CharT> String;
|
||||
typedef std::map<String, String> Section;
|
||||
typedef std::map<String, Section> Sections;
|
||||
|
||||
Sections sections;
|
||||
std::list<String> errors;
|
||||
|
||||
static const CharT char_section_start = (CharT)'[';
|
||||
static const CharT char_section_end = (CharT)']';
|
||||
static const CharT char_assign = (CharT)'=';
|
||||
static const CharT char_comment = (CharT)';';
|
||||
static const CharT char_interpol = (CharT)'$';
|
||||
static const CharT char_interpol_start = (CharT)'{';
|
||||
static const CharT char_interpol_sep = (CharT)':';
|
||||
static const CharT char_interpol_end = (CharT)'}';
|
||||
|
||||
static const int max_interpolation_depth = 10;
|
||||
|
||||
void generate(std::basic_ostream<CharT> & os) const {
|
||||
for (auto const & sec : sections) {
|
||||
os << char_section_start << sec.first << char_section_end << std::endl;
|
||||
for (auto const & val : sec.second) {
|
||||
os << val.first << char_assign << val.second << std::endl;
|
||||
}
|
||||
os << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void parse(std::basic_istream<CharT> & is) {
|
||||
String line;
|
||||
String section;
|
||||
const std::locale loc{"C"};
|
||||
while (std::getline(is, line)) {
|
||||
detail::ltrim(line, loc);
|
||||
detail::rtrim(line, loc);
|
||||
const auto length = line.length();
|
||||
if (length > 0) {
|
||||
const auto pos = line.find_first_of(char_assign);
|
||||
const auto & front = line.front();
|
||||
if (front == char_comment) {
|
||||
continue;
|
||||
}
|
||||
else if (front == char_section_start) {
|
||||
if (line.back() == char_section_end)
|
||||
section = line.substr(1, length - 2);
|
||||
else
|
||||
errors.push_back(line);
|
||||
}
|
||||
else if (pos != 0 && pos != String::npos) {
|
||||
String variable(line.substr(0, pos));
|
||||
String value(line.substr(pos + 1, length));
|
||||
detail::rtrim(variable, loc);
|
||||
detail::ltrim(value, loc);
|
||||
auto & sec = sections[section];
|
||||
if (sec.find(variable) == sec.end())
|
||||
sec.insert(std::make_pair(variable, value));
|
||||
else
|
||||
errors.push_back(line);
|
||||
}
|
||||
else {
|
||||
errors.push_back(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void interpolate() {
|
||||
int global_iteration = 0;
|
||||
auto changed = false;
|
||||
// replace each "${variable}" by "${section:variable}"
|
||||
for (auto & sec : sections)
|
||||
replace_symbols(local_symbols(sec.first, sec.second), sec.second);
|
||||
// replace each "${section:variable}" by its value
|
||||
do {
|
||||
changed = false;
|
||||
const auto syms = global_symbols();
|
||||
for (auto & sec : sections)
|
||||
changed |= replace_symbols(syms, sec.second);
|
||||
} while (changed && (max_interpolation_depth > global_iteration++));
|
||||
}
|
||||
|
||||
void default_section(const Section & sec) {
|
||||
for (auto & sec2 : sections)
|
||||
for (const auto & val : sec)
|
||||
sec2.second.insert(val);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
sections.clear();
|
||||
errors.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::list<std::pair<String, String> > Symbols;
|
||||
|
||||
auto local_symbol(const String & name) const {
|
||||
return char_interpol + (char_interpol_start + name + char_interpol_end);
|
||||
}
|
||||
|
||||
auto global_symbol(const String & sec_name, const String & name) const {
|
||||
return local_symbol(sec_name + char_interpol_sep + name);
|
||||
}
|
||||
|
||||
auto local_symbols(const String & sec_name, const Section & sec) const {
|
||||
Symbols result;
|
||||
for (const auto & val : sec)
|
||||
result.push_back(std::make_pair(local_symbol(val.first), global_symbol(sec_name, val.first)));
|
||||
return result;
|
||||
}
|
||||
|
||||
auto global_symbols() const {
|
||||
Symbols result;
|
||||
for (const auto & sec : sections)
|
||||
for (const auto & val : sec.second)
|
||||
result.push_back(
|
||||
std::make_pair(global_symbol(sec.first, val.first), val.second));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool replace_symbols(const Symbols & syms, Section & sec) const {
|
||||
auto changed = false;
|
||||
for (auto & sym : syms)
|
||||
for (auto & val : sec)
|
||||
changed |= detail::replace(val.second, sym.first, sym.second);
|
||||
return changed;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace inipp
|
693
include/rapidjson/allocators.h
Normal file
693
include/rapidjson/allocators.h
Normal file
@ -0,0 +1,693 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ALLOCATORS_H_
|
||||
#define RAPIDJSON_ALLOCATORS_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
#include "internal/meta.h"
|
||||
|
||||
#include <memory>
|
||||
#include <limits>
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11
|
||||
#include <type_traits>
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Allocator
|
||||
|
||||
/*! \class rapidjson::Allocator
|
||||
\brief Concept for allocating, resizing and freeing memory block.
|
||||
|
||||
Note that Malloc() and Realloc() are non-static but Free() is static.
|
||||
|
||||
So if an allocator need to support Free(), it needs to put its pointer in
|
||||
the header of memory block.
|
||||
|
||||
\code
|
||||
concept Allocator {
|
||||
static const bool kNeedFree; //!< Whether this allocator needs to call Free().
|
||||
|
||||
// Allocate a memory block.
|
||||
// \param size of the memory block in bytes.
|
||||
// \returns pointer to the memory block.
|
||||
void* Malloc(size_t size);
|
||||
|
||||
// Resize a memory block.
|
||||
// \param originalPtr The pointer to current memory block. Null pointer is permitted.
|
||||
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
|
||||
// \param newSize the new size in bytes.
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
|
||||
|
||||
// Free a memory block.
|
||||
// \param pointer to the memory block. Null pointer is permitted.
|
||||
static void Free(void *ptr);
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
|
||||
|
||||
/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief User-defined kDefaultChunkCapacity definition.
|
||||
|
||||
User can define this as any \c size that is a power of 2.
|
||||
*/
|
||||
|
||||
#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
|
||||
#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024)
|
||||
#endif
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// CrtAllocator
|
||||
|
||||
//! C-runtime library allocator.
|
||||
/*! This class is just wrapper for standard C library memory routines.
|
||||
\note implements Allocator concept
|
||||
*/
|
||||
class CrtAllocator {
|
||||
public:
|
||||
static const bool kNeedFree = true;
|
||||
void* Malloc(size_t size) {
|
||||
if (size) // behavior of malloc(0) is implementation defined.
|
||||
return RAPIDJSON_MALLOC(size);
|
||||
else
|
||||
return NULL; // standardize to returning NULL.
|
||||
}
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
|
||||
(void)originalSize;
|
||||
if (newSize == 0) {
|
||||
RAPIDJSON_FREE(originalPtr);
|
||||
return NULL;
|
||||
}
|
||||
return RAPIDJSON_REALLOC(originalPtr, newSize);
|
||||
}
|
||||
static void Free(void *ptr) RAPIDJSON_NOEXCEPT { RAPIDJSON_FREE(ptr); }
|
||||
|
||||
bool operator==(const CrtAllocator&) const RAPIDJSON_NOEXCEPT {
|
||||
return true;
|
||||
}
|
||||
bool operator!=(const CrtAllocator&) const RAPIDJSON_NOEXCEPT {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// MemoryPoolAllocator
|
||||
|
||||
//! Default memory allocator used by the parser and DOM.
|
||||
/*! This allocator allocate memory blocks from pre-allocated memory chunks.
|
||||
|
||||
It does not free memory blocks. And Realloc() only allocate new memory.
|
||||
|
||||
The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
|
||||
|
||||
User may also supply a buffer as the first chunk.
|
||||
|
||||
If the user-buffer is full then additional chunks are allocated by BaseAllocator.
|
||||
|
||||
The user-buffer is not deallocated by this allocator.
|
||||
|
||||
\tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
|
||||
\note implements Allocator concept
|
||||
*/
|
||||
template <typename BaseAllocator = CrtAllocator>
|
||||
class MemoryPoolAllocator {
|
||||
//! Chunk header for perpending to each chunk.
|
||||
/*! Chunks are stored as a singly linked list.
|
||||
*/
|
||||
struct ChunkHeader {
|
||||
size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
|
||||
size_t size; //!< Current size of allocated memory in bytes.
|
||||
ChunkHeader *next; //!< Next chunk in the linked list.
|
||||
};
|
||||
|
||||
struct SharedData {
|
||||
ChunkHeader *chunkHead; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
|
||||
BaseAllocator* ownBaseAllocator; //!< base allocator created by this object.
|
||||
size_t refcount;
|
||||
bool ownBuffer;
|
||||
};
|
||||
|
||||
static const size_t SIZEOF_SHARED_DATA = RAPIDJSON_ALIGN(sizeof(SharedData));
|
||||
static const size_t SIZEOF_CHUNK_HEADER = RAPIDJSON_ALIGN(sizeof(ChunkHeader));
|
||||
|
||||
static inline ChunkHeader *GetChunkHead(SharedData *shared)
|
||||
{
|
||||
return reinterpret_cast<ChunkHeader*>(reinterpret_cast<uint8_t*>(shared) + SIZEOF_SHARED_DATA);
|
||||
}
|
||||
static inline uint8_t *GetChunkBuffer(SharedData *shared)
|
||||
{
|
||||
return reinterpret_cast<uint8_t*>(shared->chunkHead) + SIZEOF_CHUNK_HEADER;
|
||||
}
|
||||
|
||||
static const size_t kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity.
|
||||
|
||||
public:
|
||||
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
|
||||
static const bool kRefCounted = true; //!< Tell users that this allocator is reference counted on copy
|
||||
|
||||
//! Constructor with chunkSize.
|
||||
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
||||
\param baseAllocator The allocator for allocating memory chunks.
|
||||
*/
|
||||
explicit
|
||||
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
||||
chunk_capacity_(chunkSize),
|
||||
baseAllocator_(baseAllocator ? baseAllocator : RAPIDJSON_NEW(BaseAllocator)()),
|
||||
shared_(static_cast<SharedData*>(baseAllocator_ ? baseAllocator_->Malloc(SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER) : 0))
|
||||
{
|
||||
RAPIDJSON_ASSERT(baseAllocator_ != 0);
|
||||
RAPIDJSON_ASSERT(shared_ != 0);
|
||||
if (baseAllocator) {
|
||||
shared_->ownBaseAllocator = 0;
|
||||
}
|
||||
else {
|
||||
shared_->ownBaseAllocator = baseAllocator_;
|
||||
}
|
||||
shared_->chunkHead = GetChunkHead(shared_);
|
||||
shared_->chunkHead->capacity = 0;
|
||||
shared_->chunkHead->size = 0;
|
||||
shared_->chunkHead->next = 0;
|
||||
shared_->ownBuffer = true;
|
||||
shared_->refcount = 1;
|
||||
}
|
||||
|
||||
//! Constructor with user-supplied buffer.
|
||||
/*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
|
||||
|
||||
The user buffer will not be deallocated when this allocator is destructed.
|
||||
|
||||
\param buffer User supplied buffer.
|
||||
\param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
|
||||
\param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
|
||||
\param baseAllocator The allocator for allocating memory chunks.
|
||||
*/
|
||||
MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
|
||||
chunk_capacity_(chunkSize),
|
||||
baseAllocator_(baseAllocator),
|
||||
shared_(static_cast<SharedData*>(AlignBuffer(buffer, size)))
|
||||
{
|
||||
RAPIDJSON_ASSERT(size >= SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER);
|
||||
shared_->chunkHead = GetChunkHead(shared_);
|
||||
shared_->chunkHead->capacity = size - SIZEOF_SHARED_DATA - SIZEOF_CHUNK_HEADER;
|
||||
shared_->chunkHead->size = 0;
|
||||
shared_->chunkHead->next = 0;
|
||||
shared_->ownBaseAllocator = 0;
|
||||
shared_->ownBuffer = false;
|
||||
shared_->refcount = 1;
|
||||
}
|
||||
|
||||
MemoryPoolAllocator(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT :
|
||||
chunk_capacity_(rhs.chunk_capacity_),
|
||||
baseAllocator_(rhs.baseAllocator_),
|
||||
shared_(rhs.shared_)
|
||||
{
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
||||
++shared_->refcount;
|
||||
}
|
||||
MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT
|
||||
{
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
|
||||
++rhs.shared_->refcount;
|
||||
this->~MemoryPoolAllocator();
|
||||
baseAllocator_ = rhs.baseAllocator_;
|
||||
chunk_capacity_ = rhs.chunk_capacity_;
|
||||
shared_ = rhs.shared_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
MemoryPoolAllocator(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT :
|
||||
chunk_capacity_(rhs.chunk_capacity_),
|
||||
baseAllocator_(rhs.baseAllocator_),
|
||||
shared_(rhs.shared_)
|
||||
{
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
|
||||
rhs.shared_ = 0;
|
||||
}
|
||||
MemoryPoolAllocator& operator=(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT
|
||||
{
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
|
||||
this->~MemoryPoolAllocator();
|
||||
baseAllocator_ = rhs.baseAllocator_;
|
||||
chunk_capacity_ = rhs.chunk_capacity_;
|
||||
shared_ = rhs.shared_;
|
||||
rhs.shared_ = 0;
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! Destructor.
|
||||
/*! This deallocates all memory chunks, excluding the user-supplied buffer.
|
||||
*/
|
||||
~MemoryPoolAllocator() RAPIDJSON_NOEXCEPT {
|
||||
if (!shared_) {
|
||||
// do nothing if moved
|
||||
return;
|
||||
}
|
||||
if (shared_->refcount > 1) {
|
||||
--shared_->refcount;
|
||||
return;
|
||||
}
|
||||
Clear();
|
||||
BaseAllocator *a = shared_->ownBaseAllocator;
|
||||
if (shared_->ownBuffer) {
|
||||
baseAllocator_->Free(shared_);
|
||||
}
|
||||
RAPIDJSON_DELETE(a);
|
||||
}
|
||||
|
||||
//! Deallocates all memory chunks, excluding the first/user one.
|
||||
void Clear() RAPIDJSON_NOEXCEPT {
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
||||
for (;;) {
|
||||
ChunkHeader* c = shared_->chunkHead;
|
||||
if (!c->next) {
|
||||
break;
|
||||
}
|
||||
shared_->chunkHead = c->next;
|
||||
baseAllocator_->Free(c);
|
||||
}
|
||||
shared_->chunkHead->size = 0;
|
||||
}
|
||||
|
||||
//! Computes the total capacity of allocated memory chunks.
|
||||
/*! \return total capacity in bytes.
|
||||
*/
|
||||
size_t Capacity() const RAPIDJSON_NOEXCEPT {
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
||||
size_t capacity = 0;
|
||||
for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next)
|
||||
capacity += c->capacity;
|
||||
return capacity;
|
||||
}
|
||||
|
||||
//! Computes the memory blocks allocated.
|
||||
/*! \return total used bytes.
|
||||
*/
|
||||
size_t Size() const RAPIDJSON_NOEXCEPT {
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
||||
size_t size = 0;
|
||||
for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next)
|
||||
size += c->size;
|
||||
return size;
|
||||
}
|
||||
|
||||
//! Whether the allocator is shared.
|
||||
/*! \return true or false.
|
||||
*/
|
||||
bool Shared() const RAPIDJSON_NOEXCEPT {
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
||||
return shared_->refcount > 1;
|
||||
}
|
||||
|
||||
//! Allocates a memory block. (concept Allocator)
|
||||
void* Malloc(size_t size) {
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
||||
if (!size)
|
||||
return NULL;
|
||||
|
||||
size = RAPIDJSON_ALIGN(size);
|
||||
if (RAPIDJSON_UNLIKELY(shared_->chunkHead->size + size > shared_->chunkHead->capacity))
|
||||
if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
|
||||
return NULL;
|
||||
|
||||
void *buffer = GetChunkBuffer(shared_) + shared_->chunkHead->size;
|
||||
shared_->chunkHead->size += size;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//! Resizes a memory block (concept Allocator)
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
|
||||
if (originalPtr == 0)
|
||||
return Malloc(newSize);
|
||||
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
||||
if (newSize == 0)
|
||||
return NULL;
|
||||
|
||||
originalSize = RAPIDJSON_ALIGN(originalSize);
|
||||
newSize = RAPIDJSON_ALIGN(newSize);
|
||||
|
||||
// Do not shrink if new size is smaller than original
|
||||
if (originalSize >= newSize)
|
||||
return originalPtr;
|
||||
|
||||
// Simply expand it if it is the last allocation and there is sufficient space
|
||||
if (originalPtr == GetChunkBuffer(shared_) + shared_->chunkHead->size - originalSize) {
|
||||
size_t increment = static_cast<size_t>(newSize - originalSize);
|
||||
if (shared_->chunkHead->size + increment <= shared_->chunkHead->capacity) {
|
||||
shared_->chunkHead->size += increment;
|
||||
return originalPtr;
|
||||
}
|
||||
}
|
||||
|
||||
// Realloc process: allocate and copy memory, do not free original buffer.
|
||||
if (void* newBuffer = Malloc(newSize)) {
|
||||
if (originalSize)
|
||||
std::memcpy(newBuffer, originalPtr, originalSize);
|
||||
return newBuffer;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//! Frees a memory block (concept Allocator)
|
||||
static void Free(void *ptr) RAPIDJSON_NOEXCEPT { (void)ptr; } // Do nothing
|
||||
|
||||
//! Compare (equality) with another MemoryPoolAllocator
|
||||
bool operator==(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT {
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
|
||||
return shared_ == rhs.shared_;
|
||||
}
|
||||
//! Compare (inequality) with another MemoryPoolAllocator
|
||||
bool operator!=(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
//! Creates a new chunk.
|
||||
/*! \param capacity Capacity of the chunk in bytes.
|
||||
\return true if success.
|
||||
*/
|
||||
bool AddChunk(size_t capacity) {
|
||||
if (!baseAllocator_)
|
||||
shared_->ownBaseAllocator = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)();
|
||||
if (ChunkHeader* chunk = static_cast<ChunkHeader*>(baseAllocator_->Malloc(SIZEOF_CHUNK_HEADER + capacity))) {
|
||||
chunk->capacity = capacity;
|
||||
chunk->size = 0;
|
||||
chunk->next = shared_->chunkHead;
|
||||
shared_->chunkHead = chunk;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void* AlignBuffer(void* buf, size_t &size)
|
||||
{
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(buf != 0);
|
||||
const uintptr_t mask = sizeof(void*) - 1;
|
||||
const uintptr_t ubuf = reinterpret_cast<uintptr_t>(buf);
|
||||
if (RAPIDJSON_UNLIKELY(ubuf & mask)) {
|
||||
const uintptr_t abuf = (ubuf + mask) & ~mask;
|
||||
RAPIDJSON_ASSERT(size >= abuf - ubuf);
|
||||
buf = reinterpret_cast<void*>(abuf);
|
||||
size -= abuf - ubuf;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
|
||||
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
|
||||
SharedData *shared_; //!< The shared data of the allocator
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
template<typename, typename = void>
|
||||
struct IsRefCounted :
|
||||
public FalseType
|
||||
{ };
|
||||
template<typename T>
|
||||
struct IsRefCounted<T, typename internal::EnableIfCond<T::kRefCounted>::Type> :
|
||||
public TrueType
|
||||
{ };
|
||||
}
|
||||
|
||||
template<typename T, typename A>
|
||||
inline T* Realloc(A& a, T* old_p, size_t old_n, size_t new_n)
|
||||
{
|
||||
RAPIDJSON_NOEXCEPT_ASSERT(old_n <= (std::numeric_limits<size_t>::max)() / sizeof(T) && new_n <= (std::numeric_limits<size_t>::max)() / sizeof(T));
|
||||
return static_cast<T*>(a.Realloc(old_p, old_n * sizeof(T), new_n * sizeof(T)));
|
||||
}
|
||||
|
||||
template<typename T, typename A>
|
||||
inline T *Malloc(A& a, size_t n = 1)
|
||||
{
|
||||
return Realloc<T, A>(a, NULL, 0, n);
|
||||
}
|
||||
|
||||
template<typename T, typename A>
|
||||
inline void Free(A& a, T *p, size_t n = 1)
|
||||
{
|
||||
static_cast<void>(Realloc<T, A>(a, p, n, 0));
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++) // std::allocator can safely be inherited
|
||||
#endif
|
||||
|
||||
template <typename T, typename BaseAllocator = CrtAllocator>
|
||||
class StdAllocator :
|
||||
public std::allocator<T>
|
||||
{
|
||||
typedef std::allocator<T> allocator_type;
|
||||
#if RAPIDJSON_HAS_CXX11
|
||||
typedef std::allocator_traits<allocator_type> traits_type;
|
||||
#else
|
||||
typedef allocator_type traits_type;
|
||||
#endif
|
||||
|
||||
public:
|
||||
typedef BaseAllocator BaseAllocatorType;
|
||||
|
||||
StdAllocator() RAPIDJSON_NOEXCEPT :
|
||||
allocator_type(),
|
||||
baseAllocator_()
|
||||
{ }
|
||||
|
||||
StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT :
|
||||
allocator_type(rhs),
|
||||
baseAllocator_(rhs.baseAllocator_)
|
||||
{ }
|
||||
|
||||
template<typename U>
|
||||
StdAllocator(const StdAllocator<U, BaseAllocator>& rhs) RAPIDJSON_NOEXCEPT :
|
||||
allocator_type(rhs),
|
||||
baseAllocator_(rhs.baseAllocator_)
|
||||
{ }
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
StdAllocator(StdAllocator&& rhs) RAPIDJSON_NOEXCEPT :
|
||||
allocator_type(std::move(rhs)),
|
||||
baseAllocator_(std::move(rhs.baseAllocator_))
|
||||
{ }
|
||||
#endif
|
||||
#if RAPIDJSON_HAS_CXX11
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
using propagate_on_container_swap = std::true_type;
|
||||
#endif
|
||||
|
||||
/* implicit */
|
||||
StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT :
|
||||
allocator_type(),
|
||||
baseAllocator_(baseAllocator)
|
||||
{ }
|
||||
|
||||
~StdAllocator() RAPIDJSON_NOEXCEPT
|
||||
{ }
|
||||
|
||||
template<typename U>
|
||||
struct rebind {
|
||||
typedef StdAllocator<U, BaseAllocator> other;
|
||||
};
|
||||
|
||||
typedef typename traits_type::size_type size_type;
|
||||
typedef typename traits_type::difference_type difference_type;
|
||||
|
||||
typedef typename traits_type::value_type value_type;
|
||||
typedef typename traits_type::pointer pointer;
|
||||
typedef typename traits_type::const_pointer const_pointer;
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11
|
||||
|
||||
typedef typename std::add_lvalue_reference<value_type>::type &reference;
|
||||
typedef typename std::add_lvalue_reference<typename std::add_const<value_type>::type>::type &const_reference;
|
||||
|
||||
pointer address(reference r) const RAPIDJSON_NOEXCEPT
|
||||
{
|
||||
return std::addressof(r);
|
||||
}
|
||||
const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT
|
||||
{
|
||||
return std::addressof(r);
|
||||
}
|
||||
|
||||
size_type max_size() const RAPIDJSON_NOEXCEPT
|
||||
{
|
||||
return traits_type::max_size(*this);
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
void construct(pointer p, Args&&... args)
|
||||
{
|
||||
traits_type::construct(*this, p, std::forward<Args>(args)...);
|
||||
}
|
||||
void destroy(pointer p)
|
||||
{
|
||||
traits_type::destroy(*this, p);
|
||||
}
|
||||
|
||||
#else // !RAPIDJSON_HAS_CXX11
|
||||
|
||||
typedef typename allocator_type::reference reference;
|
||||
typedef typename allocator_type::const_reference const_reference;
|
||||
|
||||
pointer address(reference r) const RAPIDJSON_NOEXCEPT
|
||||
{
|
||||
return allocator_type::address(r);
|
||||
}
|
||||
const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT
|
||||
{
|
||||
return allocator_type::address(r);
|
||||
}
|
||||
|
||||
size_type max_size() const RAPIDJSON_NOEXCEPT
|
||||
{
|
||||
return allocator_type::max_size();
|
||||
}
|
||||
|
||||
void construct(pointer p, const_reference r)
|
||||
{
|
||||
allocator_type::construct(p, r);
|
||||
}
|
||||
void destroy(pointer p)
|
||||
{
|
||||
allocator_type::destroy(p);
|
||||
}
|
||||
|
||||
#endif // !RAPIDJSON_HAS_CXX11
|
||||
|
||||
template <typename U>
|
||||
U* allocate(size_type n = 1, const void* = 0)
|
||||
{
|
||||
return RAPIDJSON_NAMESPACE::Malloc<U>(baseAllocator_, n);
|
||||
}
|
||||
template <typename U>
|
||||
void deallocate(U* p, size_type n = 1)
|
||||
{
|
||||
RAPIDJSON_NAMESPACE::Free<U>(baseAllocator_, p, n);
|
||||
}
|
||||
|
||||
pointer allocate(size_type n = 1, const void* = 0)
|
||||
{
|
||||
return allocate<value_type>(n);
|
||||
}
|
||||
void deallocate(pointer p, size_type n = 1)
|
||||
{
|
||||
deallocate<value_type>(p, n);
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11
|
||||
using is_always_equal = std::is_empty<BaseAllocator>;
|
||||
#endif
|
||||
|
||||
template<typename U>
|
||||
bool operator==(const StdAllocator<U, BaseAllocator>& rhs) const RAPIDJSON_NOEXCEPT
|
||||
{
|
||||
return baseAllocator_ == rhs.baseAllocator_;
|
||||
}
|
||||
template<typename U>
|
||||
bool operator!=(const StdAllocator<U, BaseAllocator>& rhs) const RAPIDJSON_NOEXCEPT
|
||||
{
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
//! rapidjson Allocator concept
|
||||
static const bool kNeedFree = BaseAllocator::kNeedFree;
|
||||
static const bool kRefCounted = internal::IsRefCounted<BaseAllocator>::Value;
|
||||
void* Malloc(size_t size)
|
||||
{
|
||||
return baseAllocator_.Malloc(size);
|
||||
}
|
||||
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize)
|
||||
{
|
||||
return baseAllocator_.Realloc(originalPtr, originalSize, newSize);
|
||||
}
|
||||
static void Free(void *ptr) RAPIDJSON_NOEXCEPT
|
||||
{
|
||||
BaseAllocator::Free(ptr);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename, typename>
|
||||
friend class StdAllocator; // access to StdAllocator<!T>.*
|
||||
|
||||
BaseAllocator baseAllocator_;
|
||||
};
|
||||
|
||||
#if !RAPIDJSON_HAS_CXX17 // std::allocator<void> deprecated in C++17
|
||||
template <typename BaseAllocator>
|
||||
class StdAllocator<void, BaseAllocator> :
|
||||
public std::allocator<void>
|
||||
{
|
||||
typedef std::allocator<void> allocator_type;
|
||||
|
||||
public:
|
||||
typedef BaseAllocator BaseAllocatorType;
|
||||
|
||||
StdAllocator() RAPIDJSON_NOEXCEPT :
|
||||
allocator_type(),
|
||||
baseAllocator_()
|
||||
{ }
|
||||
|
||||
StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT :
|
||||
allocator_type(rhs),
|
||||
baseAllocator_(rhs.baseAllocator_)
|
||||
{ }
|
||||
|
||||
template<typename U>
|
||||
StdAllocator(const StdAllocator<U, BaseAllocator>& rhs) RAPIDJSON_NOEXCEPT :
|
||||
allocator_type(rhs),
|
||||
baseAllocator_(rhs.baseAllocator_)
|
||||
{ }
|
||||
|
||||
/* implicit */
|
||||
StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT :
|
||||
allocator_type(),
|
||||
baseAllocator_(baseAllocator)
|
||||
{ }
|
||||
|
||||
~StdAllocator() RAPIDJSON_NOEXCEPT
|
||||
{ }
|
||||
|
||||
template<typename U>
|
||||
struct rebind {
|
||||
typedef StdAllocator<U, BaseAllocator> other;
|
||||
};
|
||||
|
||||
typedef typename allocator_type::value_type value_type;
|
||||
|
||||
private:
|
||||
template <typename, typename>
|
||||
friend class StdAllocator; // access to StdAllocator<!T>.*
|
||||
|
||||
BaseAllocator baseAllocator_;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ENCODINGS_H_
|
78
include/rapidjson/cursorstreamwrapper.h
Normal file
78
include/rapidjson/cursorstreamwrapper.h
Normal file
@ -0,0 +1,78 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_
|
||||
#define RAPIDJSON_CURSORSTREAMWRAPPER_H_
|
||||
|
||||
#include "stream.h"
|
||||
|
||||
#if defined(__GNUC__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
||||
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
//! Cursor stream wrapper for counting line and column number if error exists.
|
||||
/*!
|
||||
\tparam InputStream Any stream that implements Stream Concept
|
||||
*/
|
||||
template <typename InputStream, typename Encoding = UTF8<> >
|
||||
class CursorStreamWrapper : public GenericStreamWrapper<InputStream, Encoding> {
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
CursorStreamWrapper(InputStream& is):
|
||||
GenericStreamWrapper<InputStream, Encoding>(is), line_(1), col_(0) {}
|
||||
|
||||
// counting line and column number
|
||||
Ch Take() {
|
||||
Ch ch = this->is_.Take();
|
||||
if(ch == '\n') {
|
||||
line_ ++;
|
||||
col_ = 0;
|
||||
} else {
|
||||
col_ ++;
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
|
||||
//! Get the error line number, if error exists.
|
||||
size_t GetLine() const { return line_; }
|
||||
//! Get the error column number, if error exists.
|
||||
size_t GetColumn() const { return col_; }
|
||||
|
||||
private:
|
||||
size_t line_; //!< Current Line
|
||||
size_t col_; //!< Current Column
|
||||
};
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_
|
3043
include/rapidjson/document.h
Normal file
3043
include/rapidjson/document.h
Normal file
File diff suppressed because it is too large
Load Diff
299
include/rapidjson/encodedstream.h
Normal file
299
include/rapidjson/encodedstream.h
Normal file
@ -0,0 +1,299 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ENCODEDSTREAM_H_
|
||||
#define RAPIDJSON_ENCODEDSTREAM_H_
|
||||
|
||||
#include "stream.h"
|
||||
#include "memorystream.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Input byte stream wrapper with a statically bound encoding.
|
||||
/*!
|
||||
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
|
||||
\tparam InputByteStream Type of input byte stream. For example, FileReadStream.
|
||||
*/
|
||||
template <typename Encoding, typename InputByteStream>
|
||||
class EncodedInputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
EncodedInputStream(InputByteStream& is) : is_(is) {
|
||||
current_ = Encoding::TakeBOM(is_);
|
||||
}
|
||||
|
||||
Ch Peek() const { return current_; }
|
||||
Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
|
||||
size_t Tell() const { return is_.Tell(); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
EncodedInputStream(const EncodedInputStream&);
|
||||
EncodedInputStream& operator=(const EncodedInputStream&);
|
||||
|
||||
InputByteStream& is_;
|
||||
Ch current_;
|
||||
};
|
||||
|
||||
//! Specialized for UTF8 MemoryStream.
|
||||
template <>
|
||||
class EncodedInputStream<UTF8<>, MemoryStream> {
|
||||
public:
|
||||
typedef UTF8<>::Ch Ch;
|
||||
|
||||
EncodedInputStream(MemoryStream& is) : is_(is) {
|
||||
if (static_cast<unsigned char>(is_.Peek()) == 0xEFu) is_.Take();
|
||||
if (static_cast<unsigned char>(is_.Peek()) == 0xBBu) is_.Take();
|
||||
if (static_cast<unsigned char>(is_.Peek()) == 0xBFu) is_.Take();
|
||||
}
|
||||
Ch Peek() const { return is_.Peek(); }
|
||||
Ch Take() { return is_.Take(); }
|
||||
size_t Tell() const { return is_.Tell(); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) {}
|
||||
void Flush() {}
|
||||
Ch* PutBegin() { return 0; }
|
||||
size_t PutEnd(Ch*) { return 0; }
|
||||
|
||||
MemoryStream& is_;
|
||||
|
||||
private:
|
||||
EncodedInputStream(const EncodedInputStream&);
|
||||
EncodedInputStream& operator=(const EncodedInputStream&);
|
||||
};
|
||||
|
||||
//! Output byte stream wrapper with statically bound encoding.
|
||||
/*!
|
||||
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
|
||||
\tparam OutputByteStream Type of input byte stream. For example, FileWriteStream.
|
||||
*/
|
||||
template <typename Encoding, typename OutputByteStream>
|
||||
class EncodedOutputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
|
||||
if (putBOM)
|
||||
Encoding::PutBOM(os_);
|
||||
}
|
||||
|
||||
void Put(Ch c) { Encoding::Put(os_, c); }
|
||||
void Flush() { os_.Flush(); }
|
||||
|
||||
// Not implemented
|
||||
Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
|
||||
Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
EncodedOutputStream(const EncodedOutputStream&);
|
||||
EncodedOutputStream& operator=(const EncodedOutputStream&);
|
||||
|
||||
OutputByteStream& os_;
|
||||
};
|
||||
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
//! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
|
||||
/*!
|
||||
\tparam CharType Type of character for reading.
|
||||
\tparam InputByteStream type of input byte stream to be wrapped.
|
||||
*/
|
||||
template <typename CharType, typename InputByteStream>
|
||||
class AutoUTFInputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef CharType Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param is input stream to be wrapped.
|
||||
\param type UTF encoding type if it is not detected from the stream.
|
||||
*/
|
||||
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
|
||||
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
|
||||
DetectType();
|
||||
static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
|
||||
takeFunc_ = f[type_];
|
||||
current_ = takeFunc_(*is_);
|
||||
}
|
||||
|
||||
UTFType GetType() const { return type_; }
|
||||
bool HasBOM() const { return hasBOM_; }
|
||||
|
||||
Ch Peek() const { return current_; }
|
||||
Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
|
||||
size_t Tell() const { return is_->Tell(); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
AutoUTFInputStream(const AutoUTFInputStream&);
|
||||
AutoUTFInputStream& operator=(const AutoUTFInputStream&);
|
||||
|
||||
// Detect encoding type with BOM or RFC 4627
|
||||
void DetectType() {
|
||||
// BOM (Byte Order Mark):
|
||||
// 00 00 FE FF UTF-32BE
|
||||
// FF FE 00 00 UTF-32LE
|
||||
// FE FF UTF-16BE
|
||||
// FF FE UTF-16LE
|
||||
// EF BB BF UTF-8
|
||||
|
||||
const unsigned char* c = reinterpret_cast<const unsigned char *>(is_->Peek4());
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
unsigned bom = static_cast<unsigned>(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24));
|
||||
hasBOM_ = false;
|
||||
if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
|
||||
else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
|
||||
else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
|
||||
|
||||
// RFC 4627: Section 3
|
||||
// "Since the first two characters of a JSON text will always be ASCII
|
||||
// characters [RFC0020], it is possible to determine whether an octet
|
||||
// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
|
||||
// at the pattern of nulls in the first four octets."
|
||||
// 00 00 00 xx UTF-32BE
|
||||
// 00 xx 00 xx UTF-16BE
|
||||
// xx 00 00 00 UTF-32LE
|
||||
// xx 00 xx 00 UTF-16LE
|
||||
// xx xx xx xx UTF-8
|
||||
|
||||
if (!hasBOM_) {
|
||||
int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
|
||||
switch (pattern) {
|
||||
case 0x08: type_ = kUTF32BE; break;
|
||||
case 0x0A: type_ = kUTF16BE; break;
|
||||
case 0x01: type_ = kUTF32LE; break;
|
||||
case 0x05: type_ = kUTF16LE; break;
|
||||
case 0x0F: type_ = kUTF8; break;
|
||||
default: break; // Use type defined by user.
|
||||
}
|
||||
}
|
||||
|
||||
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||
}
|
||||
|
||||
typedef Ch (*TakeFunc)(InputByteStream& is);
|
||||
InputByteStream* is_;
|
||||
UTFType type_;
|
||||
Ch current_;
|
||||
TakeFunc takeFunc_;
|
||||
bool hasBOM_;
|
||||
};
|
||||
|
||||
//! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
|
||||
/*!
|
||||
\tparam CharType Type of character for writing.
|
||||
\tparam OutputByteStream type of output byte stream to be wrapped.
|
||||
*/
|
||||
template <typename CharType, typename OutputByteStream>
|
||||
class AutoUTFOutputStream {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
public:
|
||||
typedef CharType Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param os output stream to be wrapped.
|
||||
\param type UTF encoding type.
|
||||
\param putBOM Whether to write BOM at the beginning of the stream.
|
||||
*/
|
||||
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
|
||||
RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE);
|
||||
|
||||
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
|
||||
if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
|
||||
if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
|
||||
|
||||
static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
|
||||
putFunc_ = f[type_];
|
||||
|
||||
if (putBOM)
|
||||
PutBOM();
|
||||
}
|
||||
|
||||
UTFType GetType() const { return type_; }
|
||||
|
||||
void Put(Ch c) { putFunc_(*os_, c); }
|
||||
void Flush() { os_->Flush(); }
|
||||
|
||||
// Not implemented
|
||||
Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;}
|
||||
Ch Take() { RAPIDJSON_ASSERT(false); return 0;}
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
AutoUTFOutputStream(const AutoUTFOutputStream&);
|
||||
AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
|
||||
|
||||
void PutBOM() {
|
||||
typedef void (*PutBOMFunc)(OutputByteStream&);
|
||||
static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
|
||||
f[type_](*os_);
|
||||
}
|
||||
|
||||
typedef void (*PutFunc)(OutputByteStream&, Ch);
|
||||
|
||||
OutputByteStream* os_;
|
||||
UTFType type_;
|
||||
PutFunc putFunc_;
|
||||
};
|
||||
|
||||
#undef RAPIDJSON_ENCODINGS_FUNC
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
716
include/rapidjson/encodings.h
Normal file
716
include/rapidjson/encodings.h
Normal file
@ -0,0 +1,716 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ENCODINGS_H_
|
||||
#define RAPIDJSON_ENCODINGS_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data
|
||||
RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
||||
#elif defined(__GNUC__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
RAPIDJSON_DIAG_OFF(overflow)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Encoding
|
||||
|
||||
/*! \class rapidjson::Encoding
|
||||
\brief Concept for encoding of Unicode characters.
|
||||
|
||||
\code
|
||||
concept Encoding {
|
||||
typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition.
|
||||
|
||||
enum { supportUnicode = 1 }; // or 0 if not supporting unicode
|
||||
|
||||
//! \brief Encode a Unicode codepoint to an output stream.
|
||||
//! \param os Output stream.
|
||||
//! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively.
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint);
|
||||
|
||||
//! \brief Decode a Unicode codepoint from an input stream.
|
||||
//! \param is Input stream.
|
||||
//! \param codepoint Output of the unicode codepoint.
|
||||
//! \return true if a valid codepoint can be decoded from the stream.
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint);
|
||||
|
||||
//! \brief Validate one Unicode codepoint from an encoded stream.
|
||||
//! \param is Input stream to obtain codepoint.
|
||||
//! \param os Output for copying one codepoint.
|
||||
//! \return true if it is valid.
|
||||
//! \note This function just validating and copying the codepoint without actually decode it.
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os);
|
||||
|
||||
// The following functions are deal with byte streams.
|
||||
|
||||
//! Take a character from input byte stream, skip BOM if exist.
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is);
|
||||
|
||||
//! Take a character from input byte stream.
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is);
|
||||
|
||||
//! Put BOM to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os);
|
||||
|
||||
//! Put a character to output byte stream.
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c);
|
||||
};
|
||||
\endcode
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF8
|
||||
|
||||
//! UTF-8 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-8
|
||||
http://tools.ietf.org/html/rfc3629
|
||||
\tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char.
|
||||
\note implements Encoding concept
|
||||
*/
|
||||
template<typename CharType = char>
|
||||
struct UTF8 {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
if (codepoint <= 0x7F)
|
||||
os.Put(static_cast<Ch>(codepoint & 0xFF));
|
||||
else if (codepoint <= 0x7FF) {
|
||||
os.Put(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) {
|
||||
os.Put(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
os.Put(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename OutputStream>
|
||||
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
if (codepoint <= 0x7F)
|
||||
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
|
||||
else if (codepoint <= 0x7FF) {
|
||||
PutUnsafe(os, static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
|
||||
}
|
||||
else if (codepoint <= 0xFFFF) {
|
||||
PutUnsafe(os, static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
PutUnsafe(os, static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||
PutUnsafe(os, static_cast<Ch>(0x80 | (codepoint & 0x3F)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu)
|
||||
#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
|
||||
#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70)
|
||||
typename InputStream::Ch c = is.Take();
|
||||
if (!(c & 0x80)) {
|
||||
*codepoint = static_cast<unsigned char>(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char type = GetRange(static_cast<unsigned char>(c));
|
||||
if (type >= 32) {
|
||||
*codepoint = 0;
|
||||
} else {
|
||||
*codepoint = (0xFFu >> type) & static_cast<unsigned char>(c);
|
||||
}
|
||||
bool result = true;
|
||||
switch (type) {
|
||||
case 2: RAPIDJSON_TAIL(); return result;
|
||||
case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result;
|
||||
case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result;
|
||||
case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
default: return false;
|
||||
}
|
||||
#undef RAPIDJSON_COPY
|
||||
#undef RAPIDJSON_TRANS
|
||||
#undef RAPIDJSON_TAIL
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
#define RAPIDJSON_COPY() os.Put(c = is.Take())
|
||||
#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0)
|
||||
#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70)
|
||||
Ch c;
|
||||
RAPIDJSON_COPY();
|
||||
if (!(c & 0x80))
|
||||
return true;
|
||||
|
||||
bool result = true;
|
||||
switch (GetRange(static_cast<unsigned char>(c))) {
|
||||
case 2: RAPIDJSON_TAIL(); return result;
|
||||
case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result;
|
||||
case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result;
|
||||
case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result;
|
||||
default: return false;
|
||||
}
|
||||
#undef RAPIDJSON_COPY
|
||||
#undef RAPIDJSON_TRANS
|
||||
#undef RAPIDJSON_TAIL
|
||||
}
|
||||
|
||||
static unsigned char GetRange(unsigned char c) {
|
||||
// Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||
// With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types.
|
||||
static const unsigned char type[] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
|
||||
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
||||
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
|
||||
};
|
||||
return type[c];
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
typename InputByteStream::Ch c = Take(is);
|
||||
if (static_cast<unsigned char>(c) != 0xEFu) return c;
|
||||
c = is.Take();
|
||||
if (static_cast<unsigned char>(c) != 0xBBu) return c;
|
||||
c = is.Take();
|
||||
if (static_cast<unsigned char>(c) != 0xBFu) return c;
|
||||
c = is.Take();
|
||||
return c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
return static_cast<Ch>(is.Take());
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xEFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xBBu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xBFu));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF16
|
||||
|
||||
//! UTF-16 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-16
|
||||
http://tools.ietf.org/html/rfc2781
|
||||
\tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead.
|
||||
\note implements Encoding concept
|
||||
|
||||
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
|
||||
For streaming, use UTF16LE and UTF16BE, which handle endianness.
|
||||
*/
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2);
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||
if (codepoint <= 0xFFFF) {
|
||||
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
|
||||
os.Put(static_cast<typename OutputStream::Ch>(codepoint));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
unsigned v = codepoint - 0x10000;
|
||||
os.Put(static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
||||
os.Put(static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename OutputStream>
|
||||
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||
if (codepoint <= 0xFFFF) {
|
||||
RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair
|
||||
PutUnsafe(os, static_cast<typename OutputStream::Ch>(codepoint));
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
unsigned v = codepoint - 0x10000;
|
||||
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v >> 10) | 0xD800));
|
||||
PutUnsafe(os, static_cast<typename OutputStream::Ch>((v & 0x3FF) | 0xDC00));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||
typename InputStream::Ch c = is.Take();
|
||||
if (c < 0xD800 || c > 0xDFFF) {
|
||||
*codepoint = static_cast<unsigned>(c);
|
||||
return true;
|
||||
}
|
||||
else if (c <= 0xDBFF) {
|
||||
*codepoint = (static_cast<unsigned>(c) & 0x3FF) << 10;
|
||||
c = is.Take();
|
||||
*codepoint |= (static_cast<unsigned>(c) & 0x3FF);
|
||||
*codepoint += 0x10000;
|
||||
return c >= 0xDC00 && c <= 0xDFFF;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2);
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2);
|
||||
typename InputStream::Ch c;
|
||||
os.Put(static_cast<typename OutputStream::Ch>(c = is.Take()));
|
||||
if (c < 0xD800 || c > 0xDFFF)
|
||||
return true;
|
||||
else if (c <= 0xDBFF) {
|
||||
os.Put(c = is.Take());
|
||||
return c >= 0xDC00 && c <= 0xDFFF;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-16 little endian encoding.
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16LE : UTF16<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
unsigned c = static_cast<uint8_t>(is.Take());
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||
return static_cast<CharType>(c);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-16 big endian encoding.
|
||||
template<typename CharType = wchar_t>
|
||||
struct UTF16BE : UTF16<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return static_cast<uint16_t>(c) == 0xFEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
|
||||
return static_cast<CharType>(c);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((static_cast<unsigned>(c) >> 8) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(static_cast<unsigned>(c) & 0xFFu));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// UTF32
|
||||
|
||||
//! UTF-32 encoding.
|
||||
/*! http://en.wikipedia.org/wiki/UTF-32
|
||||
\tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead.
|
||||
\note implements Encoding concept
|
||||
|
||||
\note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness.
|
||||
For streaming, use UTF32LE and UTF32BE, which handle endianness.
|
||||
*/
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32 {
|
||||
typedef CharType Ch;
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4);
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
os.Put(codepoint);
|
||||
}
|
||||
|
||||
template<typename OutputStream>
|
||||
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4);
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x10FFFF);
|
||||
PutUnsafe(os, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
|
||||
Ch c = is.Take();
|
||||
*codepoint = c;
|
||||
return c <= 0x10FFFF;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4);
|
||||
Ch c;
|
||||
os.Put(c = is.Take());
|
||||
return c <= 0x10FFFF;
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-32 little endian enocoding.
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32LE : UTF32<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
unsigned c = static_cast<uint8_t>(is.Take());
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
|
||||
return static_cast<CharType>(c);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
|
||||
}
|
||||
};
|
||||
|
||||
//! UTF-32 big endian encoding.
|
||||
template<typename CharType = unsigned>
|
||||
struct UTF32BE : UTF32<CharType> {
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
CharType c = Take(is);
|
||||
return static_cast<uint32_t>(c) == 0x0000FEFFu ? Take(is) : c;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
unsigned c = static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 24;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 16;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take())) << 8;
|
||||
c |= static_cast<unsigned>(static_cast<uint8_t>(is.Take()));
|
||||
return static_cast<CharType>(c);
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0x00u));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFEu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(0xFFu));
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, CharType c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 24) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 16) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>((c >> 8) & 0xFFu));
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c & 0xFFu));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ASCII
|
||||
|
||||
//! ASCII encoding.
|
||||
/*! http://en.wikipedia.org/wiki/ASCII
|
||||
\tparam CharType Code unit for storing 7-bit ASCII data. Default is char.
|
||||
\note implements Encoding concept
|
||||
*/
|
||||
template<typename CharType = char>
|
||||
struct ASCII {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 0 };
|
||||
|
||||
template<typename OutputStream>
|
||||
static void Encode(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x7F);
|
||||
os.Put(static_cast<Ch>(codepoint & 0xFF));
|
||||
}
|
||||
|
||||
template<typename OutputStream>
|
||||
static void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
RAPIDJSON_ASSERT(codepoint <= 0x7F);
|
||||
PutUnsafe(os, static_cast<Ch>(codepoint & 0xFF));
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
uint8_t c = static_cast<uint8_t>(is.Take());
|
||||
*codepoint = c;
|
||||
return c <= 0X7F;
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static bool Validate(InputStream& is, OutputStream& os) {
|
||||
uint8_t c = static_cast<uint8_t>(is.Take());
|
||||
os.Put(static_cast<typename OutputStream::Ch>(c));
|
||||
return c <= 0x7F;
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static CharType TakeBOM(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
uint8_t c = static_cast<uint8_t>(Take(is));
|
||||
return static_cast<Ch>(c);
|
||||
}
|
||||
|
||||
template <typename InputByteStream>
|
||||
static Ch Take(InputByteStream& is) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
|
||||
return static_cast<Ch>(is.Take());
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void PutBOM(OutputByteStream& os) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
(void)os;
|
||||
}
|
||||
|
||||
template <typename OutputByteStream>
|
||||
static void Put(OutputByteStream& os, Ch c) {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
|
||||
os.Put(static_cast<typename OutputByteStream::Ch>(c));
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AutoUTF
|
||||
|
||||
//! Runtime-specified UTF encoding type of a stream.
|
||||
enum UTFType {
|
||||
kUTF8 = 0, //!< UTF-8.
|
||||
kUTF16LE = 1, //!< UTF-16 little endian.
|
||||
kUTF16BE = 2, //!< UTF-16 big endian.
|
||||
kUTF32LE = 3, //!< UTF-32 little endian.
|
||||
kUTF32BE = 4 //!< UTF-32 big endian.
|
||||
};
|
||||
|
||||
//! Dynamically select encoding according to stream's runtime-specified UTF encoding type.
|
||||
/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType().
|
||||
*/
|
||||
template<typename CharType>
|
||||
struct AutoUTF {
|
||||
typedef CharType Ch;
|
||||
|
||||
enum { supportUnicode = 1 };
|
||||
|
||||
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
|
||||
|
||||
template<typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) {
|
||||
typedef void (*EncodeFunc)(OutputStream&, unsigned);
|
||||
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) };
|
||||
(*f[os.GetType()])(os, codepoint);
|
||||
}
|
||||
|
||||
template<typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) {
|
||||
typedef void (*EncodeFunc)(OutputStream&, unsigned);
|
||||
static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) };
|
||||
(*f[os.GetType()])(os, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) {
|
||||
typedef bool (*DecodeFunc)(InputStream&, unsigned*);
|
||||
static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) };
|
||||
return (*f[is.GetType()])(is, codepoint);
|
||||
}
|
||||
|
||||
template <typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
|
||||
typedef bool (*ValidateFunc)(InputStream&, OutputStream&);
|
||||
static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) };
|
||||
return (*f[is.GetType()])(is, os);
|
||||
}
|
||||
|
||||
#undef RAPIDJSON_ENCODINGS_FUNC
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Transcoder
|
||||
|
||||
//! Encoding conversion.
|
||||
template<typename SourceEncoding, typename TargetEncoding>
|
||||
struct Transcoder {
|
||||
//! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream.
|
||||
template<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) {
|
||||
unsigned codepoint;
|
||||
if (!SourceEncoding::Decode(is, &codepoint))
|
||||
return false;
|
||||
TargetEncoding::Encode(os, codepoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
|
||||
unsigned codepoint;
|
||||
if (!SourceEncoding::Decode(is, &codepoint))
|
||||
return false;
|
||||
TargetEncoding::EncodeUnsafe(os, codepoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
//! Validate one Unicode codepoint from an encoded stream.
|
||||
template<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
|
||||
return Transcode(is, os); // Since source/target encoding is different, must transcode.
|
||||
}
|
||||
};
|
||||
|
||||
// Forward declaration.
|
||||
template<typename Stream>
|
||||
inline void PutUnsafe(Stream& stream, typename Stream::Ch c);
|
||||
|
||||
//! Specialization of Transcoder with same source and target encoding.
|
||||
template<typename Encoding>
|
||||
struct Transcoder<Encoding, Encoding> {
|
||||
template<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) {
|
||||
os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class.
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) {
|
||||
PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class.
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename InputStream, typename OutputStream>
|
||||
static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) {
|
||||
return Encoding::Validate(is, os); // source/target encoding are the same
|
||||
}
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__))
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_ENCODINGS_H_
|
176
include/rapidjson/error/en.h
Normal file
176
include/rapidjson/error/en.h
Normal file
@ -0,0 +1,176 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ERROR_EN_H_
|
||||
#define RAPIDJSON_ERROR_EN_H_
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(switch-enum)
|
||||
RAPIDJSON_DIAG_OFF(covered-switch-default)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Maps error code of parsing into error message.
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\param parseErrorCode Error code obtained in parsing.
|
||||
\return the error message.
|
||||
\note User can make a copy of this function for localization.
|
||||
Using switch-case is safer for future modification of error codes.
|
||||
*/
|
||||
inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) {
|
||||
switch (parseErrorCode) {
|
||||
case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
|
||||
|
||||
case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
|
||||
case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values.");
|
||||
|
||||
case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
|
||||
|
||||
case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
|
||||
case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
|
||||
case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
|
||||
|
||||
case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
|
||||
|
||||
case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
|
||||
case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
|
||||
case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
|
||||
case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
|
||||
case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
|
||||
|
||||
case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
|
||||
case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
|
||||
case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number.");
|
||||
|
||||
case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
|
||||
case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
|
||||
|
||||
default: return RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||
}
|
||||
}
|
||||
|
||||
//! Maps error code of validation into error message.
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\param validateErrorCode Error code obtained from validator.
|
||||
\return the error message.
|
||||
\note User can make a copy of this function for localization.
|
||||
Using switch-case is safer for future modification of error codes.
|
||||
*/
|
||||
inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode validateErrorCode) {
|
||||
switch (validateErrorCode) {
|
||||
case kValidateErrors: return RAPIDJSON_ERROR_STRING("One or more validation errors have occurred");
|
||||
case kValidateErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
|
||||
|
||||
case kValidateErrorMultipleOf: return RAPIDJSON_ERROR_STRING("Number '%actual' is not a multiple of the 'multipleOf' value '%expected'.");
|
||||
case kValidateErrorMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than the 'maximum' value '%expected'.");
|
||||
case kValidateErrorExclusiveMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than or equal to the 'exclusiveMaximum' value '%expected'.");
|
||||
case kValidateErrorMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than the 'minimum' value '%expected'.");
|
||||
case kValidateErrorExclusiveMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than or equal to the 'exclusiveMinimum' value '%expected'.");
|
||||
|
||||
case kValidateErrorMaxLength: return RAPIDJSON_ERROR_STRING("String '%actual' is longer than the 'maxLength' value '%expected'.");
|
||||
case kValidateErrorMinLength: return RAPIDJSON_ERROR_STRING("String '%actual' is shorter than the 'minLength' value '%expected'.");
|
||||
case kValidateErrorPattern: return RAPIDJSON_ERROR_STRING("String '%actual' does not match the 'pattern' regular expression.");
|
||||
|
||||
case kValidateErrorMaxItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is longer than the 'maxItems' value '%expected'.");
|
||||
case kValidateErrorMinItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is shorter than the 'minItems' value '%expected'.");
|
||||
case kValidateErrorUniqueItems: return RAPIDJSON_ERROR_STRING("Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true.");
|
||||
case kValidateErrorAdditionalItems: return RAPIDJSON_ERROR_STRING("Array has an additional item at index '%disallowed' that is not allowed by the schema.");
|
||||
|
||||
case kValidateErrorMaxProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is more than 'maxProperties' value '%expected'.");
|
||||
case kValidateErrorMinProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is less than 'minProperties' value '%expected'.");
|
||||
case kValidateErrorRequired: return RAPIDJSON_ERROR_STRING("Object is missing the following members required by the schema: '%missing'.");
|
||||
case kValidateErrorAdditionalProperties: return RAPIDJSON_ERROR_STRING("Object has an additional member '%disallowed' that is not allowed by the schema.");
|
||||
case kValidateErrorPatternProperties: return RAPIDJSON_ERROR_STRING("Object has 'patternProperties' that are not allowed by the schema.");
|
||||
case kValidateErrorDependencies: return RAPIDJSON_ERROR_STRING("Object has missing property or schema dependencies, refer to following errors.");
|
||||
|
||||
case kValidateErrorEnum: return RAPIDJSON_ERROR_STRING("Property has a value that is not one of its allowed enumerated values.");
|
||||
case kValidateErrorType: return RAPIDJSON_ERROR_STRING("Property has a type '%actual' that is not in the following list: '%expected'.");
|
||||
|
||||
case kValidateErrorOneOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'oneOf', refer to following errors.");
|
||||
case kValidateErrorOneOfMatch: return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf', indices '%matches'.");
|
||||
case kValidateErrorAllOf: return RAPIDJSON_ERROR_STRING("Property did not match all of the sub-schemas specified by 'allOf', refer to following errors.");
|
||||
case kValidateErrorAnyOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors.");
|
||||
case kValidateErrorNot: return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'.");
|
||||
|
||||
case kValidateErrorReadOnly: return RAPIDJSON_ERROR_STRING("Property is read-only but has been provided when validation is for writing.");
|
||||
case kValidateErrorWriteOnly: return RAPIDJSON_ERROR_STRING("Property is write-only but has been provided when validation is for reading.");
|
||||
|
||||
default: return RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||
}
|
||||
}
|
||||
|
||||
//! Maps error code of schema document compilation into error message.
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\param schemaErrorCode Error code obtained from compiling the schema document.
|
||||
\return the error message.
|
||||
\note User can make a copy of this function for localization.
|
||||
Using switch-case is safer for future modification of error codes.
|
||||
*/
|
||||
inline const RAPIDJSON_ERROR_CHARTYPE* GetSchemaError_En(SchemaErrorCode schemaErrorCode) {
|
||||
switch (schemaErrorCode) {
|
||||
case kSchemaErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
|
||||
|
||||
case kSchemaErrorStartUnknown: return RAPIDJSON_ERROR_STRING("Pointer '%value' to start of schema does not resolve to a location in the document.");
|
||||
case kSchemaErrorRefPlainName: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' must be a JSON pointer.");
|
||||
case kSchemaErrorRefInvalid: return RAPIDJSON_ERROR_STRING("$ref must not be an empty string.");
|
||||
case kSchemaErrorRefPointerInvalid: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' is not a valid JSON pointer at offset '%offset'.");
|
||||
case kSchemaErrorRefUnknown: return RAPIDJSON_ERROR_STRING("$ref '%value' does not resolve to a location in the target document.");
|
||||
case kSchemaErrorRefCyclical: return RAPIDJSON_ERROR_STRING("$ref '%value' is cyclical.");
|
||||
case kSchemaErrorRefNoRemoteProvider: return RAPIDJSON_ERROR_STRING("$ref is remote but there is no remote provider.");
|
||||
case kSchemaErrorRefNoRemoteSchema: return RAPIDJSON_ERROR_STRING("$ref '%value' is remote but the remote provider did not return a schema.");
|
||||
case kSchemaErrorRegexInvalid: return RAPIDJSON_ERROR_STRING("Invalid regular expression '%value' in 'pattern' or 'patternProperties'.");
|
||||
case kSchemaErrorSpecUnknown: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not recognized.");
|
||||
case kSchemaErrorSpecUnsupported: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not supported.");
|
||||
case kSchemaErrorSpecIllegal: return RAPIDJSON_ERROR_STRING("Both JSON schema draft and OpenAPI version found in document.");
|
||||
case kSchemaErrorReadOnlyAndWriteOnly: return RAPIDJSON_ERROR_STRING("Property must not be both 'readOnly' and 'writeOnly'.");
|
||||
|
||||
default: return RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||
}
|
||||
}
|
||||
|
||||
//! Maps error code of pointer parse into error message.
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\param pointerParseErrorCode Error code obtained from pointer parse.
|
||||
\return the error message.
|
||||
\note User can make a copy of this function for localization.
|
||||
Using switch-case is safer for future modification of error codes.
|
||||
*/
|
||||
inline const RAPIDJSON_ERROR_CHARTYPE* GetPointerParseError_En(PointerParseErrorCode pointerParseErrorCode) {
|
||||
switch (pointerParseErrorCode) {
|
||||
case kPointerParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
|
||||
|
||||
case kPointerParseErrorTokenMustBeginWithSolidus: return RAPIDJSON_ERROR_STRING("A token must begin with a '/'.");
|
||||
case kPointerParseErrorInvalidEscape: return RAPIDJSON_ERROR_STRING("Invalid escape.");
|
||||
case kPointerParseErrorInvalidPercentEncoding: return RAPIDJSON_ERROR_STRING("Invalid percent encoding in URI fragment.");
|
||||
case kPointerParseErrorCharacterMustPercentEncode: return RAPIDJSON_ERROR_STRING("A character must be percent encoded in a URI fragment.");
|
||||
|
||||
default: return RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||
}
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_ERROR_EN_H_
|
285
include/rapidjson/error/error.h
Normal file
285
include/rapidjson/error/error.h
Normal file
@ -0,0 +1,285 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ERROR_ERROR_H_
|
||||
#define RAPIDJSON_ERROR_ERROR_H_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
#endif
|
||||
|
||||
/*! \file error.h */
|
||||
|
||||
/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ERROR_CHARTYPE
|
||||
|
||||
//! Character type of error messages.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
The default character type is \c char.
|
||||
On Windows, user can define this macro as \c TCHAR for supporting both
|
||||
unicode/non-unicode settings.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ERROR_CHARTYPE
|
||||
#define RAPIDJSON_ERROR_CHARTYPE char
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ERROR_STRING
|
||||
|
||||
//! Macro for converting string literal to \ref RAPIDJSON_ERROR_CHARTYPE[].
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
By default this conversion macro does nothing.
|
||||
On Windows, user can define this macro as \c _T(x) for supporting both
|
||||
unicode/non-unicode settings.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ERROR_STRING
|
||||
#define RAPIDJSON_ERROR_STRING(x) x
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ParseErrorCode
|
||||
|
||||
//! Error code of parsing.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
\see GenericReader::Parse, GenericReader::GetParseErrorCode
|
||||
*/
|
||||
enum ParseErrorCode {
|
||||
kParseErrorNone = 0, //!< No error.
|
||||
|
||||
kParseErrorDocumentEmpty, //!< The document is empty.
|
||||
kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values.
|
||||
|
||||
kParseErrorValueInvalid, //!< Invalid value.
|
||||
|
||||
kParseErrorObjectMissName, //!< Missing a name for object member.
|
||||
kParseErrorObjectMissColon, //!< Missing a colon after a name of object member.
|
||||
kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member.
|
||||
|
||||
kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element.
|
||||
|
||||
kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string.
|
||||
kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid.
|
||||
kParseErrorStringEscapeInvalid, //!< Invalid escape character in string.
|
||||
kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string.
|
||||
kParseErrorStringInvalidEncoding, //!< Invalid encoding in string.
|
||||
|
||||
kParseErrorNumberTooBig, //!< Number too big to be stored in double.
|
||||
kParseErrorNumberMissFraction, //!< Miss fraction part in number.
|
||||
kParseErrorNumberMissExponent, //!< Miss exponent in number.
|
||||
|
||||
kParseErrorTermination, //!< Parsing was terminated.
|
||||
kParseErrorUnspecificSyntaxError //!< Unspecific syntax error.
|
||||
};
|
||||
|
||||
//! Result of parsing (wraps ParseErrorCode)
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\code
|
||||
Document doc;
|
||||
ParseResult ok = doc.Parse("[42]");
|
||||
if (!ok) {
|
||||
fprintf(stderr, "JSON parse error: %s (%u)",
|
||||
GetParseError_En(ok.Code()), ok.Offset());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
\endcode
|
||||
\see GenericReader::Parse, GenericDocument::Parse
|
||||
*/
|
||||
struct ParseResult {
|
||||
//!! Unspecified boolean type
|
||||
typedef bool (ParseResult::*BooleanType)() const;
|
||||
public:
|
||||
//! Default constructor, no error.
|
||||
ParseResult() : code_(kParseErrorNone), offset_(0) {}
|
||||
//! Constructor to set an error.
|
||||
ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {}
|
||||
|
||||
//! Get the error code.
|
||||
ParseErrorCode Code() const { return code_; }
|
||||
//! Get the error offset, if \ref IsError(), 0 otherwise.
|
||||
size_t Offset() const { return offset_; }
|
||||
|
||||
//! Explicit conversion to \c bool, returns \c true, iff !\ref IsError().
|
||||
operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; }
|
||||
//! Whether the result is an error.
|
||||
bool IsError() const { return code_ != kParseErrorNone; }
|
||||
|
||||
bool operator==(const ParseResult& that) const { return code_ == that.code_; }
|
||||
bool operator==(ParseErrorCode code) const { return code_ == code; }
|
||||
friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; }
|
||||
|
||||
bool operator!=(const ParseResult& that) const { return !(*this == that); }
|
||||
bool operator!=(ParseErrorCode code) const { return !(*this == code); }
|
||||
friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; }
|
||||
|
||||
//! Reset error code.
|
||||
void Clear() { Set(kParseErrorNone); }
|
||||
//! Update error code and offset.
|
||||
void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; }
|
||||
|
||||
private:
|
||||
ParseErrorCode code_;
|
||||
size_t offset_;
|
||||
};
|
||||
|
||||
//! Function pointer type of GetParseError().
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
|
||||
This is the prototype for \c GetParseError_X(), where \c X is a locale.
|
||||
User can dynamically change locale in runtime, e.g.:
|
||||
\code
|
||||
GetParseErrorFunc GetParseError = GetParseError_En; // or whatever
|
||||
const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode());
|
||||
\endcode
|
||||
*/
|
||||
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ValidateErrorCode
|
||||
|
||||
//! Error codes when validating.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
\see GenericSchemaValidator
|
||||
*/
|
||||
enum ValidateErrorCode {
|
||||
kValidateErrors = -1, //!< Top level error code when kValidateContinueOnErrorsFlag set.
|
||||
kValidateErrorNone = 0, //!< No error.
|
||||
|
||||
kValidateErrorMultipleOf, //!< Number is not a multiple of the 'multipleOf' value.
|
||||
kValidateErrorMaximum, //!< Number is greater than the 'maximum' value.
|
||||
kValidateErrorExclusiveMaximum, //!< Number is greater than or equal to the 'maximum' value.
|
||||
kValidateErrorMinimum, //!< Number is less than the 'minimum' value.
|
||||
kValidateErrorExclusiveMinimum, //!< Number is less than or equal to the 'minimum' value.
|
||||
|
||||
kValidateErrorMaxLength, //!< String is longer than the 'maxLength' value.
|
||||
kValidateErrorMinLength, //!< String is longer than the 'maxLength' value.
|
||||
kValidateErrorPattern, //!< String does not match the 'pattern' regular expression.
|
||||
|
||||
kValidateErrorMaxItems, //!< Array is longer than the 'maxItems' value.
|
||||
kValidateErrorMinItems, //!< Array is shorter than the 'minItems' value.
|
||||
kValidateErrorUniqueItems, //!< Array has duplicate items but 'uniqueItems' is true.
|
||||
kValidateErrorAdditionalItems, //!< Array has additional items that are not allowed by the schema.
|
||||
|
||||
kValidateErrorMaxProperties, //!< Object has more members than 'maxProperties' value.
|
||||
kValidateErrorMinProperties, //!< Object has less members than 'minProperties' value.
|
||||
kValidateErrorRequired, //!< Object is missing one or more members required by the schema.
|
||||
kValidateErrorAdditionalProperties, //!< Object has additional members that are not allowed by the schema.
|
||||
kValidateErrorPatternProperties, //!< See other errors.
|
||||
kValidateErrorDependencies, //!< Object has missing property or schema dependencies.
|
||||
|
||||
kValidateErrorEnum, //!< Property has a value that is not one of its allowed enumerated values.
|
||||
kValidateErrorType, //!< Property has a type that is not allowed by the schema.
|
||||
|
||||
kValidateErrorOneOf, //!< Property did not match any of the sub-schemas specified by 'oneOf'.
|
||||
kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'.
|
||||
kValidateErrorAllOf, //!< Property did not match all of the sub-schemas specified by 'allOf'.
|
||||
kValidateErrorAnyOf, //!< Property did not match any of the sub-schemas specified by 'anyOf'.
|
||||
kValidateErrorNot, //!< Property matched the sub-schema specified by 'not'.
|
||||
|
||||
kValidateErrorReadOnly, //!< Property is read-only but has been provided when validation is for writing
|
||||
kValidateErrorWriteOnly //!< Property is write-only but has been provided when validation is for reading
|
||||
};
|
||||
|
||||
//! Function pointer type of GetValidateError().
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
|
||||
This is the prototype for \c GetValidateError_X(), where \c X is a locale.
|
||||
User can dynamically change locale in runtime, e.g.:
|
||||
\code
|
||||
GetValidateErrorFunc GetValidateError = GetValidateError_En; // or whatever
|
||||
const RAPIDJSON_ERROR_CHARTYPE* s = GetValidateError(validator.GetInvalidSchemaCode());
|
||||
\endcode
|
||||
*/
|
||||
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetValidateErrorFunc)(ValidateErrorCode);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SchemaErrorCode
|
||||
|
||||
//! Error codes when validating.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
\see GenericSchemaValidator
|
||||
*/
|
||||
enum SchemaErrorCode {
|
||||
kSchemaErrorNone = 0, //!< No error.
|
||||
|
||||
kSchemaErrorStartUnknown, //!< Pointer to start of schema does not resolve to a location in the document
|
||||
kSchemaErrorRefPlainName, //!< $ref fragment must be a JSON pointer
|
||||
kSchemaErrorRefInvalid, //!< $ref must not be an empty string
|
||||
kSchemaErrorRefPointerInvalid, //!< $ref fragment is not a valid JSON pointer at offset
|
||||
kSchemaErrorRefUnknown, //!< $ref does not resolve to a location in the target document
|
||||
kSchemaErrorRefCyclical, //!< $ref is cyclical
|
||||
kSchemaErrorRefNoRemoteProvider, //!< $ref is remote but there is no remote provider
|
||||
kSchemaErrorRefNoRemoteSchema, //!< $ref is remote but the remote provider did not return a schema
|
||||
kSchemaErrorRegexInvalid, //!< Invalid regular expression in 'pattern' or 'patternProperties'
|
||||
kSchemaErrorSpecUnknown, //!< JSON schema draft or OpenAPI version is not recognized
|
||||
kSchemaErrorSpecUnsupported, //!< JSON schema draft or OpenAPI version is not supported
|
||||
kSchemaErrorSpecIllegal, //!< Both JSON schema draft and OpenAPI version found in document
|
||||
kSchemaErrorReadOnlyAndWriteOnly //!< Property must not be both 'readOnly' and 'writeOnly'
|
||||
};
|
||||
|
||||
//! Function pointer type of GetSchemaError().
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
|
||||
This is the prototype for \c GetSchemaError_X(), where \c X is a locale.
|
||||
User can dynamically change locale in runtime, e.g.:
|
||||
\code
|
||||
GetSchemaErrorFunc GetSchemaError = GetSchemaError_En; // or whatever
|
||||
const RAPIDJSON_ERROR_CHARTYPE* s = GetSchemaError(validator.GetInvalidSchemaCode());
|
||||
\endcode
|
||||
*/
|
||||
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetSchemaErrorFunc)(SchemaErrorCode);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PointerParseErrorCode
|
||||
|
||||
//! Error code of JSON pointer parsing.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
\see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode
|
||||
*/
|
||||
enum PointerParseErrorCode {
|
||||
kPointerParseErrorNone = 0, //!< The parse is successful
|
||||
|
||||
kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/'
|
||||
kPointerParseErrorInvalidEscape, //!< Invalid escape
|
||||
kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment
|
||||
kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment
|
||||
};
|
||||
|
||||
//! Function pointer type of GetPointerParseError().
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
|
||||
This is the prototype for \c GetPointerParseError_X(), where \c X is a locale.
|
||||
User can dynamically change locale in runtime, e.g.:
|
||||
\code
|
||||
GetPointerParseErrorFunc GetPointerParseError = GetPointerParseError_En; // or whatever
|
||||
const RAPIDJSON_ERROR_CHARTYPE* s = GetPointerParseError(pointer.GetParseErrorCode());
|
||||
\endcode
|
||||
*/
|
||||
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetPointerParseErrorFunc)(PointerParseErrorCode);
|
||||
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_ERROR_ERROR_H_
|
99
include/rapidjson/filereadstream.h
Normal file
99
include/rapidjson/filereadstream.h
Normal file
@ -0,0 +1,99 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_FILEREADSTREAM_H_
|
||||
#define RAPIDJSON_FILEREADSTREAM_H_
|
||||
|
||||
#include "stream.h"
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
RAPIDJSON_DIAG_OFF(unreachable-code)
|
||||
RAPIDJSON_DIAG_OFF(missing-noreturn)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! File byte stream for input using fread().
|
||||
/*!
|
||||
\note implements Stream concept
|
||||
*/
|
||||
class FileReadStream {
|
||||
public:
|
||||
typedef char Ch; //!< Character type (byte).
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param fp File pointer opened for read.
|
||||
\param buffer user-supplied buffer.
|
||||
\param bufferSize size of buffer in bytes. Must >=4 bytes.
|
||||
*/
|
||||
FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
|
||||
RAPIDJSON_ASSERT(fp_ != 0);
|
||||
RAPIDJSON_ASSERT(bufferSize >= 4);
|
||||
Read();
|
||||
}
|
||||
|
||||
Ch Peek() const { return *current_; }
|
||||
Ch Take() { Ch c = *current_; Read(); return c; }
|
||||
size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
// For encoding detection only.
|
||||
const Ch* Peek4() const {
|
||||
return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void Read() {
|
||||
if (current_ < bufferLast_)
|
||||
++current_;
|
||||
else if (!eof_) {
|
||||
count_ += readCount_;
|
||||
readCount_ = std::fread(buffer_, 1, bufferSize_, fp_);
|
||||
bufferLast_ = buffer_ + readCount_ - 1;
|
||||
current_ = buffer_;
|
||||
|
||||
if (readCount_ < bufferSize_) {
|
||||
buffer_[readCount_] = '\0';
|
||||
++bufferLast_;
|
||||
eof_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::FILE* fp_;
|
||||
Ch *buffer_;
|
||||
size_t bufferSize_;
|
||||
Ch *bufferLast_;
|
||||
Ch *current_;
|
||||
size_t readCount_;
|
||||
size_t count_; //!< Number of characters read
|
||||
bool eof_;
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
104
include/rapidjson/filewritestream.h
Normal file
104
include/rapidjson/filewritestream.h
Normal file
@ -0,0 +1,104 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_FILEWRITESTREAM_H_
|
||||
#define RAPIDJSON_FILEWRITESTREAM_H_
|
||||
|
||||
#include "stream.h"
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(unreachable-code)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Wrapper of C file stream for output using fwrite().
|
||||
/*!
|
||||
\note implements Stream concept
|
||||
*/
|
||||
class FileWriteStream {
|
||||
public:
|
||||
typedef char Ch; //!< Character type. Only support char.
|
||||
|
||||
FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) {
|
||||
RAPIDJSON_ASSERT(fp_ != 0);
|
||||
}
|
||||
|
||||
void Put(char c) {
|
||||
if (current_ >= bufferEnd_)
|
||||
Flush();
|
||||
|
||||
*current_++ = c;
|
||||
}
|
||||
|
||||
void PutN(char c, size_t n) {
|
||||
size_t avail = static_cast<size_t>(bufferEnd_ - current_);
|
||||
while (n > avail) {
|
||||
std::memset(current_, c, avail);
|
||||
current_ += avail;
|
||||
Flush();
|
||||
n -= avail;
|
||||
avail = static_cast<size_t>(bufferEnd_ - current_);
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
std::memset(current_, c, n);
|
||||
current_ += n;
|
||||
}
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
if (current_ != buffer_) {
|
||||
size_t result = std::fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
|
||||
if (result < static_cast<size_t>(current_ - buffer_)) {
|
||||
// failure deliberately ignored at this time
|
||||
// added to avoid warn_unused_result build errors
|
||||
}
|
||||
current_ = buffer_;
|
||||
}
|
||||
}
|
||||
|
||||
// Not implemented
|
||||
char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
char Take() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
FileWriteStream(const FileWriteStream&);
|
||||
FileWriteStream& operator=(const FileWriteStream&);
|
||||
|
||||
std::FILE* fp_;
|
||||
char *buffer_;
|
||||
char *bufferEnd_;
|
||||
char *current_;
|
||||
};
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(FileWriteStream& stream, char c, size_t n) {
|
||||
stream.PutN(c, n);
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_FILESTREAM_H_
|
151
include/rapidjson/fwd.h
Normal file
151
include/rapidjson/fwd.h
Normal file
@ -0,0 +1,151 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_FWD_H_
|
||||
#define RAPIDJSON_FWD_H_
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
// encodings.h
|
||||
|
||||
template<typename CharType> struct UTF8;
|
||||
template<typename CharType> struct UTF16;
|
||||
template<typename CharType> struct UTF16BE;
|
||||
template<typename CharType> struct UTF16LE;
|
||||
template<typename CharType> struct UTF32;
|
||||
template<typename CharType> struct UTF32BE;
|
||||
template<typename CharType> struct UTF32LE;
|
||||
template<typename CharType> struct ASCII;
|
||||
template<typename CharType> struct AutoUTF;
|
||||
|
||||
template<typename SourceEncoding, typename TargetEncoding>
|
||||
struct Transcoder;
|
||||
|
||||
// allocators.h
|
||||
|
||||
class CrtAllocator;
|
||||
|
||||
template <typename BaseAllocator>
|
||||
class MemoryPoolAllocator;
|
||||
|
||||
// stream.h
|
||||
|
||||
template <typename Encoding>
|
||||
struct GenericStringStream;
|
||||
|
||||
typedef GenericStringStream<UTF8<char> > StringStream;
|
||||
|
||||
template <typename Encoding>
|
||||
struct GenericInsituStringStream;
|
||||
|
||||
typedef GenericInsituStringStream<UTF8<char> > InsituStringStream;
|
||||
|
||||
// stringbuffer.h
|
||||
|
||||
template <typename Encoding, typename Allocator>
|
||||
class GenericStringBuffer;
|
||||
|
||||
typedef GenericStringBuffer<UTF8<char>, CrtAllocator> StringBuffer;
|
||||
|
||||
// filereadstream.h
|
||||
|
||||
class FileReadStream;
|
||||
|
||||
// filewritestream.h
|
||||
|
||||
class FileWriteStream;
|
||||
|
||||
// memorybuffer.h
|
||||
|
||||
template <typename Allocator>
|
||||
struct GenericMemoryBuffer;
|
||||
|
||||
typedef GenericMemoryBuffer<CrtAllocator> MemoryBuffer;
|
||||
|
||||
// memorystream.h
|
||||
|
||||
struct MemoryStream;
|
||||
|
||||
// reader.h
|
||||
|
||||
template<typename Encoding, typename Derived>
|
||||
struct BaseReaderHandler;
|
||||
|
||||
template <typename SourceEncoding, typename TargetEncoding, typename StackAllocator>
|
||||
class GenericReader;
|
||||
|
||||
typedef GenericReader<UTF8<char>, UTF8<char>, CrtAllocator> Reader;
|
||||
|
||||
// writer.h
|
||||
|
||||
template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
|
||||
class Writer;
|
||||
|
||||
// prettywriter.h
|
||||
|
||||
template<typename OutputStream, typename SourceEncoding, typename TargetEncoding, typename StackAllocator, unsigned writeFlags>
|
||||
class PrettyWriter;
|
||||
|
||||
// document.h
|
||||
|
||||
template <typename Encoding, typename Allocator>
|
||||
class GenericMember;
|
||||
|
||||
template <bool Const, typename Encoding, typename Allocator>
|
||||
class GenericMemberIterator;
|
||||
|
||||
template<typename CharType>
|
||||
struct GenericStringRef;
|
||||
|
||||
template <typename Encoding, typename Allocator>
|
||||
class GenericValue;
|
||||
|
||||
typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
|
||||
|
||||
template <typename Encoding, typename Allocator, typename StackAllocator>
|
||||
class GenericDocument;
|
||||
|
||||
typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator>, CrtAllocator> Document;
|
||||
|
||||
// pointer.h
|
||||
|
||||
template <typename ValueType, typename Allocator>
|
||||
class GenericPointer;
|
||||
|
||||
typedef GenericPointer<Value, CrtAllocator> Pointer;
|
||||
|
||||
// schema.h
|
||||
|
||||
template <typename SchemaDocumentType>
|
||||
class IGenericRemoteSchemaDocumentProvider;
|
||||
|
||||
template <typename ValueT, typename Allocator>
|
||||
class GenericSchemaDocument;
|
||||
|
||||
typedef GenericSchemaDocument<Value, CrtAllocator> SchemaDocument;
|
||||
typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
|
||||
|
||||
template <
|
||||
typename SchemaDocumentType,
|
||||
typename OutputHandler,
|
||||
typename StateAllocator>
|
||||
class GenericSchemaValidator;
|
||||
|
||||
typedef GenericSchemaValidator<SchemaDocument, BaseReaderHandler<UTF8<char>, void>, CrtAllocator> SchemaValidator;
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSONFWD_H_
|
297
include/rapidjson/internal/biginteger.h
Normal file
297
include/rapidjson/internal/biginteger.h
Normal file
@ -0,0 +1,297 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_BIGINTEGER_H_
|
||||
#define RAPIDJSON_BIGINTEGER_H_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64)
|
||||
#include <intrin.h> // for _umul128
|
||||
#if !defined(_ARM64EC_)
|
||||
#pragma intrinsic(_umul128)
|
||||
#else
|
||||
#pragma comment(lib,"softintrin")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
class BigInteger {
|
||||
public:
|
||||
typedef uint64_t Type;
|
||||
|
||||
BigInteger(const BigInteger& rhs) : count_(rhs.count_) {
|
||||
std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
|
||||
}
|
||||
|
||||
explicit BigInteger(uint64_t u) : count_(1) {
|
||||
digits_[0] = u;
|
||||
}
|
||||
|
||||
template<typename Ch>
|
||||
BigInteger(const Ch* decimals, size_t length) : count_(1) {
|
||||
RAPIDJSON_ASSERT(length > 0);
|
||||
digits_[0] = 0;
|
||||
size_t i = 0;
|
||||
const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19
|
||||
while (length >= kMaxDigitPerIteration) {
|
||||
AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration);
|
||||
length -= kMaxDigitPerIteration;
|
||||
i += kMaxDigitPerIteration;
|
||||
}
|
||||
|
||||
if (length > 0)
|
||||
AppendDecimal64(decimals + i, decimals + i + length);
|
||||
}
|
||||
|
||||
BigInteger& operator=(const BigInteger &rhs)
|
||||
{
|
||||
if (this != &rhs) {
|
||||
count_ = rhs.count_;
|
||||
std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator=(uint64_t u) {
|
||||
digits_[0] = u;
|
||||
count_ = 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator+=(uint64_t u) {
|
||||
Type backup = digits_[0];
|
||||
digits_[0] += u;
|
||||
for (size_t i = 0; i < count_ - 1; i++) {
|
||||
if (digits_[i] >= backup)
|
||||
return *this; // no carry
|
||||
backup = digits_[i + 1];
|
||||
digits_[i + 1] += 1;
|
||||
}
|
||||
|
||||
// Last carry
|
||||
if (digits_[count_ - 1] < backup)
|
||||
PushBack(1);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator*=(uint64_t u) {
|
||||
if (u == 0) return *this = 0;
|
||||
if (u == 1) return *this;
|
||||
if (*this == 1) return *this = u;
|
||||
|
||||
uint64_t k = 0;
|
||||
for (size_t i = 0; i < count_; i++) {
|
||||
uint64_t hi;
|
||||
digits_[i] = MulAdd64(digits_[i], u, k, &hi);
|
||||
k = hi;
|
||||
}
|
||||
|
||||
if (k > 0)
|
||||
PushBack(k);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator*=(uint32_t u) {
|
||||
if (u == 0) return *this = 0;
|
||||
if (u == 1) return *this;
|
||||
if (*this == 1) return *this = u;
|
||||
|
||||
uint64_t k = 0;
|
||||
for (size_t i = 0; i < count_; i++) {
|
||||
const uint64_t c = digits_[i] >> 32;
|
||||
const uint64_t d = digits_[i] & 0xFFFFFFFF;
|
||||
const uint64_t uc = u * c;
|
||||
const uint64_t ud = u * d;
|
||||
const uint64_t p0 = ud + k;
|
||||
const uint64_t p1 = uc + (p0 >> 32);
|
||||
digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32);
|
||||
k = p1 >> 32;
|
||||
}
|
||||
|
||||
if (k > 0)
|
||||
PushBack(k);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInteger& operator<<=(size_t shift) {
|
||||
if (IsZero() || shift == 0) return *this;
|
||||
|
||||
size_t offset = shift / kTypeBit;
|
||||
size_t interShift = shift % kTypeBit;
|
||||
RAPIDJSON_ASSERT(count_ + offset <= kCapacity);
|
||||
|
||||
if (interShift == 0) {
|
||||
std::memmove(digits_ + offset, digits_, count_ * sizeof(Type));
|
||||
count_ += offset;
|
||||
}
|
||||
else {
|
||||
digits_[count_] = 0;
|
||||
for (size_t i = count_; i > 0; i--)
|
||||
digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift));
|
||||
digits_[offset] = digits_[0] << interShift;
|
||||
count_ += offset;
|
||||
if (digits_[count_])
|
||||
count_++;
|
||||
}
|
||||
|
||||
std::memset(digits_, 0, offset * sizeof(Type));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const BigInteger& rhs) const {
|
||||
return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0;
|
||||
}
|
||||
|
||||
bool operator==(const Type rhs) const {
|
||||
return count_ == 1 && digits_[0] == rhs;
|
||||
}
|
||||
|
||||
BigInteger& MultiplyPow5(unsigned exp) {
|
||||
static const uint32_t kPow5[12] = {
|
||||
5,
|
||||
5 * 5,
|
||||
5 * 5 * 5,
|
||||
5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
|
||||
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
|
||||
};
|
||||
if (exp == 0) return *this;
|
||||
for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27
|
||||
for (; exp >= 13; exp -= 13) *this *= static_cast<uint32_t>(1220703125u); // 5^13
|
||||
if (exp > 0) *this *= kPow5[exp - 1];
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Compute absolute difference of this and rhs.
|
||||
// Assume this != rhs
|
||||
bool Difference(const BigInteger& rhs, BigInteger* out) const {
|
||||
int cmp = Compare(rhs);
|
||||
RAPIDJSON_ASSERT(cmp != 0);
|
||||
const BigInteger *a, *b; // Makes a > b
|
||||
bool ret;
|
||||
if (cmp < 0) { a = &rhs; b = this; ret = true; }
|
||||
else { a = this; b = &rhs; ret = false; }
|
||||
|
||||
Type borrow = 0;
|
||||
for (size_t i = 0; i < a->count_; i++) {
|
||||
Type d = a->digits_[i] - borrow;
|
||||
if (i < b->count_)
|
||||
d -= b->digits_[i];
|
||||
borrow = (d > a->digits_[i]) ? 1 : 0;
|
||||
out->digits_[i] = d;
|
||||
if (d != 0)
|
||||
out->count_ = i + 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Compare(const BigInteger& rhs) const {
|
||||
if (count_ != rhs.count_)
|
||||
return count_ < rhs.count_ ? -1 : 1;
|
||||
|
||||
for (size_t i = count_; i-- > 0;)
|
||||
if (digits_[i] != rhs.digits_[i])
|
||||
return digits_[i] < rhs.digits_[i] ? -1 : 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t GetCount() const { return count_; }
|
||||
Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; }
|
||||
bool IsZero() const { return count_ == 1 && digits_[0] == 0; }
|
||||
|
||||
private:
|
||||
template<typename Ch>
|
||||
void AppendDecimal64(const Ch* begin, const Ch* end) {
|
||||
uint64_t u = ParseUint64(begin, end);
|
||||
if (IsZero())
|
||||
*this = u;
|
||||
else {
|
||||
unsigned exp = static_cast<unsigned>(end - begin);
|
||||
(MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u
|
||||
}
|
||||
}
|
||||
|
||||
void PushBack(Type digit) {
|
||||
RAPIDJSON_ASSERT(count_ < kCapacity);
|
||||
digits_[count_++] = digit;
|
||||
}
|
||||
|
||||
template<typename Ch>
|
||||
static uint64_t ParseUint64(const Ch* begin, const Ch* end) {
|
||||
uint64_t r = 0;
|
||||
for (const Ch* p = begin; p != end; ++p) {
|
||||
RAPIDJSON_ASSERT(*p >= Ch('0') && *p <= Ch('9'));
|
||||
r = r * 10u + static_cast<unsigned>(*p - Ch('0'));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Assume a * b + k < 2^128
|
||||
static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) {
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||
uint64_t low = _umul128(a, b, outHigh) + k;
|
||||
if (low < k)
|
||||
(*outHigh)++;
|
||||
return low;
|
||||
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
|
||||
__extension__ typedef unsigned __int128 uint128;
|
||||
uint128 p = static_cast<uint128>(a) * static_cast<uint128>(b);
|
||||
p += k;
|
||||
*outHigh = static_cast<uint64_t>(p >> 64);
|
||||
return static_cast<uint64_t>(p);
|
||||
#else
|
||||
const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32;
|
||||
uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1;
|
||||
x1 += (x0 >> 32); // can't give carry
|
||||
x1 += x2;
|
||||
if (x1 < x2)
|
||||
x3 += (static_cast<uint64_t>(1) << 32);
|
||||
uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF);
|
||||
uint64_t hi = x3 + (x1 >> 32);
|
||||
|
||||
lo += k;
|
||||
if (lo < k)
|
||||
hi++;
|
||||
*outHigh = hi;
|
||||
return lo;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000
|
||||
static const size_t kCapacity = kBitCount / sizeof(Type);
|
||||
static const size_t kTypeBit = sizeof(Type) * 8;
|
||||
|
||||
Type digits_[kCapacity];
|
||||
size_t count_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_BIGINTEGER_H_
|
71
include/rapidjson/internal/clzll.h
Normal file
71
include/rapidjson/internal/clzll.h
Normal file
@ -0,0 +1,71 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_CLZLL_H_
|
||||
#define RAPIDJSON_CLZLL_H_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
#if defined(_MSC_VER) && !defined(UNDER_CE)
|
||||
#include <intrin.h>
|
||||
#if defined(_WIN64)
|
||||
#pragma intrinsic(_BitScanReverse64)
|
||||
#else
|
||||
#pragma intrinsic(_BitScanReverse)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
inline uint32_t clzll(uint64_t x) {
|
||||
// Passing 0 to __builtin_clzll is UB in GCC and results in an
|
||||
// infinite loop in the software implementation.
|
||||
RAPIDJSON_ASSERT(x != 0);
|
||||
|
||||
#if defined(_MSC_VER) && !defined(UNDER_CE)
|
||||
unsigned long r = 0;
|
||||
#if defined(_WIN64)
|
||||
_BitScanReverse64(&r, x);
|
||||
#else
|
||||
// Scan the high 32 bits.
|
||||
if (_BitScanReverse(&r, static_cast<uint32_t>(x >> 32)))
|
||||
return 63 - (r + 32);
|
||||
|
||||
// Scan the low 32 bits.
|
||||
_BitScanReverse(&r, static_cast<uint32_t>(x & 0xFFFFFFFF));
|
||||
#endif // _WIN64
|
||||
|
||||
return 63 - r;
|
||||
#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll)
|
||||
// __builtin_clzll wrapper
|
||||
return static_cast<uint32_t>(__builtin_clzll(x));
|
||||
#else
|
||||
// naive version
|
||||
uint32_t r = 0;
|
||||
while (!(x & (static_cast<uint64_t>(1) << 63))) {
|
||||
x <<= 1;
|
||||
++r;
|
||||
}
|
||||
|
||||
return r;
|
||||
#endif // _MSC_VER
|
||||
}
|
||||
|
||||
#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_CLZLL_H_
|
261
include/rapidjson/internal/diyfp.h
Normal file
261
include/rapidjson/internal/diyfp.h
Normal file
@ -0,0 +1,261 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
|
||||
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
|
||||
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
|
||||
|
||||
#ifndef RAPIDJSON_DIYFP_H_
|
||||
#define RAPIDJSON_DIYFP_H_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
#include "clzll.h"
|
||||
#include <limits>
|
||||
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER)
|
||||
#include <intrin.h>
|
||||
#if !defined(_ARM64EC_)
|
||||
#pragma intrinsic(_umul128)
|
||||
#else
|
||||
#pragma comment(lib,"softintrin")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
#endif
|
||||
|
||||
struct DiyFp {
|
||||
DiyFp() : f(), e() {}
|
||||
|
||||
DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {}
|
||||
|
||||
explicit DiyFp(double d) {
|
||||
union {
|
||||
double d;
|
||||
uint64_t u64;
|
||||
} u = { d };
|
||||
|
||||
int biased_e = static_cast<int>((u.u64 & kDpExponentMask) >> kDpSignificandSize);
|
||||
uint64_t significand = (u.u64 & kDpSignificandMask);
|
||||
if (biased_e != 0) {
|
||||
f = significand + kDpHiddenBit;
|
||||
e = biased_e - kDpExponentBias;
|
||||
}
|
||||
else {
|
||||
f = significand;
|
||||
e = kDpMinExponent + 1;
|
||||
}
|
||||
}
|
||||
|
||||
DiyFp operator-(const DiyFp& rhs) const {
|
||||
return DiyFp(f - rhs.f, e);
|
||||
}
|
||||
|
||||
DiyFp operator*(const DiyFp& rhs) const {
|
||||
#if defined(_MSC_VER) && defined(_M_AMD64)
|
||||
uint64_t h;
|
||||
uint64_t l = _umul128(f, rhs.f, &h);
|
||||
if (l & (uint64_t(1) << 63)) // rounding
|
||||
h++;
|
||||
return DiyFp(h, e + rhs.e + 64);
|
||||
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
|
||||
__extension__ typedef unsigned __int128 uint128;
|
||||
uint128 p = static_cast<uint128>(f) * static_cast<uint128>(rhs.f);
|
||||
uint64_t h = static_cast<uint64_t>(p >> 64);
|
||||
uint64_t l = static_cast<uint64_t>(p);
|
||||
if (l & (uint64_t(1) << 63)) // rounding
|
||||
h++;
|
||||
return DiyFp(h, e + rhs.e + 64);
|
||||
#else
|
||||
const uint64_t M32 = 0xFFFFFFFF;
|
||||
const uint64_t a = f >> 32;
|
||||
const uint64_t b = f & M32;
|
||||
const uint64_t c = rhs.f >> 32;
|
||||
const uint64_t d = rhs.f & M32;
|
||||
const uint64_t ac = a * c;
|
||||
const uint64_t bc = b * c;
|
||||
const uint64_t ad = a * d;
|
||||
const uint64_t bd = b * d;
|
||||
uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
|
||||
tmp += 1U << 31; /// mult_round
|
||||
return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
|
||||
#endif
|
||||
}
|
||||
|
||||
DiyFp Normalize() const {
|
||||
int s = static_cast<int>(clzll(f));
|
||||
return DiyFp(f << s, e - s);
|
||||
}
|
||||
|
||||
DiyFp NormalizeBoundary() const {
|
||||
DiyFp res = *this;
|
||||
while (!(res.f & (kDpHiddenBit << 1))) {
|
||||
res.f <<= 1;
|
||||
res.e--;
|
||||
}
|
||||
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
|
||||
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
|
||||
return res;
|
||||
}
|
||||
|
||||
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
|
||||
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
|
||||
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
|
||||
mi.f <<= mi.e - pl.e;
|
||||
mi.e = pl.e;
|
||||
*plus = pl;
|
||||
*minus = mi;
|
||||
}
|
||||
|
||||
double ToDouble() const {
|
||||
union {
|
||||
double d;
|
||||
uint64_t u64;
|
||||
}u;
|
||||
RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask);
|
||||
if (e < kDpDenormalExponent) {
|
||||
// Underflow.
|
||||
return 0.0;
|
||||
}
|
||||
if (e >= kDpMaxExponent) {
|
||||
// Overflow.
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 :
|
||||
static_cast<uint64_t>(e + kDpExponentBias);
|
||||
u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize);
|
||||
return u.d;
|
||||
}
|
||||
|
||||
static const int kDiySignificandSize = 64;
|
||||
static const int kDpSignificandSize = 52;
|
||||
static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
|
||||
static const int kDpMaxExponent = 0x7FF - kDpExponentBias;
|
||||
static const int kDpMinExponent = -kDpExponentBias;
|
||||
static const int kDpDenormalExponent = -kDpExponentBias + 1;
|
||||
static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
|
||||
static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
||||
static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
|
||||
|
||||
uint64_t f;
|
||||
int e;
|
||||
};
|
||||
|
||||
inline DiyFp GetCachedPowerByIndex(size_t index) {
|
||||
// 10^-348, 10^-340, ..., 10^340
|
||||
static const uint64_t kCachedPowers_F[] = {
|
||||
RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76),
|
||||
RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea),
|
||||
RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df),
|
||||
RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f),
|
||||
RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c),
|
||||
RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5),
|
||||
RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d),
|
||||
RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637),
|
||||
RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7),
|
||||
RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5),
|
||||
RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b),
|
||||
RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996),
|
||||
RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
|
||||
RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8),
|
||||
RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053),
|
||||
RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd),
|
||||
RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94),
|
||||
RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b),
|
||||
RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac),
|
||||
RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3),
|
||||
RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb),
|
||||
RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c),
|
||||
RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000),
|
||||
RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984),
|
||||
RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70),
|
||||
RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245),
|
||||
RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8),
|
||||
RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a),
|
||||
RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea),
|
||||
RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85),
|
||||
RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2),
|
||||
RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3),
|
||||
RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25),
|
||||
RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece),
|
||||
RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5),
|
||||
RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a),
|
||||
RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
|
||||
RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a),
|
||||
RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129),
|
||||
RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429),
|
||||
RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d),
|
||||
RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841),
|
||||
RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9),
|
||||
RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b)
|
||||
};
|
||||
static const int16_t kCachedPowers_E[] = {
|
||||
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
|
||||
-954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
|
||||
-688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
|
||||
-422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
|
||||
-157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
|
||||
109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
|
||||
375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
|
||||
641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
|
||||
907, 933, 960, 986, 1013, 1039, 1066
|
||||
};
|
||||
RAPIDJSON_ASSERT(index < 87);
|
||||
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
|
||||
}
|
||||
|
||||
inline DiyFp GetCachedPower(int e, int* K) {
|
||||
|
||||
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
|
||||
double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
|
||||
int k = static_cast<int>(dk);
|
||||
if (dk - k > 0.0)
|
||||
k++;
|
||||
|
||||
unsigned index = static_cast<unsigned>((k >> 3) + 1);
|
||||
*K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
|
||||
|
||||
return GetCachedPowerByIndex(index);
|
||||
}
|
||||
|
||||
inline DiyFp GetCachedPower10(int exp, int *outExp) {
|
||||
RAPIDJSON_ASSERT(exp >= -348);
|
||||
unsigned index = static_cast<unsigned>(exp + 348) / 8u;
|
||||
*outExp = -348 + static_cast<int>(index) * 8;
|
||||
return GetCachedPowerByIndex(index);
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
#endif
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_DIYFP_H_
|
249
include/rapidjson/internal/dtoa.h
Normal file
249
include/rapidjson/internal/dtoa.h
Normal file
@ -0,0 +1,249 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
|
||||
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
|
||||
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
|
||||
|
||||
#ifndef RAPIDJSON_DTOA_
|
||||
#define RAPIDJSON_DTOA_
|
||||
|
||||
#include "itoa.h" // GetDigitsLut()
|
||||
#include "diyfp.h"
|
||||
#include "ieee754.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124
|
||||
#endif
|
||||
|
||||
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
|
||||
while (rest < wp_w && delta - rest >= ten_kappa &&
|
||||
(rest + ten_kappa < wp_w || /// closer
|
||||
wp_w - rest > rest + ten_kappa - wp_w)) {
|
||||
buffer[len - 1]--;
|
||||
rest += ten_kappa;
|
||||
}
|
||||
}
|
||||
|
||||
inline int CountDecimalDigit32(uint32_t n) {
|
||||
// Simple pure C++ implementation was faster than __builtin_clz version in this situation.
|
||||
if (n < 10) return 1;
|
||||
if (n < 100) return 2;
|
||||
if (n < 1000) return 3;
|
||||
if (n < 10000) return 4;
|
||||
if (n < 100000) return 5;
|
||||
if (n < 1000000) return 6;
|
||||
if (n < 10000000) return 7;
|
||||
if (n < 100000000) return 8;
|
||||
// Will not reach 10 digits in DigitGen()
|
||||
//if (n < 1000000000) return 9;
|
||||
//return 10;
|
||||
return 9;
|
||||
}
|
||||
|
||||
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
|
||||
static const uint64_t kPow10[] = { 1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, 100000ULL, 1000000ULL, 10000000ULL, 100000000ULL,
|
||||
1000000000ULL, 10000000000ULL, 100000000000ULL, 1000000000000ULL,
|
||||
10000000000000ULL, 100000000000000ULL, 1000000000000000ULL,
|
||||
10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL,
|
||||
10000000000000000000ULL };
|
||||
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
|
||||
const DiyFp wp_w = Mp - W;
|
||||
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
|
||||
uint64_t p2 = Mp.f & (one.f - 1);
|
||||
int kappa = CountDecimalDigit32(p1); // kappa in [0, 9]
|
||||
*len = 0;
|
||||
|
||||
while (kappa > 0) {
|
||||
uint32_t d = 0;
|
||||
switch (kappa) {
|
||||
case 9: d = p1 / 100000000; p1 %= 100000000; break;
|
||||
case 8: d = p1 / 10000000; p1 %= 10000000; break;
|
||||
case 7: d = p1 / 1000000; p1 %= 1000000; break;
|
||||
case 6: d = p1 / 100000; p1 %= 100000; break;
|
||||
case 5: d = p1 / 10000; p1 %= 10000; break;
|
||||
case 4: d = p1 / 1000; p1 %= 1000; break;
|
||||
case 3: d = p1 / 100; p1 %= 100; break;
|
||||
case 2: d = p1 / 10; p1 %= 10; break;
|
||||
case 1: d = p1; p1 = 0; break;
|
||||
default:;
|
||||
}
|
||||
if (d || *len)
|
||||
buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
|
||||
kappa--;
|
||||
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
|
||||
if (tmp <= delta) {
|
||||
*K += kappa;
|
||||
GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// kappa = 0
|
||||
for (;;) {
|
||||
p2 *= 10;
|
||||
delta *= 10;
|
||||
char d = static_cast<char>(p2 >> -one.e);
|
||||
if (d || *len)
|
||||
buffer[(*len)++] = static_cast<char>('0' + d);
|
||||
p2 &= one.f - 1;
|
||||
kappa--;
|
||||
if (p2 < delta) {
|
||||
*K += kappa;
|
||||
int index = -kappa;
|
||||
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 20 ? kPow10[index] : 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void Grisu2(double value, char* buffer, int* length, int* K) {
|
||||
const DiyFp v(value);
|
||||
DiyFp w_m, w_p;
|
||||
v.NormalizedBoundaries(&w_m, &w_p);
|
||||
|
||||
const DiyFp c_mk = GetCachedPower(w_p.e, K);
|
||||
const DiyFp W = v.Normalize() * c_mk;
|
||||
DiyFp Wp = w_p * c_mk;
|
||||
DiyFp Wm = w_m * c_mk;
|
||||
Wm.f++;
|
||||
Wp.f--;
|
||||
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
|
||||
}
|
||||
|
||||
inline char* WriteExponent(int K, char* buffer) {
|
||||
if (K < 0) {
|
||||
*buffer++ = '-';
|
||||
K = -K;
|
||||
}
|
||||
|
||||
if (K >= 100) {
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
|
||||
K %= 100;
|
||||
const char* d = GetDigitsLut() + K * 2;
|
||||
*buffer++ = d[0];
|
||||
*buffer++ = d[1];
|
||||
}
|
||||
else if (K >= 10) {
|
||||
const char* d = GetDigitsLut() + K * 2;
|
||||
*buffer++ = d[0];
|
||||
*buffer++ = d[1];
|
||||
}
|
||||
else
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(K));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) {
|
||||
const int kk = length + k; // 10^(kk-1) <= v < 10^kk
|
||||
|
||||
if (0 <= k && kk <= 21) {
|
||||
// 1234e7 -> 12340000000
|
||||
for (int i = length; i < kk; i++)
|
||||
buffer[i] = '0';
|
||||
buffer[kk] = '.';
|
||||
buffer[kk + 1] = '0';
|
||||
return &buffer[kk + 2];
|
||||
}
|
||||
else if (0 < kk && kk <= 21) {
|
||||
// 1234e-2 -> 12.34
|
||||
std::memmove(&buffer[kk + 1], &buffer[kk], static_cast<size_t>(length - kk));
|
||||
buffer[kk] = '.';
|
||||
if (0 > k + maxDecimalPlaces) {
|
||||
// When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1
|
||||
// Remove extra trailing zeros (at least one) after truncation.
|
||||
for (int i = kk + maxDecimalPlaces; i > kk + 1; i--)
|
||||
if (buffer[i] != '0')
|
||||
return &buffer[i + 1];
|
||||
return &buffer[kk + 2]; // Reserve one zero
|
||||
}
|
||||
else
|
||||
return &buffer[length + 1];
|
||||
}
|
||||
else if (-6 < kk && kk <= 0) {
|
||||
// 1234e-6 -> 0.001234
|
||||
const int offset = 2 - kk;
|
||||
std::memmove(&buffer[offset], &buffer[0], static_cast<size_t>(length));
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
for (int i = 2; i < offset; i++)
|
||||
buffer[i] = '0';
|
||||
if (length - kk > maxDecimalPlaces) {
|
||||
// When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1
|
||||
// Remove extra trailing zeros (at least one) after truncation.
|
||||
for (int i = maxDecimalPlaces + 1; i > 2; i--)
|
||||
if (buffer[i] != '0')
|
||||
return &buffer[i + 1];
|
||||
return &buffer[3]; // Reserve one zero
|
||||
}
|
||||
else
|
||||
return &buffer[length + offset];
|
||||
}
|
||||
else if (kk < -maxDecimalPlaces) {
|
||||
// Truncate to zero
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
buffer[2] = '0';
|
||||
return &buffer[3];
|
||||
}
|
||||
else if (length == 1) {
|
||||
// 1e30
|
||||
buffer[1] = 'e';
|
||||
return WriteExponent(kk - 1, &buffer[2]);
|
||||
}
|
||||
else {
|
||||
// 1234e30 -> 1.234e33
|
||||
std::memmove(&buffer[2], &buffer[1], static_cast<size_t>(length - 1));
|
||||
buffer[1] = '.';
|
||||
buffer[length + 1] = 'e';
|
||||
return WriteExponent(kk - 1, &buffer[0 + length + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) {
|
||||
RAPIDJSON_ASSERT(maxDecimalPlaces >= 1);
|
||||
Double d(value);
|
||||
if (d.IsZero()) {
|
||||
if (d.Sign())
|
||||
*buffer++ = '-'; // -0.0, Issue #289
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
buffer[2] = '0';
|
||||
return &buffer[3];
|
||||
}
|
||||
else {
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
value = -value;
|
||||
}
|
||||
int length, K;
|
||||
Grisu2(value, buffer, &length, &K);
|
||||
return Prettify(buffer, length, K, maxDecimalPlaces);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_DTOA_
|
78
include/rapidjson/internal/ieee754.h
Normal file
78
include/rapidjson/internal/ieee754.h
Normal file
@ -0,0 +1,78 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_IEEE754_
|
||||
#define RAPIDJSON_IEEE754_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
class Double {
|
||||
public:
|
||||
Double() {}
|
||||
Double(double d) : d_(d) {}
|
||||
Double(uint64_t u) : u_(u) {}
|
||||
|
||||
double Value() const { return d_; }
|
||||
uint64_t Uint64Value() const { return u_; }
|
||||
|
||||
double NextPositiveDouble() const {
|
||||
RAPIDJSON_ASSERT(!Sign());
|
||||
return Double(u_ + 1).Value();
|
||||
}
|
||||
|
||||
bool Sign() const { return (u_ & kSignMask) != 0; }
|
||||
uint64_t Significand() const { return u_ & kSignificandMask; }
|
||||
int Exponent() const { return static_cast<int>(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); }
|
||||
|
||||
bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; }
|
||||
bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; }
|
||||
bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; }
|
||||
bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; }
|
||||
bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; }
|
||||
|
||||
uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); }
|
||||
int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; }
|
||||
uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; }
|
||||
|
||||
static int EffectiveSignificandSize(int order) {
|
||||
if (order >= -1021)
|
||||
return 53;
|
||||
else if (order <= -1074)
|
||||
return 0;
|
||||
else
|
||||
return order + 1074;
|
||||
}
|
||||
|
||||
private:
|
||||
static const int kSignificandSize = 52;
|
||||
static const int kExponentBias = 0x3FF;
|
||||
static const int kDenormalExponent = 1 - kExponentBias;
|
||||
static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000);
|
||||
static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
|
||||
static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
|
||||
static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
|
||||
|
||||
union {
|
||||
double d_;
|
||||
uint64_t u_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_IEEE754_
|
308
include/rapidjson/internal/itoa.h
Normal file
308
include/rapidjson/internal/itoa.h
Normal file
@ -0,0 +1,308 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ITOA_
|
||||
#define RAPIDJSON_ITOA_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
inline const char* GetDigitsLut() {
|
||||
static const char cDigitsLut[200] = {
|
||||
'0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
|
||||
'1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9',
|
||||
'2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9',
|
||||
'3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9',
|
||||
'4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9',
|
||||
'5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9',
|
||||
'6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9',
|
||||
'7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9',
|
||||
'8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9',
|
||||
'9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9'
|
||||
};
|
||||
return cDigitsLut;
|
||||
}
|
||||
|
||||
inline char* u32toa(uint32_t value, char* buffer) {
|
||||
RAPIDJSON_ASSERT(buffer != 0);
|
||||
|
||||
const char* cDigitsLut = GetDigitsLut();
|
||||
|
||||
if (value < 10000) {
|
||||
const uint32_t d1 = (value / 100) << 1;
|
||||
const uint32_t d2 = (value % 100) << 1;
|
||||
|
||||
if (value >= 1000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= 100)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= 10)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
}
|
||||
else if (value < 100000000) {
|
||||
// value = bbbbcccc
|
||||
const uint32_t b = value / 10000;
|
||||
const uint32_t c = value % 10000;
|
||||
|
||||
const uint32_t d1 = (b / 100) << 1;
|
||||
const uint32_t d2 = (b % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c / 100) << 1;
|
||||
const uint32_t d4 = (c % 100) << 1;
|
||||
|
||||
if (value >= 10000000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= 1000000)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= 100000)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
}
|
||||
else {
|
||||
// value = aabbbbcccc in decimal
|
||||
|
||||
const uint32_t a = value / 100000000; // 1 to 42
|
||||
value %= 100000000;
|
||||
|
||||
if (a >= 10) {
|
||||
const unsigned i = a << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
}
|
||||
else
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
|
||||
|
||||
const uint32_t b = value / 10000; // 0 to 9999
|
||||
const uint32_t c = value % 10000; // 0 to 9999
|
||||
|
||||
const uint32_t d1 = (b / 100) << 1;
|
||||
const uint32_t d2 = (b % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c / 100) << 1;
|
||||
const uint32_t d4 = (c % 100) << 1;
|
||||
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* i32toa(int32_t value, char* buffer) {
|
||||
RAPIDJSON_ASSERT(buffer != 0);
|
||||
uint32_t u = static_cast<uint32_t>(value);
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
u = ~u + 1;
|
||||
}
|
||||
|
||||
return u32toa(u, buffer);
|
||||
}
|
||||
|
||||
inline char* u64toa(uint64_t value, char* buffer) {
|
||||
RAPIDJSON_ASSERT(buffer != 0);
|
||||
const char* cDigitsLut = GetDigitsLut();
|
||||
const uint64_t kTen8 = 100000000;
|
||||
const uint64_t kTen9 = kTen8 * 10;
|
||||
const uint64_t kTen10 = kTen8 * 100;
|
||||
const uint64_t kTen11 = kTen8 * 1000;
|
||||
const uint64_t kTen12 = kTen8 * 10000;
|
||||
const uint64_t kTen13 = kTen8 * 100000;
|
||||
const uint64_t kTen14 = kTen8 * 1000000;
|
||||
const uint64_t kTen15 = kTen8 * 10000000;
|
||||
const uint64_t kTen16 = kTen8 * kTen8;
|
||||
|
||||
if (value < kTen8) {
|
||||
uint32_t v = static_cast<uint32_t>(value);
|
||||
if (v < 10000) {
|
||||
const uint32_t d1 = (v / 100) << 1;
|
||||
const uint32_t d2 = (v % 100) << 1;
|
||||
|
||||
if (v >= 1000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (v >= 100)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (v >= 10)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
}
|
||||
else {
|
||||
// value = bbbbcccc
|
||||
const uint32_t b = v / 10000;
|
||||
const uint32_t c = v % 10000;
|
||||
|
||||
const uint32_t d1 = (b / 100) << 1;
|
||||
const uint32_t d2 = (b % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c / 100) << 1;
|
||||
const uint32_t d4 = (c % 100) << 1;
|
||||
|
||||
if (value >= 10000000)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= 1000000)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= 100000)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
}
|
||||
}
|
||||
else if (value < kTen16) {
|
||||
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
|
||||
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
|
||||
|
||||
const uint32_t b0 = v0 / 10000;
|
||||
const uint32_t c0 = v0 % 10000;
|
||||
|
||||
const uint32_t d1 = (b0 / 100) << 1;
|
||||
const uint32_t d2 = (b0 % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c0 / 100) << 1;
|
||||
const uint32_t d4 = (c0 % 100) << 1;
|
||||
|
||||
const uint32_t b1 = v1 / 10000;
|
||||
const uint32_t c1 = v1 % 10000;
|
||||
|
||||
const uint32_t d5 = (b1 / 100) << 1;
|
||||
const uint32_t d6 = (b1 % 100) << 1;
|
||||
|
||||
const uint32_t d7 = (c1 / 100) << 1;
|
||||
const uint32_t d8 = (c1 % 100) << 1;
|
||||
|
||||
if (value >= kTen15)
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
if (value >= kTen14)
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
if (value >= kTen13)
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
if (value >= kTen12)
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
if (value >= kTen11)
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
if (value >= kTen10)
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
if (value >= kTen9)
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
*buffer++ = cDigitsLut[d5];
|
||||
*buffer++ = cDigitsLut[d5 + 1];
|
||||
*buffer++ = cDigitsLut[d6];
|
||||
*buffer++ = cDigitsLut[d6 + 1];
|
||||
*buffer++ = cDigitsLut[d7];
|
||||
*buffer++ = cDigitsLut[d7 + 1];
|
||||
*buffer++ = cDigitsLut[d8];
|
||||
*buffer++ = cDigitsLut[d8 + 1];
|
||||
}
|
||||
else {
|
||||
const uint32_t a = static_cast<uint32_t>(value / kTen16); // 1 to 1844
|
||||
value %= kTen16;
|
||||
|
||||
if (a < 10)
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
|
||||
else if (a < 100) {
|
||||
const uint32_t i = a << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
}
|
||||
else if (a < 1000) {
|
||||
*buffer++ = static_cast<char>('0' + static_cast<char>(a / 100));
|
||||
|
||||
const uint32_t i = (a % 100) << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
}
|
||||
else {
|
||||
const uint32_t i = (a / 100) << 1;
|
||||
const uint32_t j = (a % 100) << 1;
|
||||
*buffer++ = cDigitsLut[i];
|
||||
*buffer++ = cDigitsLut[i + 1];
|
||||
*buffer++ = cDigitsLut[j];
|
||||
*buffer++ = cDigitsLut[j + 1];
|
||||
}
|
||||
|
||||
const uint32_t v0 = static_cast<uint32_t>(value / kTen8);
|
||||
const uint32_t v1 = static_cast<uint32_t>(value % kTen8);
|
||||
|
||||
const uint32_t b0 = v0 / 10000;
|
||||
const uint32_t c0 = v0 % 10000;
|
||||
|
||||
const uint32_t d1 = (b0 / 100) << 1;
|
||||
const uint32_t d2 = (b0 % 100) << 1;
|
||||
|
||||
const uint32_t d3 = (c0 / 100) << 1;
|
||||
const uint32_t d4 = (c0 % 100) << 1;
|
||||
|
||||
const uint32_t b1 = v1 / 10000;
|
||||
const uint32_t c1 = v1 % 10000;
|
||||
|
||||
const uint32_t d5 = (b1 / 100) << 1;
|
||||
const uint32_t d6 = (b1 % 100) << 1;
|
||||
|
||||
const uint32_t d7 = (c1 / 100) << 1;
|
||||
const uint32_t d8 = (c1 % 100) << 1;
|
||||
|
||||
*buffer++ = cDigitsLut[d1];
|
||||
*buffer++ = cDigitsLut[d1 + 1];
|
||||
*buffer++ = cDigitsLut[d2];
|
||||
*buffer++ = cDigitsLut[d2 + 1];
|
||||
*buffer++ = cDigitsLut[d3];
|
||||
*buffer++ = cDigitsLut[d3 + 1];
|
||||
*buffer++ = cDigitsLut[d4];
|
||||
*buffer++ = cDigitsLut[d4 + 1];
|
||||
*buffer++ = cDigitsLut[d5];
|
||||
*buffer++ = cDigitsLut[d5 + 1];
|
||||
*buffer++ = cDigitsLut[d6];
|
||||
*buffer++ = cDigitsLut[d6 + 1];
|
||||
*buffer++ = cDigitsLut[d7];
|
||||
*buffer++ = cDigitsLut[d7 + 1];
|
||||
*buffer++ = cDigitsLut[d8];
|
||||
*buffer++ = cDigitsLut[d8 + 1];
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline char* i64toa(int64_t value, char* buffer) {
|
||||
RAPIDJSON_ASSERT(buffer != 0);
|
||||
uint64_t u = static_cast<uint64_t>(value);
|
||||
if (value < 0) {
|
||||
*buffer++ = '-';
|
||||
u = ~u + 1;
|
||||
}
|
||||
|
||||
return u64toa(u, buffer);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ITOA_
|
186
include/rapidjson/internal/meta.h
Normal file
186
include/rapidjson/internal/meta.h
Normal file
@ -0,0 +1,186 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_META_H_
|
||||
#define RAPIDJSON_INTERNAL_META_H_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(6334)
|
||||
#endif
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
#include <type_traits>
|
||||
#endif
|
||||
|
||||
//@cond RAPIDJSON_INTERNAL
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
|
||||
template <typename T> struct Void { typedef void Type; };
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// BoolType, TrueType, FalseType
|
||||
//
|
||||
template <bool Cond> struct BoolType {
|
||||
static const bool Value = Cond;
|
||||
typedef BoolType Type;
|
||||
};
|
||||
typedef BoolType<true> TrueType;
|
||||
typedef BoolType<false> FalseType;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr
|
||||
//
|
||||
|
||||
template <bool C> struct SelectIfImpl { template <typename T1, typename T2> struct Apply { typedef T1 Type; }; };
|
||||
template <> struct SelectIfImpl<false> { template <typename T1, typename T2> struct Apply { typedef T2 Type; }; };
|
||||
template <bool C, typename T1, typename T2> struct SelectIfCond : SelectIfImpl<C>::template Apply<T1,T2> {};
|
||||
template <typename C, typename T1, typename T2> struct SelectIf : SelectIfCond<C::Value, T1, T2> {};
|
||||
|
||||
template <bool Cond1, bool Cond2> struct AndExprCond : FalseType {};
|
||||
template <> struct AndExprCond<true, true> : TrueType {};
|
||||
template <bool Cond1, bool Cond2> struct OrExprCond : TrueType {};
|
||||
template <> struct OrExprCond<false, false> : FalseType {};
|
||||
|
||||
template <typename C> struct BoolExpr : SelectIf<C,TrueType,FalseType>::Type {};
|
||||
template <typename C> struct NotExpr : SelectIf<C,FalseType,TrueType>::Type {};
|
||||
template <typename C1, typename C2> struct AndExpr : AndExprCond<C1::Value, C2::Value>::Type {};
|
||||
template <typename C1, typename C2> struct OrExpr : OrExprCond<C1::Value, C2::Value>::Type {};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// AddConst, MaybeAddConst, RemoveConst
|
||||
template <typename T> struct AddConst { typedef const T Type; };
|
||||
template <bool Constify, typename T> struct MaybeAddConst : SelectIfCond<Constify, const T, T> {};
|
||||
template <typename T> struct RemoveConst { typedef T Type; };
|
||||
template <typename T> struct RemoveConst<const T> { typedef T Type; };
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsSame, IsConst, IsMoreConst, IsPointer
|
||||
//
|
||||
template <typename T, typename U> struct IsSame : FalseType {};
|
||||
template <typename T> struct IsSame<T, T> : TrueType {};
|
||||
|
||||
template <typename T> struct IsConst : FalseType {};
|
||||
template <typename T> struct IsConst<const T> : TrueType {};
|
||||
|
||||
template <typename CT, typename T>
|
||||
struct IsMoreConst
|
||||
: AndExpr<IsSame<typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>,
|
||||
BoolType<IsConst<CT>::Value >= IsConst<T>::Value> >::Type {};
|
||||
|
||||
template <typename T> struct IsPointer : FalseType {};
|
||||
template <typename T> struct IsPointer<T*> : TrueType {};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// IsBaseOf
|
||||
//
|
||||
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
|
||||
template <typename B, typename D> struct IsBaseOf
|
||||
: BoolType< ::std::is_base_of<B,D>::value> {};
|
||||
|
||||
#else // simplified version adopted from Boost
|
||||
|
||||
template<typename B, typename D> struct IsBaseOfImpl {
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0);
|
||||
RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0);
|
||||
|
||||
typedef char (&Yes)[1];
|
||||
typedef char (&No) [2];
|
||||
|
||||
template <typename T>
|
||||
static Yes Check(const D*, T);
|
||||
static No Check(const B*, int);
|
||||
|
||||
struct Host {
|
||||
operator const B*() const;
|
||||
operator const D*();
|
||||
};
|
||||
|
||||
enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) };
|
||||
};
|
||||
|
||||
template <typename B, typename D> struct IsBaseOf
|
||||
: OrExpr<IsSame<B, D>, BoolExpr<IsBaseOfImpl<B, D> > >::Type {};
|
||||
|
||||
#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// EnableIf / DisableIf
|
||||
//
|
||||
template <bool Condition, typename T = void> struct EnableIfCond { typedef T Type; };
|
||||
template <typename T> struct EnableIfCond<false, T> { /* empty */ };
|
||||
|
||||
template <bool Condition, typename T = void> struct DisableIfCond { typedef T Type; };
|
||||
template <typename T> struct DisableIfCond<true, T> { /* empty */ };
|
||||
|
||||
template <typename Condition, typename T = void>
|
||||
struct EnableIf : EnableIfCond<Condition::Value, T> {};
|
||||
|
||||
template <typename Condition, typename T = void>
|
||||
struct DisableIf : DisableIfCond<Condition::Value, T> {};
|
||||
|
||||
// SFINAE helpers
|
||||
struct SfinaeTag {};
|
||||
template <typename T> struct RemoveSfinaeTag;
|
||||
template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; };
|
||||
|
||||
#define RAPIDJSON_REMOVEFPTR_(type) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \
|
||||
< ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type
|
||||
|
||||
#define RAPIDJSON_ENABLEIF(cond) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||
|
||||
#define RAPIDJSON_DISABLEIF(cond) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
|
||||
|
||||
#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond), \
|
||||
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
|
||||
|
||||
#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
|
||||
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
|
||||
<RAPIDJSON_REMOVEFPTR_(cond), \
|
||||
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
//@endcond
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_META_H_
|
55
include/rapidjson/internal/pow10.h
Normal file
55
include/rapidjson/internal/pow10.h
Normal file
@ -0,0 +1,55 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_POW10_
|
||||
#define RAPIDJSON_POW10_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
//! Computes integer powers of 10 in double (10.0^n).
|
||||
/*! This function uses lookup table for fast and accurate results.
|
||||
\param n non-negative exponent. Must <= 308.
|
||||
\return 10.0^n
|
||||
*/
|
||||
inline double Pow10(int n) {
|
||||
static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
|
||||
1e+0,
|
||||
1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20,
|
||||
1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
|
||||
1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
|
||||
1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
|
||||
1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100,
|
||||
1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120,
|
||||
1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140,
|
||||
1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160,
|
||||
1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180,
|
||||
1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200,
|
||||
1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220,
|
||||
1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240,
|
||||
1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260,
|
||||
1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280,
|
||||
1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
|
||||
1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
|
||||
};
|
||||
RAPIDJSON_ASSERT(n >= 0 && n <= 308);
|
||||
return e[n];
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_POW10_
|
739
include/rapidjson/internal/regex.h
Normal file
739
include/rapidjson/internal/regex.h
Normal file
@ -0,0 +1,739 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_REGEX_H_
|
||||
#define RAPIDJSON_INTERNAL_REGEX_H_
|
||||
|
||||
#include "../allocators.h"
|
||||
#include "../stream.h"
|
||||
#include "stack.h"
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
RAPIDJSON_DIAG_OFF(switch-enum)
|
||||
#elif defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
#ifndef RAPIDJSON_REGEX_VERBOSE
|
||||
#define RAPIDJSON_REGEX_VERBOSE 0
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// DecodedStream
|
||||
|
||||
template <typename SourceStream, typename Encoding>
|
||||
class DecodedStream {
|
||||
public:
|
||||
DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); }
|
||||
unsigned Peek() { return codepoint_; }
|
||||
unsigned Take() {
|
||||
unsigned c = codepoint_;
|
||||
if (c) // No further decoding when '\0'
|
||||
Decode();
|
||||
return c;
|
||||
}
|
||||
|
||||
private:
|
||||
void Decode() {
|
||||
if (!Encoding::Decode(ss_, &codepoint_))
|
||||
codepoint_ = 0;
|
||||
}
|
||||
|
||||
SourceStream& ss_;
|
||||
unsigned codepoint_;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GenericRegex
|
||||
|
||||
static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1
|
||||
static const SizeType kRegexInvalidRange = ~SizeType(0);
|
||||
|
||||
template <typename Encoding, typename Allocator>
|
||||
class GenericRegexSearch;
|
||||
|
||||
//! Regular expression engine with subset of ECMAscript grammar.
|
||||
/*!
|
||||
Supported regular expression syntax:
|
||||
- \c ab Concatenation
|
||||
- \c a|b Alternation
|
||||
- \c a? Zero or one
|
||||
- \c a* Zero or more
|
||||
- \c a+ One or more
|
||||
- \c a{3} Exactly 3 times
|
||||
- \c a{3,} At least 3 times
|
||||
- \c a{3,5} 3 to 5 times
|
||||
- \c (ab) Grouping
|
||||
- \c ^a At the beginning
|
||||
- \c a$ At the end
|
||||
- \c . Any character
|
||||
- \c [abc] Character classes
|
||||
- \c [a-c] Character class range
|
||||
- \c [a-z0-9_] Character class combination
|
||||
- \c [^abc] Negated character classes
|
||||
- \c [^a-c] Negated character class range
|
||||
- \c [\b] Backspace (U+0008)
|
||||
- \c \\| \\\\ ... Escape characters
|
||||
- \c \\f Form feed (U+000C)
|
||||
- \c \\n Line feed (U+000A)
|
||||
- \c \\r Carriage return (U+000D)
|
||||
- \c \\t Tab (U+0009)
|
||||
- \c \\v Vertical tab (U+000B)
|
||||
|
||||
\note This is a Thompson NFA engine, implemented with reference to
|
||||
Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).",
|
||||
https://swtch.com/~rsc/regexp/regexp1.html
|
||||
*/
|
||||
template <typename Encoding, typename Allocator = CrtAllocator>
|
||||
class GenericRegex {
|
||||
public:
|
||||
typedef Encoding EncodingType;
|
||||
typedef typename Encoding::Ch Ch;
|
||||
template <typename, typename> friend class GenericRegexSearch;
|
||||
|
||||
GenericRegex(const Ch* source, Allocator* allocator = 0) :
|
||||
ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_),
|
||||
states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(),
|
||||
anchorBegin_(), anchorEnd_()
|
||||
{
|
||||
GenericStringStream<Encoding> ss(source);
|
||||
DecodedStream<GenericStringStream<Encoding>, Encoding> ds(ss);
|
||||
Parse(ds);
|
||||
}
|
||||
|
||||
~GenericRegex()
|
||||
{
|
||||
RAPIDJSON_DELETE(ownAllocator_);
|
||||
}
|
||||
|
||||
bool IsValid() const {
|
||||
return root_ != kRegexInvalidState;
|
||||
}
|
||||
|
||||
private:
|
||||
enum Operator {
|
||||
kZeroOrOne,
|
||||
kZeroOrMore,
|
||||
kOneOrMore,
|
||||
kConcatenation,
|
||||
kAlternation,
|
||||
kLeftParenthesis
|
||||
};
|
||||
|
||||
static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.'
|
||||
static const unsigned kRangeCharacterClass = 0xFFFFFFFE;
|
||||
static const unsigned kRangeNegationFlag = 0x80000000;
|
||||
|
||||
struct Range {
|
||||
unsigned start; //
|
||||
unsigned end;
|
||||
SizeType next;
|
||||
};
|
||||
|
||||
struct State {
|
||||
SizeType out; //!< Equals to kInvalid for matching state
|
||||
SizeType out1; //!< Equals to non-kInvalid for split
|
||||
SizeType rangeStart;
|
||||
unsigned codepoint;
|
||||
};
|
||||
|
||||
struct Frag {
|
||||
Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {}
|
||||
SizeType start;
|
||||
SizeType out; //!< link-list of all output states
|
||||
SizeType minIndex;
|
||||
};
|
||||
|
||||
State& GetState(SizeType index) {
|
||||
RAPIDJSON_ASSERT(index < stateCount_);
|
||||
return states_.template Bottom<State>()[index];
|
||||
}
|
||||
|
||||
const State& GetState(SizeType index) const {
|
||||
RAPIDJSON_ASSERT(index < stateCount_);
|
||||
return states_.template Bottom<State>()[index];
|
||||
}
|
||||
|
||||
Range& GetRange(SizeType index) {
|
||||
RAPIDJSON_ASSERT(index < rangeCount_);
|
||||
return ranges_.template Bottom<Range>()[index];
|
||||
}
|
||||
|
||||
const Range& GetRange(SizeType index) const {
|
||||
RAPIDJSON_ASSERT(index < rangeCount_);
|
||||
return ranges_.template Bottom<Range>()[index];
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
void Parse(DecodedStream<InputStream, Encoding>& ds) {
|
||||
Stack<Allocator> operandStack(allocator_, 256); // Frag
|
||||
Stack<Allocator> operatorStack(allocator_, 256); // Operator
|
||||
Stack<Allocator> atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis)
|
||||
|
||||
*atomCountStack.template Push<unsigned>() = 0;
|
||||
|
||||
unsigned codepoint;
|
||||
while (ds.Peek() != 0) {
|
||||
switch (codepoint = ds.Take()) {
|
||||
case '^':
|
||||
anchorBegin_ = true;
|
||||
break;
|
||||
|
||||
case '$':
|
||||
anchorEnd_ = true;
|
||||
break;
|
||||
|
||||
case '|':
|
||||
while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() < kAlternation)
|
||||
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
|
||||
return;
|
||||
*operatorStack.template Push<Operator>() = kAlternation;
|
||||
*atomCountStack.template Top<unsigned>() = 0;
|
||||
break;
|
||||
|
||||
case '(':
|
||||
*operatorStack.template Push<Operator>() = kLeftParenthesis;
|
||||
*atomCountStack.template Push<unsigned>() = 0;
|
||||
break;
|
||||
|
||||
case ')':
|
||||
while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis)
|
||||
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
|
||||
return;
|
||||
if (operatorStack.Empty())
|
||||
return;
|
||||
operatorStack.template Pop<Operator>(1);
|
||||
atomCountStack.template Pop<unsigned>(1);
|
||||
ImplicitConcatenation(atomCountStack, operatorStack);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
if (!Eval(operandStack, kZeroOrOne))
|
||||
return;
|
||||
break;
|
||||
|
||||
case '*':
|
||||
if (!Eval(operandStack, kZeroOrMore))
|
||||
return;
|
||||
break;
|
||||
|
||||
case '+':
|
||||
if (!Eval(operandStack, kOneOrMore))
|
||||
return;
|
||||
break;
|
||||
|
||||
case '{':
|
||||
{
|
||||
unsigned n, m;
|
||||
if (!ParseUnsigned(ds, &n))
|
||||
return;
|
||||
|
||||
if (ds.Peek() == ',') {
|
||||
ds.Take();
|
||||
if (ds.Peek() == '}')
|
||||
m = kInfinityQuantifier;
|
||||
else if (!ParseUnsigned(ds, &m) || m < n)
|
||||
return;
|
||||
}
|
||||
else
|
||||
m = n;
|
||||
|
||||
if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}')
|
||||
return;
|
||||
ds.Take();
|
||||
}
|
||||
break;
|
||||
|
||||
case '.':
|
||||
PushOperand(operandStack, kAnyCharacterClass);
|
||||
ImplicitConcatenation(atomCountStack, operatorStack);
|
||||
break;
|
||||
|
||||
case '[':
|
||||
{
|
||||
SizeType range;
|
||||
if (!ParseRange(ds, &range))
|
||||
return;
|
||||
SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass);
|
||||
GetState(s).rangeStart = range;
|
||||
*operandStack.template Push<Frag>() = Frag(s, s, s);
|
||||
}
|
||||
ImplicitConcatenation(atomCountStack, operatorStack);
|
||||
break;
|
||||
|
||||
case '\\': // Escape character
|
||||
if (!CharacterEscape(ds, &codepoint))
|
||||
return; // Unsupported escape character
|
||||
// fall through to default
|
||||
RAPIDJSON_DELIBERATE_FALLTHROUGH;
|
||||
|
||||
default: // Pattern character
|
||||
PushOperand(operandStack, codepoint);
|
||||
ImplicitConcatenation(atomCountStack, operatorStack);
|
||||
}
|
||||
}
|
||||
|
||||
while (!operatorStack.Empty())
|
||||
if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
|
||||
return;
|
||||
|
||||
// Link the operand to matching state.
|
||||
if (operandStack.GetSize() == sizeof(Frag)) {
|
||||
Frag* e = operandStack.template Pop<Frag>(1);
|
||||
Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0));
|
||||
root_ = e->start;
|
||||
|
||||
#if RAPIDJSON_REGEX_VERBOSE
|
||||
printf("root: %d\n", root_);
|
||||
for (SizeType i = 0; i < stateCount_ ; i++) {
|
||||
State& s = GetState(i);
|
||||
printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) {
|
||||
State* s = states_.template Push<State>();
|
||||
s->out = out;
|
||||
s->out1 = out1;
|
||||
s->codepoint = codepoint;
|
||||
s->rangeStart = kRegexInvalidRange;
|
||||
return stateCount_++;
|
||||
}
|
||||
|
||||
void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) {
|
||||
SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint);
|
||||
*operandStack.template Push<Frag>() = Frag(s, s, s);
|
||||
}
|
||||
|
||||
void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) {
|
||||
if (*atomCountStack.template Top<unsigned>())
|
||||
*operatorStack.template Push<Operator>() = kConcatenation;
|
||||
(*atomCountStack.template Top<unsigned>())++;
|
||||
}
|
||||
|
||||
SizeType Append(SizeType l1, SizeType l2) {
|
||||
SizeType old = l1;
|
||||
while (GetState(l1).out != kRegexInvalidState)
|
||||
l1 = GetState(l1).out;
|
||||
GetState(l1).out = l2;
|
||||
return old;
|
||||
}
|
||||
|
||||
void Patch(SizeType l, SizeType s) {
|
||||
for (SizeType next; l != kRegexInvalidState; l = next) {
|
||||
next = GetState(l).out;
|
||||
GetState(l).out = s;
|
||||
}
|
||||
}
|
||||
|
||||
bool Eval(Stack<Allocator>& operandStack, Operator op) {
|
||||
switch (op) {
|
||||
case kConcatenation:
|
||||
RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2);
|
||||
{
|
||||
Frag e2 = *operandStack.template Pop<Frag>(1);
|
||||
Frag e1 = *operandStack.template Pop<Frag>(1);
|
||||
Patch(e1.out, e2.start);
|
||||
*operandStack.template Push<Frag>() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex));
|
||||
}
|
||||
return true;
|
||||
|
||||
case kAlternation:
|
||||
if (operandStack.GetSize() >= sizeof(Frag) * 2) {
|
||||
Frag e2 = *operandStack.template Pop<Frag>(1);
|
||||
Frag e1 = *operandStack.template Pop<Frag>(1);
|
||||
SizeType s = NewState(e1.start, e2.start, 0);
|
||||
*operandStack.template Push<Frag>() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case kZeroOrOne:
|
||||
if (operandStack.GetSize() >= sizeof(Frag)) {
|
||||
Frag e = *operandStack.template Pop<Frag>(1);
|
||||
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
||||
*operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case kZeroOrMore:
|
||||
if (operandStack.GetSize() >= sizeof(Frag)) {
|
||||
Frag e = *operandStack.template Pop<Frag>(1);
|
||||
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
||||
Patch(e.out, s);
|
||||
*operandStack.template Push<Frag>() = Frag(s, s, e.minIndex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case kOneOrMore:
|
||||
if (operandStack.GetSize() >= sizeof(Frag)) {
|
||||
Frag e = *operandStack.template Pop<Frag>(1);
|
||||
SizeType s = NewState(kRegexInvalidState, e.start, 0);
|
||||
Patch(e.out, s);
|
||||
*operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
// syntax error (e.g. unclosed kLeftParenthesis)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EvalQuantifier(Stack<Allocator>& operandStack, unsigned n, unsigned m) {
|
||||
RAPIDJSON_ASSERT(n <= m);
|
||||
RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag));
|
||||
|
||||
if (n == 0) {
|
||||
if (m == 0) // a{0} not support
|
||||
return false;
|
||||
else if (m == kInfinityQuantifier)
|
||||
Eval(operandStack, kZeroOrMore); // a{0,} -> a*
|
||||
else {
|
||||
Eval(operandStack, kZeroOrOne); // a{0,5} -> a?
|
||||
for (unsigned i = 0; i < m - 1; i++)
|
||||
CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a?
|
||||
for (unsigned i = 0; i < m - 1; i++)
|
||||
Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a?
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a
|
||||
CloneTopOperand(operandStack);
|
||||
|
||||
if (m == kInfinityQuantifier)
|
||||
Eval(operandStack, kOneOrMore); // a{3,} -> a a a+
|
||||
else if (m > n) {
|
||||
CloneTopOperand(operandStack); // a{3,5} -> a a a a
|
||||
Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a?
|
||||
for (unsigned i = n; i < m - 1; i++)
|
||||
CloneTopOperand(operandStack); // a{3,5} -> a a a a? a?
|
||||
for (unsigned i = n; i < m; i++)
|
||||
Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a?
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < n - 1; i++)
|
||||
Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a?
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; }
|
||||
|
||||
void CloneTopOperand(Stack<Allocator>& operandStack) {
|
||||
const Frag src = *operandStack.template Top<Frag>(); // Copy constructor to prevent invalidation
|
||||
SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_)
|
||||
State* s = states_.template Push<State>(count);
|
||||
memcpy(s, &GetState(src.minIndex), count * sizeof(State));
|
||||
for (SizeType j = 0; j < count; j++) {
|
||||
if (s[j].out != kRegexInvalidState)
|
||||
s[j].out += count;
|
||||
if (s[j].out1 != kRegexInvalidState)
|
||||
s[j].out1 += count;
|
||||
}
|
||||
*operandStack.template Push<Frag>() = Frag(src.start + count, src.out + count, src.minIndex + count);
|
||||
stateCount_ += count;
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool ParseUnsigned(DecodedStream<InputStream, Encoding>& ds, unsigned* u) {
|
||||
unsigned r = 0;
|
||||
if (ds.Peek() < '0' || ds.Peek() > '9')
|
||||
return false;
|
||||
while (ds.Peek() >= '0' && ds.Peek() <= '9') {
|
||||
if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295
|
||||
return false; // overflow
|
||||
r = r * 10 + (ds.Take() - '0');
|
||||
}
|
||||
*u = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool ParseRange(DecodedStream<InputStream, Encoding>& ds, SizeType* range) {
|
||||
bool isBegin = true;
|
||||
bool negate = false;
|
||||
int step = 0;
|
||||
SizeType start = kRegexInvalidRange;
|
||||
SizeType current = kRegexInvalidRange;
|
||||
unsigned codepoint;
|
||||
while ((codepoint = ds.Take()) != 0) {
|
||||
if (isBegin) {
|
||||
isBegin = false;
|
||||
if (codepoint == '^') {
|
||||
negate = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
switch (codepoint) {
|
||||
case ']':
|
||||
if (start == kRegexInvalidRange)
|
||||
return false; // Error: nothing inside []
|
||||
if (step == 2) { // Add trailing '-'
|
||||
SizeType r = NewRange('-');
|
||||
RAPIDJSON_ASSERT(current != kRegexInvalidRange);
|
||||
GetRange(current).next = r;
|
||||
}
|
||||
if (negate)
|
||||
GetRange(start).start |= kRangeNegationFlag;
|
||||
*range = start;
|
||||
return true;
|
||||
|
||||
case '\\':
|
||||
if (ds.Peek() == 'b') {
|
||||
ds.Take();
|
||||
codepoint = 0x0008; // Escape backspace character
|
||||
}
|
||||
else if (!CharacterEscape(ds, &codepoint))
|
||||
return false;
|
||||
// fall through to default
|
||||
RAPIDJSON_DELIBERATE_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
switch (step) {
|
||||
case 1:
|
||||
if (codepoint == '-') {
|
||||
step++;
|
||||
break;
|
||||
}
|
||||
// fall through to step 0 for other characters
|
||||
RAPIDJSON_DELIBERATE_FALLTHROUGH;
|
||||
|
||||
case 0:
|
||||
{
|
||||
SizeType r = NewRange(codepoint);
|
||||
if (current != kRegexInvalidRange)
|
||||
GetRange(current).next = r;
|
||||
if (start == kRegexInvalidRange)
|
||||
start = r;
|
||||
current = r;
|
||||
}
|
||||
step = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
RAPIDJSON_ASSERT(step == 2);
|
||||
GetRange(current).end = codepoint;
|
||||
step = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SizeType NewRange(unsigned codepoint) {
|
||||
Range* r = ranges_.template Push<Range>();
|
||||
r->start = r->end = codepoint;
|
||||
r->next = kRegexInvalidRange;
|
||||
return rangeCount_++;
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool CharacterEscape(DecodedStream<InputStream, Encoding>& ds, unsigned* escapedCodepoint) {
|
||||
unsigned codepoint;
|
||||
switch (codepoint = ds.Take()) {
|
||||
case '^':
|
||||
case '$':
|
||||
case '|':
|
||||
case '(':
|
||||
case ')':
|
||||
case '?':
|
||||
case '*':
|
||||
case '+':
|
||||
case '.':
|
||||
case '[':
|
||||
case ']':
|
||||
case '{':
|
||||
case '}':
|
||||
case '\\':
|
||||
*escapedCodepoint = codepoint; return true;
|
||||
case 'f': *escapedCodepoint = 0x000C; return true;
|
||||
case 'n': *escapedCodepoint = 0x000A; return true;
|
||||
case 'r': *escapedCodepoint = 0x000D; return true;
|
||||
case 't': *escapedCodepoint = 0x0009; return true;
|
||||
case 'v': *escapedCodepoint = 0x000B; return true;
|
||||
default:
|
||||
return false; // Unsupported escape character
|
||||
}
|
||||
}
|
||||
|
||||
Allocator* ownAllocator_;
|
||||
Allocator* allocator_;
|
||||
Stack<Allocator> states_;
|
||||
Stack<Allocator> ranges_;
|
||||
SizeType root_;
|
||||
SizeType stateCount_;
|
||||
SizeType rangeCount_;
|
||||
|
||||
static const unsigned kInfinityQuantifier = ~0u;
|
||||
|
||||
// For SearchWithAnchoring()
|
||||
bool anchorBegin_;
|
||||
bool anchorEnd_;
|
||||
};
|
||||
|
||||
template <typename RegexType, typename Allocator = CrtAllocator>
|
||||
class GenericRegexSearch {
|
||||
public:
|
||||
typedef typename RegexType::EncodingType Encoding;
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) :
|
||||
regex_(regex), allocator_(allocator), ownAllocator_(0),
|
||||
state0_(allocator, 0), state1_(allocator, 0), stateSet_()
|
||||
{
|
||||
RAPIDJSON_ASSERT(regex_.IsValid());
|
||||
if (!allocator_)
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
||||
stateSet_ = static_cast<unsigned*>(allocator_->Malloc(GetStateSetSize()));
|
||||
state0_.template Reserve<SizeType>(regex_.stateCount_);
|
||||
state1_.template Reserve<SizeType>(regex_.stateCount_);
|
||||
}
|
||||
|
||||
~GenericRegexSearch() {
|
||||
Allocator::Free(stateSet_);
|
||||
RAPIDJSON_DELETE(ownAllocator_);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool Match(InputStream& is) {
|
||||
return SearchWithAnchoring(is, true, true);
|
||||
}
|
||||
|
||||
bool Match(const Ch* s) {
|
||||
GenericStringStream<Encoding> is(s);
|
||||
return Match(is);
|
||||
}
|
||||
|
||||
template <typename InputStream>
|
||||
bool Search(InputStream& is) {
|
||||
return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_);
|
||||
}
|
||||
|
||||
bool Search(const Ch* s) {
|
||||
GenericStringStream<Encoding> is(s);
|
||||
return Search(is);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef typename RegexType::State State;
|
||||
typedef typename RegexType::Range Range;
|
||||
|
||||
template <typename InputStream>
|
||||
bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) {
|
||||
DecodedStream<InputStream, Encoding> ds(is);
|
||||
|
||||
state0_.Clear();
|
||||
Stack<Allocator> *current = &state0_, *next = &state1_;
|
||||
const size_t stateSetSize = GetStateSetSize();
|
||||
std::memset(stateSet_, 0, stateSetSize);
|
||||
|
||||
bool matched = AddState(*current, regex_.root_);
|
||||
unsigned codepoint;
|
||||
while (!current->Empty() && (codepoint = ds.Take()) != 0) {
|
||||
std::memset(stateSet_, 0, stateSetSize);
|
||||
next->Clear();
|
||||
matched = false;
|
||||
for (const SizeType* s = current->template Bottom<SizeType>(); s != current->template End<SizeType>(); ++s) {
|
||||
const State& sr = regex_.GetState(*s);
|
||||
if (sr.codepoint == codepoint ||
|
||||
sr.codepoint == RegexType::kAnyCharacterClass ||
|
||||
(sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint)))
|
||||
{
|
||||
matched = AddState(*next, sr.out) || matched;
|
||||
if (!anchorEnd && matched)
|
||||
return true;
|
||||
}
|
||||
if (!anchorBegin)
|
||||
AddState(*next, regex_.root_);
|
||||
}
|
||||
internal::Swap(current, next);
|
||||
}
|
||||
|
||||
return matched;
|
||||
}
|
||||
|
||||
size_t GetStateSetSize() const {
|
||||
return (regex_.stateCount_ + 31) / 32 * 4;
|
||||
}
|
||||
|
||||
// Return whether the added states is a match state
|
||||
bool AddState(Stack<Allocator>& l, SizeType index) {
|
||||
RAPIDJSON_ASSERT(index != kRegexInvalidState);
|
||||
|
||||
const State& s = regex_.GetState(index);
|
||||
if (s.out1 != kRegexInvalidState) { // Split
|
||||
bool matched = AddState(l, s.out);
|
||||
return AddState(l, s.out1) || matched;
|
||||
}
|
||||
else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) {
|
||||
stateSet_[index >> 5] |= (1u << (index & 31));
|
||||
*l.template PushUnsafe<SizeType>() = index;
|
||||
}
|
||||
return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation.
|
||||
}
|
||||
|
||||
bool MatchRange(SizeType rangeIndex, unsigned codepoint) const {
|
||||
bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0;
|
||||
while (rangeIndex != kRegexInvalidRange) {
|
||||
const Range& r = regex_.GetRange(rangeIndex);
|
||||
if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end)
|
||||
return yes;
|
||||
rangeIndex = r.next;
|
||||
}
|
||||
return !yes;
|
||||
}
|
||||
|
||||
const RegexType& regex_;
|
||||
Allocator* allocator_;
|
||||
Allocator* ownAllocator_;
|
||||
Stack<Allocator> state0_;
|
||||
Stack<Allocator> state1_;
|
||||
uint32_t* stateSet_;
|
||||
};
|
||||
|
||||
typedef GenericRegex<UTF8<> > Regex;
|
||||
typedef GenericRegexSearch<Regex> RegexSearch;
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_REGEX_H_
|
232
include/rapidjson/internal/stack.h
Normal file
232
include/rapidjson/internal/stack.h
Normal file
@ -0,0 +1,232 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_STACK_H_
|
||||
#define RAPIDJSON_INTERNAL_STACK_H_
|
||||
|
||||
#include "../allocators.h"
|
||||
#include "swap.h"
|
||||
#include <cstddef>
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Stack
|
||||
|
||||
//! A type-unsafe stack for storing different types of data.
|
||||
/*! \tparam Allocator Allocator for allocating stack memory.
|
||||
*/
|
||||
template <typename Allocator>
|
||||
class Stack {
|
||||
public:
|
||||
// Optimization note: Do not allocate memory for stack_ in constructor.
|
||||
// Do it lazily when first Push() -> Expand() -> Resize().
|
||||
Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
Stack(Stack&& rhs)
|
||||
: allocator_(rhs.allocator_),
|
||||
ownAllocator_(rhs.ownAllocator_),
|
||||
stack_(rhs.stack_),
|
||||
stackTop_(rhs.stackTop_),
|
||||
stackEnd_(rhs.stackEnd_),
|
||||
initialCapacity_(rhs.initialCapacity_)
|
||||
{
|
||||
rhs.allocator_ = 0;
|
||||
rhs.ownAllocator_ = 0;
|
||||
rhs.stack_ = 0;
|
||||
rhs.stackTop_ = 0;
|
||||
rhs.stackEnd_ = 0;
|
||||
rhs.initialCapacity_ = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
~Stack() {
|
||||
Destroy();
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
Stack& operator=(Stack&& rhs) {
|
||||
if (&rhs != this)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
allocator_ = rhs.allocator_;
|
||||
ownAllocator_ = rhs.ownAllocator_;
|
||||
stack_ = rhs.stack_;
|
||||
stackTop_ = rhs.stackTop_;
|
||||
stackEnd_ = rhs.stackEnd_;
|
||||
initialCapacity_ = rhs.initialCapacity_;
|
||||
|
||||
rhs.allocator_ = 0;
|
||||
rhs.ownAllocator_ = 0;
|
||||
rhs.stack_ = 0;
|
||||
rhs.stackTop_ = 0;
|
||||
rhs.stackEnd_ = 0;
|
||||
rhs.initialCapacity_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT {
|
||||
internal::Swap(allocator_, rhs.allocator_);
|
||||
internal::Swap(ownAllocator_, rhs.ownAllocator_);
|
||||
internal::Swap(stack_, rhs.stack_);
|
||||
internal::Swap(stackTop_, rhs.stackTop_);
|
||||
internal::Swap(stackEnd_, rhs.stackEnd_);
|
||||
internal::Swap(initialCapacity_, rhs.initialCapacity_);
|
||||
}
|
||||
|
||||
void Clear() { stackTop_ = stack_; }
|
||||
|
||||
void ShrinkToFit() {
|
||||
if (Empty()) {
|
||||
// If the stack is empty, completely deallocate the memory.
|
||||
Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc)
|
||||
stack_ = 0;
|
||||
stackTop_ = 0;
|
||||
stackEnd_ = 0;
|
||||
}
|
||||
else
|
||||
Resize(GetSize());
|
||||
}
|
||||
|
||||
// Optimization note: try to minimize the size of this function for force inline.
|
||||
// Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
|
||||
template<typename T>
|
||||
RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) {
|
||||
// Expand the stack if needed
|
||||
if (RAPIDJSON_UNLIKELY(static_cast<std::ptrdiff_t>(sizeof(T) * count) > (stackEnd_ - stackTop_)))
|
||||
Expand<T>(count);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
|
||||
Reserve<T>(count);
|
||||
return PushUnsafe<T>(count);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) {
|
||||
RAPIDJSON_ASSERT(stackTop_);
|
||||
RAPIDJSON_ASSERT(static_cast<std::ptrdiff_t>(sizeof(T) * count) <= (stackEnd_ - stackTop_));
|
||||
T* ret = reinterpret_cast<T*>(stackTop_);
|
||||
stackTop_ += sizeof(T) * count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Pop(size_t count) {
|
||||
RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
|
||||
stackTop_ -= count * sizeof(T);
|
||||
return reinterpret_cast<T*>(stackTop_);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Top() {
|
||||
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
|
||||
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T* Top() const {
|
||||
RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
|
||||
return reinterpret_cast<T*>(stackTop_ - sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* End() { return reinterpret_cast<T*>(stackTop_); }
|
||||
|
||||
template<typename T>
|
||||
const T* End() const { return reinterpret_cast<T*>(stackTop_); }
|
||||
|
||||
template<typename T>
|
||||
T* Bottom() { return reinterpret_cast<T*>(stack_); }
|
||||
|
||||
template<typename T>
|
||||
const T* Bottom() const { return reinterpret_cast<T*>(stack_); }
|
||||
|
||||
bool HasAllocator() const {
|
||||
return allocator_ != 0;
|
||||
}
|
||||
|
||||
Allocator& GetAllocator() {
|
||||
RAPIDJSON_ASSERT(allocator_);
|
||||
return *allocator_;
|
||||
}
|
||||
|
||||
bool Empty() const { return stackTop_ == stack_; }
|
||||
size_t GetSize() const { return static_cast<size_t>(stackTop_ - stack_); }
|
||||
size_t GetCapacity() const { return static_cast<size_t>(stackEnd_ - stack_); }
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
void Expand(size_t count) {
|
||||
// Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity.
|
||||
size_t newCapacity;
|
||||
if (stack_ == 0) {
|
||||
if (!allocator_)
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
||||
newCapacity = initialCapacity_;
|
||||
} else {
|
||||
newCapacity = GetCapacity();
|
||||
newCapacity += (newCapacity + 1) / 2;
|
||||
}
|
||||
size_t newSize = GetSize() + sizeof(T) * count;
|
||||
if (newCapacity < newSize)
|
||||
newCapacity = newSize;
|
||||
|
||||
Resize(newCapacity);
|
||||
}
|
||||
|
||||
void Resize(size_t newCapacity) {
|
||||
const size_t size = GetSize(); // Backup the current size
|
||||
stack_ = static_cast<char*>(allocator_->Realloc(stack_, GetCapacity(), newCapacity));
|
||||
stackTop_ = stack_ + size;
|
||||
stackEnd_ = stack_ + newCapacity;
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
Allocator::Free(stack_);
|
||||
RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack
|
||||
}
|
||||
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
Stack(const Stack&);
|
||||
Stack& operator=(const Stack&);
|
||||
|
||||
Allocator* allocator_;
|
||||
Allocator* ownAllocator_;
|
||||
char *stack_;
|
||||
char *stackTop_;
|
||||
char *stackEnd_;
|
||||
size_t initialCapacity_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_STACK_H_
|
83
include/rapidjson/internal/strfunc.h
Normal file
83
include/rapidjson/internal/strfunc.h
Normal file
@ -0,0 +1,83 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
#define RAPIDJSON_INTERNAL_STRFUNC_H_
|
||||
|
||||
#include "../stream.h"
|
||||
#include <cwchar>
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
//! Custom strlen() which works on different character types.
|
||||
/*! \tparam Ch Character type (e.g. char, wchar_t, short)
|
||||
\param s Null-terminated input string.
|
||||
\return Number of characters in the string.
|
||||
\note This has the same semantics as strlen(), the return value is not number of Unicode codepoints.
|
||||
*/
|
||||
template <typename Ch>
|
||||
inline SizeType StrLen(const Ch* s) {
|
||||
RAPIDJSON_ASSERT(s != 0);
|
||||
const Ch* p = s;
|
||||
while (*p) ++p;
|
||||
return SizeType(p - s);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline SizeType StrLen(const char* s) {
|
||||
return SizeType(std::strlen(s));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline SizeType StrLen(const wchar_t* s) {
|
||||
return SizeType(std::wcslen(s));
|
||||
}
|
||||
|
||||
//! Custom strcmpn() which works on different character types.
|
||||
/*! \tparam Ch Character type (e.g. char, wchar_t, short)
|
||||
\param s1 Null-terminated input string.
|
||||
\param s2 Null-terminated input string.
|
||||
\return 0 if equal
|
||||
*/
|
||||
template<typename Ch>
|
||||
inline int StrCmp(const Ch* s1, const Ch* s2) {
|
||||
RAPIDJSON_ASSERT(s1 != 0);
|
||||
RAPIDJSON_ASSERT(s2 != 0);
|
||||
while(*s1 && (*s1 == *s2)) { s1++; s2++; }
|
||||
return static_cast<unsigned>(*s1) < static_cast<unsigned>(*s2) ? -1 : static_cast<unsigned>(*s1) > static_cast<unsigned>(*s2);
|
||||
}
|
||||
|
||||
//! Returns number of code points in a encoded string.
|
||||
template<typename Encoding>
|
||||
bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) {
|
||||
RAPIDJSON_ASSERT(s != 0);
|
||||
RAPIDJSON_ASSERT(outCount != 0);
|
||||
GenericStringStream<Encoding> is(s);
|
||||
const typename Encoding::Ch* end = s + length;
|
||||
SizeType count = 0;
|
||||
while (is.src_ < end) {
|
||||
unsigned codepoint;
|
||||
if (!Encoding::Decode(is, &codepoint))
|
||||
return false;
|
||||
count++;
|
||||
}
|
||||
*outCount = count;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_STRFUNC_H_
|
293
include/rapidjson/internal/strtod.h
Normal file
293
include/rapidjson/internal/strtod.h
Normal file
@ -0,0 +1,293 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_STRTOD_
|
||||
#define RAPIDJSON_STRTOD_
|
||||
|
||||
#include "ieee754.h"
|
||||
#include "biginteger.h"
|
||||
#include "diyfp.h"
|
||||
#include "pow10.h"
|
||||
#include <climits>
|
||||
#include <limits>
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
inline double FastPath(double significand, int exp) {
|
||||
if (exp < -308)
|
||||
return 0.0;
|
||||
else if (exp >= 0)
|
||||
return significand * internal::Pow10(exp);
|
||||
else
|
||||
return significand / internal::Pow10(-exp);
|
||||
}
|
||||
|
||||
inline double StrtodNormalPrecision(double d, int p) {
|
||||
if (p < -308) {
|
||||
// Prevent expSum < -308, making Pow10(p) = 0
|
||||
d = FastPath(d, -308);
|
||||
d = FastPath(d, p + 308);
|
||||
}
|
||||
else
|
||||
d = FastPath(d, p);
|
||||
return d;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T Min3(T a, T b, T c) {
|
||||
T m = a;
|
||||
if (m > b) m = b;
|
||||
if (m > c) m = c;
|
||||
return m;
|
||||
}
|
||||
|
||||
inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) {
|
||||
const Double db(b);
|
||||
const uint64_t bInt = db.IntegerSignificand();
|
||||
const int bExp = db.IntegerExponent();
|
||||
const int hExp = bExp - 1;
|
||||
|
||||
int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0;
|
||||
|
||||
// Adjust for decimal exponent
|
||||
if (dExp >= 0) {
|
||||
dS_Exp2 += dExp;
|
||||
dS_Exp5 += dExp;
|
||||
}
|
||||
else {
|
||||
bS_Exp2 -= dExp;
|
||||
bS_Exp5 -= dExp;
|
||||
hS_Exp2 -= dExp;
|
||||
hS_Exp5 -= dExp;
|
||||
}
|
||||
|
||||
// Adjust for binary exponent
|
||||
if (bExp >= 0)
|
||||
bS_Exp2 += bExp;
|
||||
else {
|
||||
dS_Exp2 -= bExp;
|
||||
hS_Exp2 -= bExp;
|
||||
}
|
||||
|
||||
// Adjust for half ulp exponent
|
||||
if (hExp >= 0)
|
||||
hS_Exp2 += hExp;
|
||||
else {
|
||||
dS_Exp2 -= hExp;
|
||||
bS_Exp2 -= hExp;
|
||||
}
|
||||
|
||||
// Remove common power of two factor from all three scaled values
|
||||
int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2);
|
||||
dS_Exp2 -= common_Exp2;
|
||||
bS_Exp2 -= common_Exp2;
|
||||
hS_Exp2 -= common_Exp2;
|
||||
|
||||
BigInteger dS = d;
|
||||
dS.MultiplyPow5(static_cast<unsigned>(dS_Exp5)) <<= static_cast<unsigned>(dS_Exp2);
|
||||
|
||||
BigInteger bS(bInt);
|
||||
bS.MultiplyPow5(static_cast<unsigned>(bS_Exp5)) <<= static_cast<unsigned>(bS_Exp2);
|
||||
|
||||
BigInteger hS(1);
|
||||
hS.MultiplyPow5(static_cast<unsigned>(hS_Exp5)) <<= static_cast<unsigned>(hS_Exp2);
|
||||
|
||||
BigInteger delta(0);
|
||||
dS.Difference(bS, &delta);
|
||||
|
||||
return delta.Compare(hS);
|
||||
}
|
||||
|
||||
inline bool StrtodFast(double d, int p, double* result) {
|
||||
// Use fast path for string-to-double conversion if possible
|
||||
// see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
|
||||
if (p > 22 && p < 22 + 16) {
|
||||
// Fast Path Cases In Disguise
|
||||
d *= internal::Pow10(p - 22);
|
||||
p = 22;
|
||||
}
|
||||
|
||||
if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1
|
||||
*result = FastPath(d, p);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute an approximation and see if it is within 1/2 ULP
|
||||
template<typename Ch>
|
||||
inline bool StrtodDiyFp(const Ch* decimals, int dLen, int dExp, double* result) {
|
||||
uint64_t significand = 0;
|
||||
int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999
|
||||
for (; i < dLen; i++) {
|
||||
if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) ||
|
||||
(significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] > Ch('5')))
|
||||
break;
|
||||
significand = significand * 10u + static_cast<unsigned>(decimals[i] - Ch('0'));
|
||||
}
|
||||
|
||||
if (i < dLen && decimals[i] >= Ch('5')) // Rounding
|
||||
significand++;
|
||||
|
||||
int remaining = dLen - i;
|
||||
const int kUlpShift = 3;
|
||||
const int kUlp = 1 << kUlpShift;
|
||||
int64_t error = (remaining == 0) ? 0 : kUlp / 2;
|
||||
|
||||
DiyFp v(significand, 0);
|
||||
v = v.Normalize();
|
||||
error <<= -v.e;
|
||||
|
||||
dExp += remaining;
|
||||
|
||||
int actualExp;
|
||||
DiyFp cachedPower = GetCachedPower10(dExp, &actualExp);
|
||||
if (actualExp != dExp) {
|
||||
static const DiyFp kPow10[] = {
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6
|
||||
DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7
|
||||
};
|
||||
int adjustment = dExp - actualExp;
|
||||
RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8);
|
||||
v = v * kPow10[adjustment - 1];
|
||||
if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit
|
||||
error += kUlp / 2;
|
||||
}
|
||||
|
||||
v = v * cachedPower;
|
||||
|
||||
error += kUlp + (error == 0 ? 0 : 1);
|
||||
|
||||
const int oldExp = v.e;
|
||||
v = v.Normalize();
|
||||
error <<= oldExp - v.e;
|
||||
|
||||
const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e);
|
||||
int precisionSize = 64 - effectiveSignificandSize;
|
||||
if (precisionSize + kUlpShift >= 64) {
|
||||
int scaleExp = (precisionSize + kUlpShift) - 63;
|
||||
v.f >>= scaleExp;
|
||||
v.e += scaleExp;
|
||||
error = (error >> scaleExp) + 1 + kUlp;
|
||||
precisionSize -= scaleExp;
|
||||
}
|
||||
|
||||
DiyFp rounded(v.f >> precisionSize, v.e + precisionSize);
|
||||
const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp;
|
||||
const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp;
|
||||
if (precisionBits >= halfWay + static_cast<unsigned>(error)) {
|
||||
rounded.f++;
|
||||
if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340)
|
||||
rounded.f >>= 1;
|
||||
rounded.e++;
|
||||
}
|
||||
}
|
||||
|
||||
*result = rounded.ToDouble();
|
||||
|
||||
return halfWay - static_cast<unsigned>(error) >= precisionBits || precisionBits >= halfWay + static_cast<unsigned>(error);
|
||||
}
|
||||
|
||||
template<typename Ch>
|
||||
inline double StrtodBigInteger(double approx, const Ch* decimals, int dLen, int dExp) {
|
||||
RAPIDJSON_ASSERT(dLen >= 0);
|
||||
const BigInteger dInt(decimals, static_cast<unsigned>(dLen));
|
||||
Double a(approx);
|
||||
int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp);
|
||||
if (cmp < 0)
|
||||
return a.Value(); // within half ULP
|
||||
else if (cmp == 0) {
|
||||
// Round towards even
|
||||
if (a.Significand() & 1)
|
||||
return a.NextPositiveDouble();
|
||||
else
|
||||
return a.Value();
|
||||
}
|
||||
else // adjustment
|
||||
return a.NextPositiveDouble();
|
||||
}
|
||||
|
||||
template<typename Ch>
|
||||
inline double StrtodFullPrecision(double d, int p, const Ch* decimals, size_t length, size_t decimalPosition, int exp) {
|
||||
RAPIDJSON_ASSERT(d >= 0.0);
|
||||
RAPIDJSON_ASSERT(length >= 1);
|
||||
|
||||
double result = 0.0;
|
||||
if (StrtodFast(d, p, &result))
|
||||
return result;
|
||||
|
||||
RAPIDJSON_ASSERT(length <= INT_MAX);
|
||||
int dLen = static_cast<int>(length);
|
||||
|
||||
RAPIDJSON_ASSERT(length >= decimalPosition);
|
||||
RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX);
|
||||
int dExpAdjust = static_cast<int>(length - decimalPosition);
|
||||
|
||||
RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust);
|
||||
int dExp = exp - dExpAdjust;
|
||||
|
||||
// Make sure length+dExp does not overflow
|
||||
RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen);
|
||||
|
||||
// Trim leading zeros
|
||||
while (dLen > 0 && *decimals == '0') {
|
||||
dLen--;
|
||||
decimals++;
|
||||
}
|
||||
|
||||
// Trim trailing zeros
|
||||
while (dLen > 0 && decimals[dLen - 1] == '0') {
|
||||
dLen--;
|
||||
dExp++;
|
||||
}
|
||||
|
||||
if (dLen == 0) { // Buffer only contains zeros.
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Trim right-most digits
|
||||
const int kMaxDecimalDigit = 767 + 1;
|
||||
if (dLen > kMaxDecimalDigit) {
|
||||
dExp += dLen - kMaxDecimalDigit;
|
||||
dLen = kMaxDecimalDigit;
|
||||
}
|
||||
|
||||
// If too small, underflow to zero.
|
||||
// Any x <= 10^-324 is interpreted as zero.
|
||||
if (dLen + dExp <= -324)
|
||||
return 0.0;
|
||||
|
||||
// If too large, overflow to infinity.
|
||||
// Any x >= 10^309 is interpreted as +infinity.
|
||||
if (dLen + dExp > 309)
|
||||
return std::numeric_limits<double>::infinity();
|
||||
|
||||
if (StrtodDiyFp(decimals, dLen, dExp, &result))
|
||||
return result;
|
||||
|
||||
// Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison
|
||||
return StrtodBigInteger(result, decimals, dLen, dExp);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_STRTOD_
|
46
include/rapidjson/internal/swap.h
Normal file
46
include/rapidjson/internal/swap.h
Normal file
@ -0,0 +1,46 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_INTERNAL_SWAP_H_
|
||||
#define RAPIDJSON_INTERNAL_SWAP_H_
|
||||
|
||||
#include "../rapidjson.h"
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
namespace internal {
|
||||
|
||||
//! Custom swap() to avoid dependency on C++ <algorithm> header
|
||||
/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only.
|
||||
\note This has the same semantics as std::swap().
|
||||
*/
|
||||
template <typename T>
|
||||
inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT {
|
||||
T tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_INTERNAL_SWAP_H_
|
128
include/rapidjson/istreamwrapper.h
Normal file
128
include/rapidjson/istreamwrapper.h
Normal file
@ -0,0 +1,128 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_ISTREAMWRAPPER_H_
|
||||
#define RAPIDJSON_ISTREAMWRAPPER_H_
|
||||
|
||||
#include "stream.h"
|
||||
#include <iosfwd>
|
||||
#include <ios>
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
#elif defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept.
|
||||
/*!
|
||||
The classes can be wrapped including but not limited to:
|
||||
|
||||
- \c std::istringstream
|
||||
- \c std::stringstream
|
||||
- \c std::wistringstream
|
||||
- \c std::wstringstream
|
||||
- \c std::ifstream
|
||||
- \c std::fstream
|
||||
- \c std::wifstream
|
||||
- \c std::wfstream
|
||||
|
||||
\tparam StreamType Class derived from \c std::basic_istream.
|
||||
*/
|
||||
|
||||
template <typename StreamType>
|
||||
class BasicIStreamWrapper {
|
||||
public:
|
||||
typedef typename StreamType::char_type Ch;
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param stream stream opened for read.
|
||||
*/
|
||||
BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
|
||||
Read();
|
||||
}
|
||||
|
||||
//! Constructor.
|
||||
/*!
|
||||
\param stream stream opened for read.
|
||||
\param buffer user-supplied buffer.
|
||||
\param bufferSize size of buffer in bytes. Must >=4 bytes.
|
||||
*/
|
||||
BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
|
||||
RAPIDJSON_ASSERT(bufferSize >= 4);
|
||||
Read();
|
||||
}
|
||||
|
||||
Ch Peek() const { return *current_; }
|
||||
Ch Take() { Ch c = *current_; Read(); return c; }
|
||||
size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
|
||||
|
||||
// Not implemented
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
// For encoding detection only.
|
||||
const Ch* Peek4() const {
|
||||
return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0;
|
||||
}
|
||||
|
||||
private:
|
||||
BasicIStreamWrapper();
|
||||
BasicIStreamWrapper(const BasicIStreamWrapper&);
|
||||
BasicIStreamWrapper& operator=(const BasicIStreamWrapper&);
|
||||
|
||||
void Read() {
|
||||
if (current_ < bufferLast_)
|
||||
++current_;
|
||||
else if (!eof_) {
|
||||
count_ += readCount_;
|
||||
readCount_ = bufferSize_;
|
||||
bufferLast_ = buffer_ + readCount_ - 1;
|
||||
current_ = buffer_;
|
||||
|
||||
if (!stream_.read(buffer_, static_cast<std::streamsize>(bufferSize_))) {
|
||||
readCount_ = static_cast<size_t>(stream_.gcount());
|
||||
*(bufferLast_ = buffer_ + readCount_) = '\0';
|
||||
eof_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StreamType &stream_;
|
||||
Ch peekBuffer_[4], *buffer_;
|
||||
size_t bufferSize_;
|
||||
Ch *bufferLast_;
|
||||
Ch *current_;
|
||||
size_t readCount_;
|
||||
size_t count_; //!< Number of characters read
|
||||
bool eof_;
|
||||
};
|
||||
|
||||
typedef BasicIStreamWrapper<std::istream> IStreamWrapper;
|
||||
typedef BasicIStreamWrapper<std::wistream> WIStreamWrapper;
|
||||
|
||||
#if defined(__clang__) || defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_ISTREAMWRAPPER_H_
|
70
include/rapidjson/memorybuffer.h
Normal file
70
include/rapidjson/memorybuffer.h
Normal file
@ -0,0 +1,70 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_MEMORYBUFFER_H_
|
||||
#define RAPIDJSON_MEMORYBUFFER_H_
|
||||
|
||||
#include "stream.h"
|
||||
#include "internal/stack.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Represents an in-memory output byte stream.
|
||||
/*!
|
||||
This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream.
|
||||
|
||||
It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file.
|
||||
|
||||
Differences between MemoryBuffer and StringBuffer:
|
||||
1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer.
|
||||
2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator.
|
||||
|
||||
\tparam Allocator type for allocating memory buffer.
|
||||
\note implements Stream concept
|
||||
*/
|
||||
template <typename Allocator = CrtAllocator>
|
||||
struct GenericMemoryBuffer {
|
||||
typedef char Ch; // byte
|
||||
|
||||
GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
|
||||
|
||||
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
|
||||
void Flush() {}
|
||||
|
||||
void Clear() { stack_.Clear(); }
|
||||
void ShrinkToFit() { stack_.ShrinkToFit(); }
|
||||
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
|
||||
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
|
||||
|
||||
const Ch* GetBuffer() const {
|
||||
return stack_.template Bottom<Ch>();
|
||||
}
|
||||
|
||||
size_t GetSize() const { return stack_.GetSize(); }
|
||||
|
||||
static const size_t kDefaultCapacity = 256;
|
||||
mutable internal::Stack<Allocator> stack_;
|
||||
};
|
||||
|
||||
typedef GenericMemoryBuffer<> MemoryBuffer;
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) {
|
||||
std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c));
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_MEMORYBUFFER_H_
|
71
include/rapidjson/memorystream.h
Normal file
71
include/rapidjson/memorystream.h
Normal file
@ -0,0 +1,71 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_MEMORYSTREAM_H_
|
||||
#define RAPIDJSON_MEMORYSTREAM_H_
|
||||
|
||||
#include "stream.h"
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(unreachable-code)
|
||||
RAPIDJSON_DIAG_OFF(missing-noreturn)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Represents an in-memory input byte stream.
|
||||
/*!
|
||||
This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream.
|
||||
|
||||
It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file.
|
||||
|
||||
Differences between MemoryStream and StringStream:
|
||||
1. StringStream has encoding but MemoryStream is a byte stream.
|
||||
2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source.
|
||||
3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4().
|
||||
\note implements Stream concept
|
||||
*/
|
||||
struct MemoryStream {
|
||||
typedef char Ch; // byte
|
||||
|
||||
MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {}
|
||||
|
||||
Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; }
|
||||
Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; }
|
||||
size_t Tell() const { return static_cast<size_t>(src_ - begin_); }
|
||||
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
// For encoding detection only.
|
||||
const Ch* Peek4() const {
|
||||
return Tell() + 4 <= size_ ? src_ : 0;
|
||||
}
|
||||
|
||||
const Ch* src_; //!< Current read position.
|
||||
const Ch* begin_; //!< Original head of the string.
|
||||
const Ch* end_; //!< End of stream.
|
||||
size_t size_; //!< Size of the stream.
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_MEMORYBUFFER_H_
|
316
include/rapidjson/msinttypes/inttypes.h
Normal file
316
include/rapidjson/msinttypes/inttypes.h
Normal file
@ -0,0 +1,316 @@
|
||||
// ISO C9x compliant inttypes.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006-2013 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the product nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The above software in this distribution may have been modified by
|
||||
// THL A29 Limited ("Tencent Modifications").
|
||||
// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
|
||||
|
||||
#ifndef _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_INTTYPES_H_ // [
|
||||
#define _MSC_INTTYPES_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
// miloyip: VC supports inttypes.h since VC2013
|
||||
#if _MSC_VER >= 1800
|
||||
#include <inttypes.h>
|
||||
#else
|
||||
|
||||
// 7.8 Format conversion of integer types
|
||||
|
||||
typedef struct {
|
||||
intmax_t quot;
|
||||
intmax_t rem;
|
||||
} imaxdiv_t;
|
||||
|
||||
// 7.8.1 Macros for format specifiers
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198
|
||||
|
||||
// The fprintf macros for signed integers are:
|
||||
#define PRId8 "d"
|
||||
#define PRIi8 "i"
|
||||
#define PRIdLEAST8 "d"
|
||||
#define PRIiLEAST8 "i"
|
||||
#define PRIdFAST8 "d"
|
||||
#define PRIiFAST8 "i"
|
||||
|
||||
#define PRId16 "hd"
|
||||
#define PRIi16 "hi"
|
||||
#define PRIdLEAST16 "hd"
|
||||
#define PRIiLEAST16 "hi"
|
||||
#define PRIdFAST16 "hd"
|
||||
#define PRIiFAST16 "hi"
|
||||
|
||||
#define PRId32 "I32d"
|
||||
#define PRIi32 "I32i"
|
||||
#define PRIdLEAST32 "I32d"
|
||||
#define PRIiLEAST32 "I32i"
|
||||
#define PRIdFAST32 "I32d"
|
||||
#define PRIiFAST32 "I32i"
|
||||
|
||||
#define PRId64 "I64d"
|
||||
#define PRIi64 "I64i"
|
||||
#define PRIdLEAST64 "I64d"
|
||||
#define PRIiLEAST64 "I64i"
|
||||
#define PRIdFAST64 "I64d"
|
||||
#define PRIiFAST64 "I64i"
|
||||
|
||||
#define PRIdMAX "I64d"
|
||||
#define PRIiMAX "I64i"
|
||||
|
||||
#define PRIdPTR "Id"
|
||||
#define PRIiPTR "Ii"
|
||||
|
||||
// The fprintf macros for unsigned integers are:
|
||||
#define PRIo8 "o"
|
||||
#define PRIu8 "u"
|
||||
#define PRIx8 "x"
|
||||
#define PRIX8 "X"
|
||||
#define PRIoLEAST8 "o"
|
||||
#define PRIuLEAST8 "u"
|
||||
#define PRIxLEAST8 "x"
|
||||
#define PRIXLEAST8 "X"
|
||||
#define PRIoFAST8 "o"
|
||||
#define PRIuFAST8 "u"
|
||||
#define PRIxFAST8 "x"
|
||||
#define PRIXFAST8 "X"
|
||||
|
||||
#define PRIo16 "ho"
|
||||
#define PRIu16 "hu"
|
||||
#define PRIx16 "hx"
|
||||
#define PRIX16 "hX"
|
||||
#define PRIoLEAST16 "ho"
|
||||
#define PRIuLEAST16 "hu"
|
||||
#define PRIxLEAST16 "hx"
|
||||
#define PRIXLEAST16 "hX"
|
||||
#define PRIoFAST16 "ho"
|
||||
#define PRIuFAST16 "hu"
|
||||
#define PRIxFAST16 "hx"
|
||||
#define PRIXFAST16 "hX"
|
||||
|
||||
#define PRIo32 "I32o"
|
||||
#define PRIu32 "I32u"
|
||||
#define PRIx32 "I32x"
|
||||
#define PRIX32 "I32X"
|
||||
#define PRIoLEAST32 "I32o"
|
||||
#define PRIuLEAST32 "I32u"
|
||||
#define PRIxLEAST32 "I32x"
|
||||
#define PRIXLEAST32 "I32X"
|
||||
#define PRIoFAST32 "I32o"
|
||||
#define PRIuFAST32 "I32u"
|
||||
#define PRIxFAST32 "I32x"
|
||||
#define PRIXFAST32 "I32X"
|
||||
|
||||
#define PRIo64 "I64o"
|
||||
#define PRIu64 "I64u"
|
||||
#define PRIx64 "I64x"
|
||||
#define PRIX64 "I64X"
|
||||
#define PRIoLEAST64 "I64o"
|
||||
#define PRIuLEAST64 "I64u"
|
||||
#define PRIxLEAST64 "I64x"
|
||||
#define PRIXLEAST64 "I64X"
|
||||
#define PRIoFAST64 "I64o"
|
||||
#define PRIuFAST64 "I64u"
|
||||
#define PRIxFAST64 "I64x"
|
||||
#define PRIXFAST64 "I64X"
|
||||
|
||||
#define PRIoMAX "I64o"
|
||||
#define PRIuMAX "I64u"
|
||||
#define PRIxMAX "I64x"
|
||||
#define PRIXMAX "I64X"
|
||||
|
||||
#define PRIoPTR "Io"
|
||||
#define PRIuPTR "Iu"
|
||||
#define PRIxPTR "Ix"
|
||||
#define PRIXPTR "IX"
|
||||
|
||||
// The fscanf macros for signed integers are:
|
||||
#define SCNd8 "d"
|
||||
#define SCNi8 "i"
|
||||
#define SCNdLEAST8 "d"
|
||||
#define SCNiLEAST8 "i"
|
||||
#define SCNdFAST8 "d"
|
||||
#define SCNiFAST8 "i"
|
||||
|
||||
#define SCNd16 "hd"
|
||||
#define SCNi16 "hi"
|
||||
#define SCNdLEAST16 "hd"
|
||||
#define SCNiLEAST16 "hi"
|
||||
#define SCNdFAST16 "hd"
|
||||
#define SCNiFAST16 "hi"
|
||||
|
||||
#define SCNd32 "ld"
|
||||
#define SCNi32 "li"
|
||||
#define SCNdLEAST32 "ld"
|
||||
#define SCNiLEAST32 "li"
|
||||
#define SCNdFAST32 "ld"
|
||||
#define SCNiFAST32 "li"
|
||||
|
||||
#define SCNd64 "I64d"
|
||||
#define SCNi64 "I64i"
|
||||
#define SCNdLEAST64 "I64d"
|
||||
#define SCNiLEAST64 "I64i"
|
||||
#define SCNdFAST64 "I64d"
|
||||
#define SCNiFAST64 "I64i"
|
||||
|
||||
#define SCNdMAX "I64d"
|
||||
#define SCNiMAX "I64i"
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define SCNdPTR "I64d"
|
||||
# define SCNiPTR "I64i"
|
||||
#else // _WIN64 ][
|
||||
# define SCNdPTR "ld"
|
||||
# define SCNiPTR "li"
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// The fscanf macros for unsigned integers are:
|
||||
#define SCNo8 "o"
|
||||
#define SCNu8 "u"
|
||||
#define SCNx8 "x"
|
||||
#define SCNX8 "X"
|
||||
#define SCNoLEAST8 "o"
|
||||
#define SCNuLEAST8 "u"
|
||||
#define SCNxLEAST8 "x"
|
||||
#define SCNXLEAST8 "X"
|
||||
#define SCNoFAST8 "o"
|
||||
#define SCNuFAST8 "u"
|
||||
#define SCNxFAST8 "x"
|
||||
#define SCNXFAST8 "X"
|
||||
|
||||
#define SCNo16 "ho"
|
||||
#define SCNu16 "hu"
|
||||
#define SCNx16 "hx"
|
||||
#define SCNX16 "hX"
|
||||
#define SCNoLEAST16 "ho"
|
||||
#define SCNuLEAST16 "hu"
|
||||
#define SCNxLEAST16 "hx"
|
||||
#define SCNXLEAST16 "hX"
|
||||
#define SCNoFAST16 "ho"
|
||||
#define SCNuFAST16 "hu"
|
||||
#define SCNxFAST16 "hx"
|
||||
#define SCNXFAST16 "hX"
|
||||
|
||||
#define SCNo32 "lo"
|
||||
#define SCNu32 "lu"
|
||||
#define SCNx32 "lx"
|
||||
#define SCNX32 "lX"
|
||||
#define SCNoLEAST32 "lo"
|
||||
#define SCNuLEAST32 "lu"
|
||||
#define SCNxLEAST32 "lx"
|
||||
#define SCNXLEAST32 "lX"
|
||||
#define SCNoFAST32 "lo"
|
||||
#define SCNuFAST32 "lu"
|
||||
#define SCNxFAST32 "lx"
|
||||
#define SCNXFAST32 "lX"
|
||||
|
||||
#define SCNo64 "I64o"
|
||||
#define SCNu64 "I64u"
|
||||
#define SCNx64 "I64x"
|
||||
#define SCNX64 "I64X"
|
||||
#define SCNoLEAST64 "I64o"
|
||||
#define SCNuLEAST64 "I64u"
|
||||
#define SCNxLEAST64 "I64x"
|
||||
#define SCNXLEAST64 "I64X"
|
||||
#define SCNoFAST64 "I64o"
|
||||
#define SCNuFAST64 "I64u"
|
||||
#define SCNxFAST64 "I64x"
|
||||
#define SCNXFAST64 "I64X"
|
||||
|
||||
#define SCNoMAX "I64o"
|
||||
#define SCNuMAX "I64u"
|
||||
#define SCNxMAX "I64x"
|
||||
#define SCNXMAX "I64X"
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define SCNoPTR "I64o"
|
||||
# define SCNuPTR "I64u"
|
||||
# define SCNxPTR "I64x"
|
||||
# define SCNXPTR "I64X"
|
||||
#else // _WIN64 ][
|
||||
# define SCNoPTR "lo"
|
||||
# define SCNuPTR "lu"
|
||||
# define SCNxPTR "lx"
|
||||
# define SCNXPTR "lX"
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#endif // __STDC_FORMAT_MACROS ]
|
||||
|
||||
// 7.8.2 Functions for greatest-width integer types
|
||||
|
||||
// 7.8.2.1 The imaxabs function
|
||||
#define imaxabs _abs64
|
||||
|
||||
// 7.8.2.2 The imaxdiv function
|
||||
|
||||
// This is modified version of div() function from Microsoft's div.c found
|
||||
// in %MSVC.NET%\crt\src\div.c
|
||||
#ifdef STATIC_IMAXDIV // [
|
||||
static
|
||||
#else // STATIC_IMAXDIV ][
|
||||
_inline
|
||||
#endif // STATIC_IMAXDIV ]
|
||||
imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
|
||||
{
|
||||
imaxdiv_t result;
|
||||
|
||||
result.quot = numer / denom;
|
||||
result.rem = numer % denom;
|
||||
|
||||
if (numer < 0 && result.rem > 0) {
|
||||
// did division wrong; must fix up
|
||||
++result.quot;
|
||||
result.rem -= denom;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 7.8.2.3 The strtoimax and strtoumax functions
|
||||
#define strtoimax _strtoi64
|
||||
#define strtoumax _strtoui64
|
||||
|
||||
// 7.8.2.4 The wcstoimax and wcstoumax functions
|
||||
#define wcstoimax _wcstoi64
|
||||
#define wcstoumax _wcstoui64
|
||||
|
||||
#endif // _MSC_VER >= 1800
|
||||
|
||||
#endif // _MSC_INTTYPES_H_ ]
|
300
include/rapidjson/msinttypes/stdint.h
Normal file
300
include/rapidjson/msinttypes/stdint.h
Normal file
@ -0,0 +1,300 @@
|
||||
// ISO C9x compliant stdint.h for Microsoft Visual Studio
|
||||
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
//
|
||||
// Copyright (c) 2006-2013 Alexander Chemeris
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the product nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The above software in this distribution may have been modified by
|
||||
// THL A29 Limited ("Tencent Modifications").
|
||||
// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited.
|
||||
|
||||
#ifndef _MSC_VER // [
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif // _MSC_VER ]
|
||||
|
||||
#ifndef _MSC_STDINT_H_ // [
|
||||
#define _MSC_STDINT_H_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010.
|
||||
#if _MSC_VER >= 1600 // [
|
||||
#include <stdint.h>
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||
|
||||
#undef INT8_C
|
||||
#undef INT16_C
|
||||
#undef INT32_C
|
||||
#undef INT64_C
|
||||
#undef UINT8_C
|
||||
#undef UINT16_C
|
||||
#undef UINT32_C
|
||||
#undef UINT64_C
|
||||
|
||||
// 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
// 7.18.4.2 Macros for greatest-width integer constants
|
||||
// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
|
||||
// Check out Issue 9 for the details.
|
||||
#ifndef INTMAX_C // [
|
||||
# define INTMAX_C INT64_C
|
||||
#endif // INTMAX_C ]
|
||||
#ifndef UINTMAX_C // [
|
||||
# define UINTMAX_C UINT64_C
|
||||
#endif // UINTMAX_C ]
|
||||
|
||||
#endif // __STDC_CONSTANT_MACROS ]
|
||||
|
||||
#else // ] _MSC_VER >= 1700 [
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
|
||||
// compiling for ARM we have to wrap <wchar.h> include with 'extern "C++" {}'
|
||||
// or compiler would give many errors like this:
|
||||
// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
|
||||
#if defined(__cplusplus) && !defined(_M_ARM)
|
||||
extern "C" {
|
||||
#endif
|
||||
# include <wchar.h>
|
||||
#if defined(__cplusplus) && !defined(_M_ARM)
|
||||
}
|
||||
#endif
|
||||
|
||||
// Define _W64 macros to mark types changing their size, like intptr_t.
|
||||
#ifndef _W64
|
||||
# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
|
||||
# define _W64 __w64
|
||||
# else
|
||||
# define _W64
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
// 7.18.1 Integer types
|
||||
|
||||
// 7.18.1.1 Exact-width integer types
|
||||
|
||||
// Visual Studio 6 and Embedded Visual C++ 4 doesn't
|
||||
// realize that, e.g. char has the same size as __int8
|
||||
// so we give up on __intX for them.
|
||||
#if (_MSC_VER < 1300)
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
typedef signed __int8 int8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#endif
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
|
||||
// 7.18.1.2 Minimum-width integer types
|
||||
typedef int8_t int_least8_t;
|
||||
typedef int16_t int_least16_t;
|
||||
typedef int32_t int_least32_t;
|
||||
typedef int64_t int_least64_t;
|
||||
typedef uint8_t uint_least8_t;
|
||||
typedef uint16_t uint_least16_t;
|
||||
typedef uint32_t uint_least32_t;
|
||||
typedef uint64_t uint_least64_t;
|
||||
|
||||
// 7.18.1.3 Fastest minimum-width integer types
|
||||
typedef int8_t int_fast8_t;
|
||||
typedef int16_t int_fast16_t;
|
||||
typedef int32_t int_fast32_t;
|
||||
typedef int64_t int_fast64_t;
|
||||
typedef uint8_t uint_fast8_t;
|
||||
typedef uint16_t uint_fast16_t;
|
||||
typedef uint32_t uint_fast32_t;
|
||||
typedef uint64_t uint_fast64_t;
|
||||
|
||||
// 7.18.1.4 Integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
typedef signed __int64 intptr_t;
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else // _WIN64 ][
|
||||
typedef _W64 signed int intptr_t;
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.1.5 Greatest-width integer types
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
|
||||
|
||||
// 7.18.2 Limits of specified-width integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
|
||||
|
||||
// 7.18.2.1 Limits of exact-width integer types
|
||||
#define INT8_MIN ((int8_t)_I8_MIN)
|
||||
#define INT8_MAX _I8_MAX
|
||||
#define INT16_MIN ((int16_t)_I16_MIN)
|
||||
#define INT16_MAX _I16_MAX
|
||||
#define INT32_MIN ((int32_t)_I32_MIN)
|
||||
#define INT32_MAX _I32_MAX
|
||||
#define INT64_MIN ((int64_t)_I64_MIN)
|
||||
#define INT64_MAX _I64_MAX
|
||||
#define UINT8_MAX _UI8_MAX
|
||||
#define UINT16_MAX _UI16_MAX
|
||||
#define UINT32_MAX _UI32_MAX
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
|
||||
// 7.18.2.2 Limits of minimum-width integer types
|
||||
#define INT_LEAST8_MIN INT8_MIN
|
||||
#define INT_LEAST8_MAX INT8_MAX
|
||||
#define INT_LEAST16_MIN INT16_MIN
|
||||
#define INT_LEAST16_MAX INT16_MAX
|
||||
#define INT_LEAST32_MIN INT32_MIN
|
||||
#define INT_LEAST32_MAX INT32_MAX
|
||||
#define INT_LEAST64_MIN INT64_MIN
|
||||
#define INT_LEAST64_MAX INT64_MAX
|
||||
#define UINT_LEAST8_MAX UINT8_MAX
|
||||
#define UINT_LEAST16_MAX UINT16_MAX
|
||||
#define UINT_LEAST32_MAX UINT32_MAX
|
||||
#define UINT_LEAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.3 Limits of fastest minimum-width integer types
|
||||
#define INT_FAST8_MIN INT8_MIN
|
||||
#define INT_FAST8_MAX INT8_MAX
|
||||
#define INT_FAST16_MIN INT16_MIN
|
||||
#define INT_FAST16_MAX INT16_MAX
|
||||
#define INT_FAST32_MIN INT32_MIN
|
||||
#define INT_FAST32_MAX INT32_MAX
|
||||
#define INT_FAST64_MIN INT64_MIN
|
||||
#define INT_FAST64_MAX INT64_MAX
|
||||
#define UINT_FAST8_MAX UINT8_MAX
|
||||
#define UINT_FAST16_MAX UINT16_MAX
|
||||
#define UINT_FAST32_MAX UINT32_MAX
|
||||
#define UINT_FAST64_MAX UINT64_MAX
|
||||
|
||||
// 7.18.2.4 Limits of integer types capable of holding object pointers
|
||||
#ifdef _WIN64 // [
|
||||
# define INTPTR_MIN INT64_MIN
|
||||
# define INTPTR_MAX INT64_MAX
|
||||
# define UINTPTR_MAX UINT64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define INTPTR_MIN INT32_MIN
|
||||
# define INTPTR_MAX INT32_MAX
|
||||
# define UINTPTR_MAX UINT32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
// 7.18.2.5 Limits of greatest-width integer types
|
||||
#define INTMAX_MIN INT64_MIN
|
||||
#define INTMAX_MAX INT64_MAX
|
||||
#define UINTMAX_MAX UINT64_MAX
|
||||
|
||||
// 7.18.3 Limits of other integer types
|
||||
|
||||
#ifdef _WIN64 // [
|
||||
# define PTRDIFF_MIN _I64_MIN
|
||||
# define PTRDIFF_MAX _I64_MAX
|
||||
#else // _WIN64 ][
|
||||
# define PTRDIFF_MIN _I32_MIN
|
||||
# define PTRDIFF_MAX _I32_MAX
|
||||
#endif // _WIN64 ]
|
||||
|
||||
#define SIG_ATOMIC_MIN INT_MIN
|
||||
#define SIG_ATOMIC_MAX INT_MAX
|
||||
|
||||
#ifndef SIZE_MAX // [
|
||||
# ifdef _WIN64 // [
|
||||
# define SIZE_MAX _UI64_MAX
|
||||
# else // _WIN64 ][
|
||||
# define SIZE_MAX _UI32_MAX
|
||||
# endif // _WIN64 ]
|
||||
#endif // SIZE_MAX ]
|
||||
|
||||
// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
|
||||
#ifndef WCHAR_MIN // [
|
||||
# define WCHAR_MIN 0
|
||||
#endif // WCHAR_MIN ]
|
||||
#ifndef WCHAR_MAX // [
|
||||
# define WCHAR_MAX _UI16_MAX
|
||||
#endif // WCHAR_MAX ]
|
||||
|
||||
#define WINT_MIN 0
|
||||
#define WINT_MAX _UI16_MAX
|
||||
|
||||
#endif // __STDC_LIMIT_MACROS ]
|
||||
|
||||
|
||||
// 7.18.4 Limits of other integer types
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
|
||||
|
||||
// 7.18.4.1 Macros for minimum-width integer constants
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
// 7.18.4.2 Macros for greatest-width integer constants
|
||||
// These #ifndef's are needed to prevent collisions with <boost/cstdint.hpp>.
|
||||
// Check out Issue 9 for the details.
|
||||
#ifndef INTMAX_C // [
|
||||
# define INTMAX_C INT64_C
|
||||
#endif // INTMAX_C ]
|
||||
#ifndef UINTMAX_C // [
|
||||
# define UINTMAX_C UINT64_C
|
||||
#endif // UINTMAX_C ]
|
||||
|
||||
#endif // __STDC_CONSTANT_MACROS ]
|
||||
|
||||
#endif // _MSC_VER >= 1600 ]
|
||||
|
||||
#endif // _MSC_STDINT_H_ ]
|
81
include/rapidjson/ostreamwrapper.h
Normal file
81
include/rapidjson/ostreamwrapper.h
Normal file
@ -0,0 +1,81 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_OSTREAMWRAPPER_H_
|
||||
#define RAPIDJSON_OSTREAMWRAPPER_H_
|
||||
|
||||
#include "stream.h"
|
||||
#include <iosfwd>
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept.
|
||||
/*!
|
||||
The classes can be wrapped including but not limited to:
|
||||
|
||||
- \c std::ostringstream
|
||||
- \c std::stringstream
|
||||
- \c std::wpstringstream
|
||||
- \c std::wstringstream
|
||||
- \c std::ifstream
|
||||
- \c std::fstream
|
||||
- \c std::wofstream
|
||||
- \c std::wfstream
|
||||
|
||||
\tparam StreamType Class derived from \c std::basic_ostream.
|
||||
*/
|
||||
|
||||
template <typename StreamType>
|
||||
class BasicOStreamWrapper {
|
||||
public:
|
||||
typedef typename StreamType::char_type Ch;
|
||||
BasicOStreamWrapper(StreamType& stream) : stream_(stream) {}
|
||||
|
||||
void Put(Ch c) {
|
||||
stream_.put(c);
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
stream_.flush();
|
||||
}
|
||||
|
||||
// Not implemented
|
||||
char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
char Take() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
|
||||
char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
private:
|
||||
BasicOStreamWrapper(const BasicOStreamWrapper&);
|
||||
BasicOStreamWrapper& operator=(const BasicOStreamWrapper&);
|
||||
|
||||
StreamType& stream_;
|
||||
};
|
||||
|
||||
typedef BasicOStreamWrapper<std::ostream> OStreamWrapper;
|
||||
typedef BasicOStreamWrapper<std::wostream> WOStreamWrapper;
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_OSTREAMWRAPPER_H_
|
1476
include/rapidjson/pointer.h
Normal file
1476
include/rapidjson/pointer.h
Normal file
File diff suppressed because it is too large
Load Diff
277
include/rapidjson/prettywriter.h
Normal file
277
include/rapidjson/prettywriter.h
Normal file
@ -0,0 +1,277 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_PRETTYWRITER_H_
|
||||
#define RAPIDJSON_PRETTYWRITER_H_
|
||||
|
||||
#include "writer.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(effc++)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Combination of PrettyWriter format flags.
|
||||
/*! \see PrettyWriter::SetFormatOptions
|
||||
*/
|
||||
enum PrettyFormatOptions {
|
||||
kFormatDefault = 0, //!< Default pretty formatting.
|
||||
kFormatSingleLineArray = 1 //!< Format arrays on a single line.
|
||||
};
|
||||
|
||||
//! Writer with indentation and spacing.
|
||||
/*!
|
||||
\tparam OutputStream Type of output os.
|
||||
\tparam SourceEncoding Encoding of source string.
|
||||
\tparam TargetEncoding Encoding of output stream.
|
||||
\tparam StackAllocator Type of allocator for allocating memory of stack.
|
||||
*/
|
||||
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
|
||||
class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> {
|
||||
public:
|
||||
typedef Writer<OutputStream, SourceEncoding, TargetEncoding, StackAllocator, writeFlags> Base;
|
||||
typedef typename Base::Ch Ch;
|
||||
|
||||
//! Constructor
|
||||
/*! \param os Output stream.
|
||||
\param allocator User supplied allocator. If it is null, it will create a private one.
|
||||
\param levelDepth Initial capacity of stack.
|
||||
*/
|
||||
explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
|
||||
Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {}
|
||||
|
||||
|
||||
explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
|
||||
Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
PrettyWriter(PrettyWriter&& rhs) :
|
||||
Base(std::forward<PrettyWriter>(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {}
|
||||
#endif
|
||||
|
||||
//! Set custom indentation.
|
||||
/*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
|
||||
\param indentCharCount Number of indent characters for each indentation level.
|
||||
\note The default indentation is 4 spaces.
|
||||
*/
|
||||
PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
|
||||
RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
|
||||
indentChar_ = indentChar;
|
||||
indentCharCount_ = indentCharCount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Set pretty writer formatting options.
|
||||
/*! \param options Formatting options.
|
||||
*/
|
||||
PrettyWriter& SetFormatOptions(PrettyFormatOptions options) {
|
||||
formatOptions_ = options;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*! @name Implementation of Handler
|
||||
\see Handler
|
||||
*/
|
||||
//@{
|
||||
|
||||
bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); }
|
||||
bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); }
|
||||
bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); }
|
||||
bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); }
|
||||
bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); }
|
||||
bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); }
|
||||
bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); }
|
||||
|
||||
bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
|
||||
RAPIDJSON_ASSERT(str != 0);
|
||||
(void)copy;
|
||||
PrettyPrefix(kNumberType);
|
||||
return Base::EndValue(Base::WriteString(str, length));
|
||||
}
|
||||
|
||||
bool String(const Ch* str, SizeType length, bool copy = false) {
|
||||
RAPIDJSON_ASSERT(str != 0);
|
||||
(void)copy;
|
||||
PrettyPrefix(kStringType);
|
||||
return Base::EndValue(Base::WriteString(str, length));
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
bool String(const std::basic_string<Ch>& str) {
|
||||
return String(str.data(), SizeType(str.size()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool StartObject() {
|
||||
PrettyPrefix(kObjectType);
|
||||
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
|
||||
return Base::WriteStartObject();
|
||||
}
|
||||
|
||||
bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
bool Key(const std::basic_string<Ch>& str) {
|
||||
return Key(str.data(), SizeType(str.size()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool EndObject(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object
|
||||
RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray); // currently inside an Array, not Object
|
||||
RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top<typename Base::Level>()->valueCount % 2); // Object has a Key without a Value
|
||||
|
||||
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
|
||||
|
||||
if (!empty) {
|
||||
Base::os_->Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
bool ret = Base::EndValue(Base::WriteEndObject());
|
||||
(void)ret;
|
||||
RAPIDJSON_ASSERT(ret == true);
|
||||
if (Base::level_stack_.Empty()) // end of json text
|
||||
Base::Flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StartArray() {
|
||||
PrettyPrefix(kArrayType);
|
||||
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
|
||||
return Base::WriteStartArray();
|
||||
}
|
||||
|
||||
bool EndArray(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
|
||||
RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
|
||||
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
|
||||
|
||||
if (!empty && !(formatOptions_ & kFormatSingleLineArray)) {
|
||||
Base::os_->Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
bool ret = Base::EndValue(Base::WriteEndArray());
|
||||
(void)ret;
|
||||
RAPIDJSON_ASSERT(ret == true);
|
||||
if (Base::level_stack_.Empty()) // end of json text
|
||||
Base::Flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
/*! @name Convenience extensions */
|
||||
//@{
|
||||
|
||||
//! Simpler but slower overload.
|
||||
bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
|
||||
bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); }
|
||||
|
||||
//@}
|
||||
|
||||
//! Write a raw JSON value.
|
||||
/*!
|
||||
For user to write a stringified JSON as a value.
|
||||
|
||||
\param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
|
||||
\param length Length of the json.
|
||||
\param type Type of the root of json.
|
||||
\note When using PrettyWriter::RawValue(), the result json may not be indented correctly.
|
||||
*/
|
||||
bool RawValue(const Ch* json, size_t length, Type type) {
|
||||
RAPIDJSON_ASSERT(json != 0);
|
||||
PrettyPrefix(type);
|
||||
return Base::EndValue(Base::WriteRawValue(json, length));
|
||||
}
|
||||
|
||||
protected:
|
||||
void PrettyPrefix(Type type) {
|
||||
(void)type;
|
||||
if (Base::level_stack_.GetSize() != 0) { // this value is not at root
|
||||
typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
|
||||
|
||||
if (level->inArray) {
|
||||
if (level->valueCount > 0) {
|
||||
Base::os_->Put(','); // add comma if it is not the first element in array
|
||||
if (formatOptions_ & kFormatSingleLineArray)
|
||||
Base::os_->Put(' ');
|
||||
}
|
||||
|
||||
if (!(formatOptions_ & kFormatSingleLineArray)) {
|
||||
Base::os_->Put('\n');
|
||||
WriteIndent();
|
||||
}
|
||||
}
|
||||
else { // in object
|
||||
if (level->valueCount > 0) {
|
||||
if (level->valueCount % 2 == 0) {
|
||||
Base::os_->Put(',');
|
||||
Base::os_->Put('\n');
|
||||
}
|
||||
else {
|
||||
Base::os_->Put(':');
|
||||
Base::os_->Put(' ');
|
||||
}
|
||||
}
|
||||
else
|
||||
Base::os_->Put('\n');
|
||||
|
||||
if (level->valueCount % 2 == 0)
|
||||
WriteIndent();
|
||||
}
|
||||
if (!level->inArray && level->valueCount % 2 == 0)
|
||||
RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
|
||||
level->valueCount++;
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root.
|
||||
Base::hasRoot_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteIndent() {
|
||||
size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
|
||||
PutN(*Base::os_, static_cast<typename OutputStream::Ch>(indentChar_), count);
|
||||
}
|
||||
|
||||
Ch indentChar_;
|
||||
unsigned indentCharCount_;
|
||||
PrettyFormatOptions formatOptions_;
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
PrettyWriter(const PrettyWriter&);
|
||||
PrettyWriter& operator=(const PrettyWriter&);
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSON_H_
|
741
include/rapidjson/rapidjson.h
Normal file
741
include/rapidjson/rapidjson.h
Normal file
@ -0,0 +1,741 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_RAPIDJSON_H_
|
||||
#define RAPIDJSON_RAPIDJSON_H_
|
||||
|
||||
/*!\file rapidjson.h
|
||||
\brief common definitions and configuration
|
||||
|
||||
\see RAPIDJSON_CONFIG
|
||||
*/
|
||||
|
||||
/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration
|
||||
\brief Configuration macros for library features
|
||||
|
||||
Some RapidJSON features are configurable to adapt the library to a wide
|
||||
variety of platforms, environments and usage scenarios. Most of the
|
||||
features can be configured in terms of overridden or predefined
|
||||
preprocessor macros at compile-time.
|
||||
|
||||
Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs.
|
||||
|
||||
\note These macros should be given on the compiler command-line
|
||||
(where applicable) to avoid inconsistent values when compiling
|
||||
different translation units of a single application.
|
||||
*/
|
||||
|
||||
#include <cstdlib> // malloc(), realloc(), free(), size_t
|
||||
#include <cstring> // memset(), memcpy(), memmove(), memcmp()
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_VERSION_STRING
|
||||
//
|
||||
// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt.
|
||||
//
|
||||
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
// token stringification
|
||||
#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x)
|
||||
#define RAPIDJSON_DO_STRINGIFY(x) #x
|
||||
|
||||
// token concatenation
|
||||
#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y)
|
||||
#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y)
|
||||
#define RAPIDJSON_DO_JOIN2(X, Y) X##Y
|
||||
//!@endcond
|
||||
|
||||
/*! \def RAPIDJSON_MAJOR_VERSION
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief Major version of RapidJSON in integer.
|
||||
*/
|
||||
/*! \def RAPIDJSON_MINOR_VERSION
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief Minor version of RapidJSON in integer.
|
||||
*/
|
||||
/*! \def RAPIDJSON_PATCH_VERSION
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief Patch version of RapidJSON in integer.
|
||||
*/
|
||||
/*! \def RAPIDJSON_VERSION_STRING
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief Version of RapidJSON in "<major>.<minor>.<patch>" string format.
|
||||
*/
|
||||
#define RAPIDJSON_MAJOR_VERSION 1
|
||||
#define RAPIDJSON_MINOR_VERSION 1
|
||||
#define RAPIDJSON_PATCH_VERSION 0
|
||||
#define RAPIDJSON_VERSION_STRING \
|
||||
RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_NAMESPACE_(BEGIN|END)
|
||||
/*! \def RAPIDJSON_NAMESPACE
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief provide custom rapidjson namespace
|
||||
|
||||
In order to avoid symbol clashes and/or "One Definition Rule" errors
|
||||
between multiple inclusions of (different versions of) RapidJSON in
|
||||
a single binary, users can customize the name of the main RapidJSON
|
||||
namespace.
|
||||
|
||||
In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE
|
||||
to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple
|
||||
levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref
|
||||
RAPIDJSON_NAMESPACE_END need to be defined as well:
|
||||
|
||||
\code
|
||||
// in some .cpp file
|
||||
#define RAPIDJSON_NAMESPACE my::rapidjson
|
||||
#define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson {
|
||||
#define RAPIDJSON_NAMESPACE_END } }
|
||||
#include "rapidjson/..."
|
||||
\endcode
|
||||
|
||||
\see rapidjson
|
||||
*/
|
||||
/*! \def RAPIDJSON_NAMESPACE_BEGIN
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief provide custom rapidjson namespace (opening expression)
|
||||
\see RAPIDJSON_NAMESPACE
|
||||
*/
|
||||
/*! \def RAPIDJSON_NAMESPACE_END
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief provide custom rapidjson namespace (closing expression)
|
||||
\see RAPIDJSON_NAMESPACE
|
||||
*/
|
||||
#ifndef RAPIDJSON_NAMESPACE
|
||||
#define RAPIDJSON_NAMESPACE rapidjson
|
||||
#endif
|
||||
#ifndef RAPIDJSON_NAMESPACE_BEGIN
|
||||
#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE {
|
||||
#endif
|
||||
#ifndef RAPIDJSON_NAMESPACE_END
|
||||
#define RAPIDJSON_NAMESPACE_END }
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// __cplusplus macro
|
||||
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define RAPIDJSON_CPLUSPLUS _MSVC_LANG
|
||||
#else
|
||||
#define RAPIDJSON_CPLUSPLUS __cplusplus
|
||||
#endif
|
||||
|
||||
//!@endcond
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_HAS_STDSTRING
|
||||
|
||||
#ifndef RAPIDJSON_HAS_STDSTRING
|
||||
#ifdef RAPIDJSON_DOXYGEN_RUNNING
|
||||
#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation
|
||||
#else
|
||||
#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default
|
||||
#endif
|
||||
/*! \def RAPIDJSON_HAS_STDSTRING
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief Enable RapidJSON support for \c std::string
|
||||
|
||||
By defining this preprocessor symbol to \c 1, several convenience functions for using
|
||||
\ref rapidjson::GenericValue with \c std::string are enabled, especially
|
||||
for construction and comparison.
|
||||
|
||||
\hideinitializer
|
||||
*/
|
||||
#endif // !defined(RAPIDJSON_HAS_STDSTRING)
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
#include <string>
|
||||
#endif // RAPIDJSON_HAS_STDSTRING
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_USE_MEMBERSMAP
|
||||
|
||||
/*! \def RAPIDJSON_USE_MEMBERSMAP
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief Enable RapidJSON support for object members handling in a \c std::multimap
|
||||
|
||||
By defining this preprocessor symbol to \c 1, \ref rapidjson::GenericValue object
|
||||
members are stored in a \c std::multimap for faster lookup and deletion times, a
|
||||
trade off with a slightly slower insertion time and a small object allocat(or)ed
|
||||
memory overhead.
|
||||
|
||||
\hideinitializer
|
||||
*/
|
||||
#ifndef RAPIDJSON_USE_MEMBERSMAP
|
||||
#define RAPIDJSON_USE_MEMBERSMAP 0 // not by default
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_NO_INT64DEFINE
|
||||
|
||||
/*! \def RAPIDJSON_NO_INT64DEFINE
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief Use external 64-bit integer types.
|
||||
|
||||
RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types
|
||||
to be available at global scope.
|
||||
|
||||
If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to
|
||||
prevent RapidJSON from defining its own types.
|
||||
*/
|
||||
#ifndef RAPIDJSON_NO_INT64DEFINE
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013
|
||||
#include "msinttypes/stdint.h"
|
||||
#include "msinttypes/inttypes.h"
|
||||
#else
|
||||
// Other compilers should have this.
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
//!@endcond
|
||||
#ifdef RAPIDJSON_DOXYGEN_RUNNING
|
||||
#define RAPIDJSON_NO_INT64DEFINE
|
||||
#endif
|
||||
#endif // RAPIDJSON_NO_INT64TYPEDEF
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_FORCEINLINE
|
||||
|
||||
#ifndef RAPIDJSON_FORCEINLINE
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
#if defined(_MSC_VER) && defined(NDEBUG)
|
||||
#define RAPIDJSON_FORCEINLINE __forceinline
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG)
|
||||
#define RAPIDJSON_FORCEINLINE __attribute__((always_inline))
|
||||
#else
|
||||
#define RAPIDJSON_FORCEINLINE
|
||||
#endif
|
||||
//!@endcond
|
||||
#endif // RAPIDJSON_FORCEINLINE
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ENDIAN
|
||||
#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine
|
||||
#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine
|
||||
|
||||
//! Endianness of the machine.
|
||||
/*!
|
||||
\def RAPIDJSON_ENDIAN
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
|
||||
GCC 4.6 provided macro for detecting endianness of the target machine. But other
|
||||
compilers may not have this. User can define RAPIDJSON_ENDIAN to either
|
||||
\ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN.
|
||||
|
||||
Default detection implemented with reference to
|
||||
\li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html
|
||||
\li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp
|
||||
*/
|
||||
#ifndef RAPIDJSON_ENDIAN
|
||||
// Detect with GCC 4.6's macro
|
||||
# ifdef __BYTE_ORDER__
|
||||
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
# else
|
||||
# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN.
|
||||
# endif // __BYTE_ORDER__
|
||||
// Detect with GLIBC's endian.h
|
||||
# elif defined(__GLIBC__)
|
||||
# include <endian.h>
|
||||
# if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif (__BYTE_ORDER == __BIG_ENDIAN)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
# else
|
||||
# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN.
|
||||
# endif // __GLIBC__
|
||||
// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro
|
||||
# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
// Detect with architecture macros
|
||||
# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
|
||||
# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64))
|
||||
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
|
||||
# elif defined(RAPIDJSON_DOXYGEN_RUNNING)
|
||||
# define RAPIDJSON_ENDIAN
|
||||
# else
|
||||
# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN.
|
||||
# endif
|
||||
#endif // RAPIDJSON_ENDIAN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_64BIT
|
||||
|
||||
//! Whether using 64-bit architecture
|
||||
#ifndef RAPIDJSON_64BIT
|
||||
#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__)
|
||||
#define RAPIDJSON_64BIT 1
|
||||
#else
|
||||
#define RAPIDJSON_64BIT 0
|
||||
#endif
|
||||
#endif // RAPIDJSON_64BIT
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ALIGN
|
||||
|
||||
//! Data alignment of the machine.
|
||||
/*! \ingroup RAPIDJSON_CONFIG
|
||||
\param x pointer to align
|
||||
|
||||
Some machines require strict data alignment. The default is 8 bytes.
|
||||
User can customize by defining the RAPIDJSON_ALIGN function macro.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ALIGN
|
||||
#define RAPIDJSON_ALIGN(x) (((x) + static_cast<size_t>(7u)) & ~static_cast<size_t>(7u))
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_UINT64_C2
|
||||
|
||||
//! Construct a 64-bit literal by a pair of 32-bit integer.
|
||||
/*!
|
||||
64-bit literal with or without ULL suffix is prone to compiler warnings.
|
||||
UINT64_C() is C macro which cause compilation problems.
|
||||
Use this macro to define 64-bit constants by a pair of 32-bit integer.
|
||||
*/
|
||||
#ifndef RAPIDJSON_UINT64_C2
|
||||
#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32))
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_48BITPOINTER_OPTIMIZATION
|
||||
|
||||
//! Use only lower 48-bit address for some pointers.
|
||||
/*!
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
|
||||
This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address.
|
||||
The higher 16-bit can be used for storing other data.
|
||||
\c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture.
|
||||
*/
|
||||
#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION
|
||||
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
|
||||
#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1
|
||||
#else
|
||||
#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0
|
||||
#endif
|
||||
#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION
|
||||
|
||||
#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1
|
||||
#if RAPIDJSON_64BIT != 1
|
||||
#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1
|
||||
#endif
|
||||
#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast<type *>((reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast<uintptr_t>(reinterpret_cast<const void*>(x))))
|
||||
#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast<type *>(reinterpret_cast<uintptr_t>(p) & static_cast<uintptr_t>(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF))))
|
||||
#else
|
||||
#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x))
|
||||
#define RAPIDJSON_GETPOINTER(type, p) (p)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD
|
||||
|
||||
/*! \def RAPIDJSON_SIMD
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief Enable SSE2/SSE4.2/Neon optimization.
|
||||
|
||||
RapidJSON supports optimized implementations for some parsing operations
|
||||
based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel
|
||||
or ARM compatible processors.
|
||||
|
||||
To enable these optimizations, three different symbols can be defined;
|
||||
\code
|
||||
// Enable SSE2 optimization.
|
||||
#define RAPIDJSON_SSE2
|
||||
|
||||
// Enable SSE4.2 optimization.
|
||||
#define RAPIDJSON_SSE42
|
||||
\endcode
|
||||
|
||||
// Enable ARM Neon optimization.
|
||||
#define RAPIDJSON_NEON
|
||||
\endcode
|
||||
|
||||
\c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined.
|
||||
|
||||
If any of these symbols is defined, RapidJSON defines the macro
|
||||
\c RAPIDJSON_SIMD to indicate the availability of the optimized code.
|
||||
*/
|
||||
#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \
|
||||
|| defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING)
|
||||
#define RAPIDJSON_SIMD
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
|
||||
#ifndef RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
/*! \def RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief User-provided \c SizeType definition.
|
||||
|
||||
In order to avoid using 32-bit size types for indexing strings and arrays,
|
||||
define this preprocessor symbol and provide the type rapidjson::SizeType
|
||||
before including RapidJSON:
|
||||
\code
|
||||
#define RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
namespace rapidjson { typedef ::std::size_t SizeType; }
|
||||
#include "rapidjson/..."
|
||||
\endcode
|
||||
|
||||
\see rapidjson::SizeType
|
||||
*/
|
||||
#ifdef RAPIDJSON_DOXYGEN_RUNNING
|
||||
#define RAPIDJSON_NO_SIZETYPEDEFINE
|
||||
#endif
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
//! Size type (for string lengths, array sizes, etc.)
|
||||
/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms,
|
||||
instead of using \c size_t. Users may override the SizeType by defining
|
||||
\ref RAPIDJSON_NO_SIZETYPEDEFINE.
|
||||
*/
|
||||
typedef unsigned SizeType;
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
#endif
|
||||
|
||||
// always import std::size_t to rapidjson namespace
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
using std::size_t;
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_ASSERT
|
||||
|
||||
//! Assertion.
|
||||
/*! \ingroup RAPIDJSON_CONFIG
|
||||
By default, rapidjson uses C \c assert() for internal assertions.
|
||||
User can override it by defining RAPIDJSON_ASSERT(x) macro.
|
||||
|
||||
\note Parsing errors are handled and can be customized by the
|
||||
\ref RAPIDJSON_ERRORS APIs.
|
||||
*/
|
||||
#ifndef RAPIDJSON_ASSERT
|
||||
#include <cassert>
|
||||
#define RAPIDJSON_ASSERT(x) assert(x)
|
||||
#endif // RAPIDJSON_ASSERT
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_STATIC_ASSERT
|
||||
|
||||
// Prefer C++11 static_assert, if available
|
||||
#ifndef RAPIDJSON_STATIC_ASSERT
|
||||
#if RAPIDJSON_CPLUSPLUS >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 )
|
||||
#define RAPIDJSON_STATIC_ASSERT(x) \
|
||||
static_assert(x, RAPIDJSON_STRINGIFY(x))
|
||||
#endif // C++11
|
||||
#endif // RAPIDJSON_STATIC_ASSERT
|
||||
|
||||
// Adopt C++03 implementation from boost
|
||||
#ifndef RAPIDJSON_STATIC_ASSERT
|
||||
#ifndef __clang__
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
#endif
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
template <bool x> struct STATIC_ASSERTION_FAILURE;
|
||||
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
|
||||
template <size_t x> struct StaticAssertTest {};
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused))
|
||||
#else
|
||||
#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
|
||||
#endif
|
||||
#ifndef __clang__
|
||||
//!@endcond
|
||||
#endif
|
||||
|
||||
/*! \def RAPIDJSON_STATIC_ASSERT
|
||||
\brief (Internal) macro to check for conditions at compile-time
|
||||
\param x compile-time condition
|
||||
\hideinitializer
|
||||
*/
|
||||
#define RAPIDJSON_STATIC_ASSERT(x) \
|
||||
typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \
|
||||
sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \
|
||||
RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
|
||||
#endif // RAPIDJSON_STATIC_ASSERT
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY
|
||||
|
||||
//! Compiler branching hint for expression with high probability to be true.
|
||||
/*!
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\param x Boolean expression likely to be true.
|
||||
*/
|
||||
#ifndef RAPIDJSON_LIKELY
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
#else
|
||||
#define RAPIDJSON_LIKELY(x) (x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//! Compiler branching hint for expression with low probability to be true.
|
||||
/*!
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\param x Boolean expression unlikely to be true.
|
||||
*/
|
||||
#ifndef RAPIDJSON_UNLIKELY
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
#else
|
||||
#define RAPIDJSON_UNLIKELY(x) (x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers
|
||||
|
||||
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
|
||||
|
||||
#define RAPIDJSON_MULTILINEMACRO_BEGIN do {
|
||||
#define RAPIDJSON_MULTILINEMACRO_END \
|
||||
} while((void)0, 0)
|
||||
|
||||
// adopted from Boost
|
||||
#define RAPIDJSON_VERSION_CODE(x,y,z) \
|
||||
(((x)*100000) + ((y)*100) + (z))
|
||||
|
||||
#if defined(__has_builtin)
|
||||
#define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x)
|
||||
#else
|
||||
#define RAPIDJSON_HAS_BUILTIN(x) 0
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define RAPIDJSON_GNUC \
|
||||
RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__)
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0))
|
||||
|
||||
#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x))
|
||||
#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x)
|
||||
#define RAPIDJSON_DIAG_OFF(x) \
|
||||
RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x)))
|
||||
|
||||
// push/pop support in Clang and GCC>=4.6
|
||||
#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0))
|
||||
#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
|
||||
#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
|
||||
#else // GCC >= 4.2, < 4.6
|
||||
#define RAPIDJSON_DIAG_PUSH /* ignored */
|
||||
#define RAPIDJSON_DIAG_POP /* ignored */
|
||||
#endif
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
// pragma (MSVC specific)
|
||||
#define RAPIDJSON_PRAGMA(x) __pragma(x)
|
||||
#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x))
|
||||
|
||||
#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x)
|
||||
#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
|
||||
#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
|
||||
|
||||
#else
|
||||
|
||||
#define RAPIDJSON_DIAG_OFF(x) /* ignored */
|
||||
#define RAPIDJSON_DIAG_PUSH /* ignored */
|
||||
#define RAPIDJSON_DIAG_POP /* ignored */
|
||||
|
||||
#endif // RAPIDJSON_DIAG_*
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// C++11 features
|
||||
|
||||
#ifndef RAPIDJSON_HAS_CXX11
|
||||
#define RAPIDJSON_HAS_CXX11 (RAPIDJSON_CPLUSPLUS >= 201103L)
|
||||
#endif
|
||||
|
||||
#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
#if RAPIDJSON_HAS_CXX11
|
||||
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
|
||||
#elif defined(__clang__)
|
||||
#if __has_feature(cxx_rvalue_references) && \
|
||||
(defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306)
|
||||
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
|
||||
#else
|
||||
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
|
||||
#endif
|
||||
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
|
||||
(defined(_MSC_VER) && _MSC_VER >= 1600) || \
|
||||
(defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__))
|
||||
|
||||
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
|
||||
#else
|
||||
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
|
||||
#endif
|
||||
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
#include <utility> // std::move
|
||||
#endif
|
||||
|
||||
#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT
|
||||
#if RAPIDJSON_HAS_CXX11
|
||||
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
|
||||
#elif defined(__clang__)
|
||||
#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept)
|
||||
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
|
||||
(defined(_MSC_VER) && _MSC_VER >= 1900) || \
|
||||
(defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__))
|
||||
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
|
||||
#else
|
||||
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0
|
||||
#endif
|
||||
#endif
|
||||
#ifndef RAPIDJSON_NOEXCEPT
|
||||
#if RAPIDJSON_HAS_CXX11_NOEXCEPT
|
||||
#define RAPIDJSON_NOEXCEPT noexcept
|
||||
#else
|
||||
#define RAPIDJSON_NOEXCEPT throw()
|
||||
#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT
|
||||
#endif
|
||||
|
||||
// no automatic detection, yet
|
||||
#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS
|
||||
#if (defined(_MSC_VER) && _MSC_VER >= 1700)
|
||||
#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1
|
||||
#else
|
||||
#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR
|
||||
#if defined(__clang__)
|
||||
#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for)
|
||||
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
|
||||
(defined(_MSC_VER) && _MSC_VER >= 1700) || \
|
||||
(defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__))
|
||||
#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1
|
||||
#else
|
||||
#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0
|
||||
#endif
|
||||
#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// C++17 features
|
||||
|
||||
#ifndef RAPIDJSON_HAS_CXX17
|
||||
#define RAPIDJSON_HAS_CXX17 (RAPIDJSON_CPLUSPLUS >= 201703L)
|
||||
#endif
|
||||
|
||||
#if RAPIDJSON_HAS_CXX17
|
||||
# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]]
|
||||
#elif defined(__has_cpp_attribute)
|
||||
# if __has_cpp_attribute(clang::fallthrough)
|
||||
# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[clang::fallthrough]]
|
||||
# elif __has_cpp_attribute(fallthrough)
|
||||
# define RAPIDJSON_DELIBERATE_FALLTHROUGH __attribute__((fallthrough))
|
||||
# else
|
||||
# define RAPIDJSON_DELIBERATE_FALLTHROUGH
|
||||
# endif
|
||||
#else
|
||||
# define RAPIDJSON_DELIBERATE_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
//!@endcond
|
||||
|
||||
//! Assertion (in non-throwing contexts).
|
||||
/*! \ingroup RAPIDJSON_CONFIG
|
||||
Some functions provide a \c noexcept guarantee, if the compiler supports it.
|
||||
In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to
|
||||
throw an exception. This macro adds a separate customization point for
|
||||
such cases.
|
||||
|
||||
Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is
|
||||
supported, and to \ref RAPIDJSON_ASSERT otherwise.
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_NOEXCEPT_ASSERT
|
||||
|
||||
#ifndef RAPIDJSON_NOEXCEPT_ASSERT
|
||||
#ifdef RAPIDJSON_ASSERT_THROWS
|
||||
#include <cassert>
|
||||
#define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x)
|
||||
#else
|
||||
#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x)
|
||||
#endif // RAPIDJSON_ASSERT_THROWS
|
||||
#endif // RAPIDJSON_NOEXCEPT_ASSERT
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// malloc/realloc/free
|
||||
|
||||
#ifndef RAPIDJSON_MALLOC
|
||||
///! customization point for global \c malloc
|
||||
#define RAPIDJSON_MALLOC(size) std::malloc(size)
|
||||
#endif
|
||||
#ifndef RAPIDJSON_REALLOC
|
||||
///! customization point for global \c realloc
|
||||
#define RAPIDJSON_REALLOC(ptr, new_size) std::realloc(ptr, new_size)
|
||||
#endif
|
||||
#ifndef RAPIDJSON_FREE
|
||||
///! customization point for global \c free
|
||||
#define RAPIDJSON_FREE(ptr) std::free(ptr)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// new/delete
|
||||
|
||||
#ifndef RAPIDJSON_NEW
|
||||
///! customization point for global \c new
|
||||
#define RAPIDJSON_NEW(TypeName) new TypeName
|
||||
#endif
|
||||
#ifndef RAPIDJSON_DELETE
|
||||
///! customization point for global \c delete
|
||||
#define RAPIDJSON_DELETE(x) delete x
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Type
|
||||
|
||||
/*! \namespace rapidjson
|
||||
\brief main RapidJSON namespace
|
||||
\see RAPIDJSON_NAMESPACE
|
||||
*/
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Type of JSON value
|
||||
enum Type {
|
||||
kNullType = 0, //!< null
|
||||
kFalseType = 1, //!< false
|
||||
kTrueType = 2, //!< true
|
||||
kObjectType = 3, //!< object
|
||||
kArrayType = 4, //!< array
|
||||
kStringType = 5, //!< string
|
||||
kNumberType = 6 //!< number
|
||||
};
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSON_H_
|
2246
include/rapidjson/reader.h
Normal file
2246
include/rapidjson/reader.h
Normal file
File diff suppressed because it is too large
Load Diff
3262
include/rapidjson/schema.h
Normal file
3262
include/rapidjson/schema.h
Normal file
File diff suppressed because it is too large
Load Diff
223
include/rapidjson/stream.h
Normal file
223
include/rapidjson/stream.h
Normal file
@ -0,0 +1,223 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#include "rapidjson.h"
|
||||
|
||||
#ifndef RAPIDJSON_STREAM_H_
|
||||
#define RAPIDJSON_STREAM_H_
|
||||
|
||||
#include "encodings.h"
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Stream
|
||||
|
||||
/*! \class rapidjson::Stream
|
||||
\brief Concept for reading and writing characters.
|
||||
|
||||
For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().
|
||||
|
||||
For write-only stream, only need to implement Put() and Flush().
|
||||
|
||||
\code
|
||||
concept Stream {
|
||||
typename Ch; //!< Character type of the stream.
|
||||
|
||||
//! Read the current character from stream without moving the read cursor.
|
||||
Ch Peek() const;
|
||||
|
||||
//! Read the current character from stream and moving the read cursor to next character.
|
||||
Ch Take();
|
||||
|
||||
//! Get the current read cursor.
|
||||
//! \return Number of characters read from start.
|
||||
size_t Tell();
|
||||
|
||||
//! Begin writing operation at the current read pointer.
|
||||
//! \return The begin writer pointer.
|
||||
Ch* PutBegin();
|
||||
|
||||
//! Write a character.
|
||||
void Put(Ch c);
|
||||
|
||||
//! Flush the buffer.
|
||||
void Flush();
|
||||
|
||||
//! End the writing operation.
|
||||
//! \param begin The begin write pointer returned by PutBegin().
|
||||
//! \return Number of characters written.
|
||||
size_t PutEnd(Ch* begin);
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
|
||||
//! Provides additional information for stream.
|
||||
/*!
|
||||
By using traits pattern, this type provides a default configuration for stream.
|
||||
For custom stream, this type can be specialized for other configuration.
|
||||
See TEST(Reader, CustomStringStream) in readertest.cpp for example.
|
||||
*/
|
||||
template<typename Stream>
|
||||
struct StreamTraits {
|
||||
//! Whether to make local copy of stream for optimization during parsing.
|
||||
/*!
|
||||
By default, for safety, streams do not use local copy optimization.
|
||||
Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
|
||||
*/
|
||||
enum { copyOptimization = 0 };
|
||||
};
|
||||
|
||||
//! Reserve n characters for writing to a stream.
|
||||
template<typename Stream>
|
||||
inline void PutReserve(Stream& stream, size_t count) {
|
||||
(void)stream;
|
||||
(void)count;
|
||||
}
|
||||
|
||||
//! Write character to a stream, presuming buffer is reserved.
|
||||
template<typename Stream>
|
||||
inline void PutUnsafe(Stream& stream, typename Stream::Ch c) {
|
||||
stream.Put(c);
|
||||
}
|
||||
|
||||
//! Put N copies of a character to a stream.
|
||||
template<typename Stream, typename Ch>
|
||||
inline void PutN(Stream& stream, Ch c, size_t n) {
|
||||
PutReserve(stream, n);
|
||||
for (size_t i = 0; i < n; i++)
|
||||
PutUnsafe(stream, c);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GenericStreamWrapper
|
||||
|
||||
//! A Stream Wrapper
|
||||
/*! \tThis string stream is a wrapper for any stream by just forwarding any
|
||||
\treceived message to the origin stream.
|
||||
\note implements Stream concept
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4702) // unreachable code
|
||||
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
||||
#endif
|
||||
|
||||
template <typename InputStream, typename Encoding = UTF8<> >
|
||||
class GenericStreamWrapper {
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
GenericStreamWrapper(InputStream& is): is_(is) {}
|
||||
|
||||
Ch Peek() const { return is_.Peek(); }
|
||||
Ch Take() { return is_.Take(); }
|
||||
size_t Tell() { return is_.Tell(); }
|
||||
Ch* PutBegin() { return is_.PutBegin(); }
|
||||
void Put(Ch ch) { is_.Put(ch); }
|
||||
void Flush() { is_.Flush(); }
|
||||
size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); }
|
||||
|
||||
// wrapper for MemoryStream
|
||||
const Ch* Peek4() const { return is_.Peek4(); }
|
||||
|
||||
// wrapper for AutoUTFInputStream
|
||||
UTFType GetType() const { return is_.GetType(); }
|
||||
bool HasBOM() const { return is_.HasBOM(); }
|
||||
|
||||
protected:
|
||||
InputStream& is_;
|
||||
};
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// StringStream
|
||||
|
||||
//! Read-only string stream.
|
||||
/*! \note implements Stream concept
|
||||
*/
|
||||
template <typename Encoding>
|
||||
struct GenericStringStream {
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericStringStream(const Ch *src) : src_(src), head_(src) {}
|
||||
|
||||
Ch Peek() const { return *src_; }
|
||||
Ch Take() { return *src_++; }
|
||||
size_t Tell() const { return static_cast<size_t>(src_ - head_); }
|
||||
|
||||
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
|
||||
void Put(Ch) { RAPIDJSON_ASSERT(false); }
|
||||
void Flush() { RAPIDJSON_ASSERT(false); }
|
||||
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
|
||||
|
||||
const Ch* src_; //!< Current read position.
|
||||
const Ch* head_; //!< Original head of the string.
|
||||
};
|
||||
|
||||
template <typename Encoding>
|
||||
struct StreamTraits<GenericStringStream<Encoding> > {
|
||||
enum { copyOptimization = 1 };
|
||||
};
|
||||
|
||||
//! String stream with UTF8 encoding.
|
||||
typedef GenericStringStream<UTF8<> > StringStream;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// InsituStringStream
|
||||
|
||||
//! A read-write string stream.
|
||||
/*! This string stream is particularly designed for in-situ parsing.
|
||||
\note implements Stream concept
|
||||
*/
|
||||
template <typename Encoding>
|
||||
struct GenericInsituStringStream {
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}
|
||||
|
||||
// Read
|
||||
Ch Peek() { return *src_; }
|
||||
Ch Take() { return *src_++; }
|
||||
size_t Tell() { return static_cast<size_t>(src_ - head_); }
|
||||
|
||||
// Write
|
||||
void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
|
||||
|
||||
Ch* PutBegin() { return dst_ = src_; }
|
||||
size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); }
|
||||
void Flush() {}
|
||||
|
||||
Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; }
|
||||
void Pop(size_t count) { dst_ -= count; }
|
||||
|
||||
Ch* src_;
|
||||
Ch* dst_;
|
||||
Ch* head_;
|
||||
};
|
||||
|
||||
template <typename Encoding>
|
||||
struct StreamTraits<GenericInsituStringStream<Encoding> > {
|
||||
enum { copyOptimization = 1 };
|
||||
};
|
||||
|
||||
//! Insitu string stream with UTF8 encoding.
|
||||
typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#endif // RAPIDJSON_STREAM_H_
|
121
include/rapidjson/stringbuffer.h
Normal file
121
include/rapidjson/stringbuffer.h
Normal file
@ -0,0 +1,121 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_STRINGBUFFER_H_
|
||||
#define RAPIDJSON_STRINGBUFFER_H_
|
||||
|
||||
#include "stream.h"
|
||||
#include "internal/stack.h"
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
#include <utility> // std::move
|
||||
#endif
|
||||
|
||||
#include "internal/stack.h"
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
//! Represents an in-memory output stream.
|
||||
/*!
|
||||
\tparam Encoding Encoding of the stream.
|
||||
\tparam Allocator type for allocating memory buffer.
|
||||
\note implements Stream concept
|
||||
*/
|
||||
template <typename Encoding, typename Allocator = CrtAllocator>
|
||||
class GenericStringBuffer {
|
||||
public:
|
||||
typedef typename Encoding::Ch Ch;
|
||||
|
||||
GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {}
|
||||
GenericStringBuffer& operator=(GenericStringBuffer&& rhs) {
|
||||
if (&rhs != this)
|
||||
stack_ = std::move(rhs.stack_);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Put(Ch c) { *stack_.template Push<Ch>() = c; }
|
||||
void PutUnsafe(Ch c) { *stack_.template PushUnsafe<Ch>() = c; }
|
||||
void Flush() {}
|
||||
|
||||
void Clear() { stack_.Clear(); }
|
||||
void ShrinkToFit() {
|
||||
// Push and pop a null terminator. This is safe.
|
||||
*stack_.template Push<Ch>() = '\0';
|
||||
stack_.ShrinkToFit();
|
||||
stack_.template Pop<Ch>(1);
|
||||
}
|
||||
|
||||
void Reserve(size_t count) { stack_.template Reserve<Ch>(count); }
|
||||
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
|
||||
Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe<Ch>(count); }
|
||||
void Pop(size_t count) { stack_.template Pop<Ch>(count); }
|
||||
|
||||
const Ch* GetString() const {
|
||||
// Push and pop a null terminator. This is safe.
|
||||
*stack_.template Push<Ch>() = '\0';
|
||||
stack_.template Pop<Ch>(1);
|
||||
|
||||
return stack_.template Bottom<Ch>();
|
||||
}
|
||||
|
||||
//! Get the size of string in bytes in the string buffer.
|
||||
size_t GetSize() const { return stack_.GetSize(); }
|
||||
|
||||
//! Get the length of string in Ch in the string buffer.
|
||||
size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); }
|
||||
|
||||
static const size_t kDefaultCapacity = 256;
|
||||
mutable internal::Stack<Allocator> stack_;
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
GenericStringBuffer(const GenericStringBuffer&);
|
||||
GenericStringBuffer& operator=(const GenericStringBuffer&);
|
||||
};
|
||||
|
||||
//! String buffer with UTF8 encoding
|
||||
typedef GenericStringBuffer<UTF8<> > StringBuffer;
|
||||
|
||||
template<typename Encoding, typename Allocator>
|
||||
inline void PutReserve(GenericStringBuffer<Encoding, Allocator>& stream, size_t count) {
|
||||
stream.Reserve(count);
|
||||
}
|
||||
|
||||
template<typename Encoding, typename Allocator>
|
||||
inline void PutUnsafe(GenericStringBuffer<Encoding, Allocator>& stream, typename Encoding::Ch c) {
|
||||
stream.PutUnsafe(c);
|
||||
}
|
||||
|
||||
//! Implement specialized version of PutN() with memset() for better performance.
|
||||
template<>
|
||||
inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
|
||||
std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_STRINGBUFFER_H_
|
481
include/rapidjson/uri.h
Normal file
481
include/rapidjson/uri.h
Normal file
@ -0,0 +1,481 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// (C) Copyright IBM Corporation 2021
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_URI_H_
|
||||
#define RAPIDJSON_URI_H_
|
||||
|
||||
#include "internal/strfunc.h"
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#elif defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GenericUri
|
||||
|
||||
template <typename ValueType, typename Allocator=CrtAllocator>
|
||||
class GenericUri {
|
||||
public:
|
||||
typedef typename ValueType::Ch Ch;
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
typedef std::basic_string<Ch> String;
|
||||
#endif
|
||||
|
||||
//! Constructors
|
||||
GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
||||
}
|
||||
|
||||
GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
||||
Parse(uri, len);
|
||||
}
|
||||
|
||||
GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
||||
Parse(uri, internal::StrLen<Ch>(uri));
|
||||
}
|
||||
|
||||
// Use with specializations of GenericValue
|
||||
template<typename T> GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
||||
const Ch* u = uri.template Get<const Ch*>(); // TypeHelper from document.h
|
||||
Parse(u, internal::StrLen<Ch>(u));
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
||||
Parse(uri.c_str(), internal::StrLen<Ch>(uri.c_str()));
|
||||
}
|
||||
#endif
|
||||
|
||||
//! Copy constructor
|
||||
GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(), ownAllocator_() {
|
||||
*this = rhs;
|
||||
}
|
||||
|
||||
//! Copy constructor
|
||||
GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() {
|
||||
*this = rhs;
|
||||
}
|
||||
|
||||
//! Destructor.
|
||||
~GenericUri() {
|
||||
Free();
|
||||
RAPIDJSON_DELETE(ownAllocator_);
|
||||
}
|
||||
|
||||
//! Assignment operator
|
||||
GenericUri& operator=(const GenericUri& rhs) {
|
||||
if (this != &rhs) {
|
||||
// Do not delete ownAllocator
|
||||
Free();
|
||||
Allocate(rhs.GetStringLength());
|
||||
auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength());
|
||||
path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength());
|
||||
query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength());
|
||||
frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength());
|
||||
base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength());
|
||||
uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength());
|
||||
CopyPart(uri_, rhs.uri_, rhs.GetStringLength());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
//! Getters
|
||||
// Use with specializations of GenericValue
|
||||
template<typename T> void Get(T& uri, Allocator& allocator) {
|
||||
uri.template Set<const Ch*>(this->GetString(), allocator); // TypeHelper from document.h
|
||||
}
|
||||
|
||||
const Ch* GetString() const { return uri_; }
|
||||
SizeType GetStringLength() const { return uri_ == 0 ? 0 : internal::StrLen<Ch>(uri_); }
|
||||
const Ch* GetBaseString() const { return base_; }
|
||||
SizeType GetBaseStringLength() const { return base_ == 0 ? 0 : internal::StrLen<Ch>(base_); }
|
||||
const Ch* GetSchemeString() const { return scheme_; }
|
||||
SizeType GetSchemeStringLength() const { return scheme_ == 0 ? 0 : internal::StrLen<Ch>(scheme_); }
|
||||
const Ch* GetAuthString() const { return auth_; }
|
||||
SizeType GetAuthStringLength() const { return auth_ == 0 ? 0 : internal::StrLen<Ch>(auth_); }
|
||||
const Ch* GetPathString() const { return path_; }
|
||||
SizeType GetPathStringLength() const { return path_ == 0 ? 0 : internal::StrLen<Ch>(path_); }
|
||||
const Ch* GetQueryString() const { return query_; }
|
||||
SizeType GetQueryStringLength() const { return query_ == 0 ? 0 : internal::StrLen<Ch>(query_); }
|
||||
const Ch* GetFragString() const { return frag_; }
|
||||
SizeType GetFragStringLength() const { return frag_ == 0 ? 0 : internal::StrLen<Ch>(frag_); }
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); }
|
||||
static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); }
|
||||
static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); }
|
||||
static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); }
|
||||
static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); }
|
||||
static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); }
|
||||
static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); }
|
||||
#endif
|
||||
|
||||
//! Equality operators
|
||||
bool operator==(const GenericUri& rhs) const {
|
||||
return Match(rhs, true);
|
||||
}
|
||||
|
||||
bool operator!=(const GenericUri& rhs) const {
|
||||
return !Match(rhs, true);
|
||||
}
|
||||
|
||||
bool Match(const GenericUri& uri, bool full = true) const {
|
||||
Ch* s1;
|
||||
Ch* s2;
|
||||
if (full) {
|
||||
s1 = uri_;
|
||||
s2 = uri.uri_;
|
||||
} else {
|
||||
s1 = base_;
|
||||
s2 = uri.base_;
|
||||
}
|
||||
if (s1 == s2) return true;
|
||||
if (s1 == 0 || s2 == 0) return false;
|
||||
return internal::StrCmp<Ch>(s1, s2) == 0;
|
||||
}
|
||||
|
||||
//! Resolve this URI against another (base) URI in accordance with URI resolution rules.
|
||||
// See https://tools.ietf.org/html/rfc3986
|
||||
// Use for resolving an id or $ref with an in-scope id.
|
||||
// Returns a new GenericUri for the resolved URI.
|
||||
GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) {
|
||||
GenericUri resuri;
|
||||
resuri.allocator_ = allocator;
|
||||
// Ensure enough space for combining paths
|
||||
resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash
|
||||
|
||||
if (!(GetSchemeStringLength() == 0)) {
|
||||
// Use all of this URI
|
||||
resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength());
|
||||
resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength());
|
||||
resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
|
||||
resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
|
||||
resuri.RemoveDotSegments();
|
||||
} else {
|
||||
// Use the base scheme
|
||||
resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength());
|
||||
if (!(GetAuthStringLength() == 0)) {
|
||||
// Use this auth, path, query
|
||||
resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength());
|
||||
resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
|
||||
resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
|
||||
resuri.RemoveDotSegments();
|
||||
} else {
|
||||
// Use the base auth
|
||||
resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength());
|
||||
if (GetPathStringLength() == 0) {
|
||||
// Use the base path
|
||||
resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength());
|
||||
if (GetQueryStringLength() == 0) {
|
||||
// Use the base query
|
||||
resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength());
|
||||
} else {
|
||||
// Use this query
|
||||
resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
|
||||
}
|
||||
} else {
|
||||
if (path_[0] == '/') {
|
||||
// Absolute path - use all of this path
|
||||
resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength());
|
||||
resuri.RemoveDotSegments();
|
||||
} else {
|
||||
// Relative path - append this path to base path after base path's last slash
|
||||
size_t pos = 0;
|
||||
if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) {
|
||||
resuri.path_[pos] = '/';
|
||||
pos++;
|
||||
}
|
||||
size_t lastslashpos = baseuri.GetPathStringLength();
|
||||
while (lastslashpos > 0) {
|
||||
if (baseuri.path_[lastslashpos - 1] == '/') break;
|
||||
lastslashpos--;
|
||||
}
|
||||
std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch));
|
||||
pos += lastslashpos;
|
||||
resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength());
|
||||
resuri.RemoveDotSegments();
|
||||
}
|
||||
// Use this query
|
||||
resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Always use this frag
|
||||
resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength());
|
||||
|
||||
// Re-constitute base_ and uri_
|
||||
resuri.SetBase();
|
||||
resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1;
|
||||
resuri.SetUri();
|
||||
return resuri;
|
||||
}
|
||||
|
||||
//! Get the allocator of this GenericUri.
|
||||
Allocator& GetAllocator() { return *allocator_; }
|
||||
|
||||
private:
|
||||
// Allocate memory for a URI
|
||||
// Returns total amount allocated
|
||||
std::size_t Allocate(std::size_t len) {
|
||||
// Create own allocator if user did not supply.
|
||||
if (!allocator_)
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
||||
|
||||
// Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated.
|
||||
// Order: scheme, auth, path, query, frag, base, uri
|
||||
// Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
|
||||
size_t total = (3 * len + 7) * sizeof(Ch);
|
||||
scheme_ = static_cast<Ch*>(allocator_->Malloc(total));
|
||||
*scheme_ = '\0';
|
||||
auth_ = scheme_;
|
||||
auth_++;
|
||||
*auth_ = '\0';
|
||||
path_ = auth_;
|
||||
path_++;
|
||||
*path_ = '\0';
|
||||
query_ = path_;
|
||||
query_++;
|
||||
*query_ = '\0';
|
||||
frag_ = query_;
|
||||
frag_++;
|
||||
*frag_ = '\0';
|
||||
base_ = frag_;
|
||||
base_++;
|
||||
*base_ = '\0';
|
||||
uri_ = base_;
|
||||
uri_++;
|
||||
*uri_ = '\0';
|
||||
return total;
|
||||
}
|
||||
|
||||
// Free memory for a URI
|
||||
void Free() {
|
||||
if (scheme_) {
|
||||
Allocator::Free(scheme_);
|
||||
scheme_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a URI into constituent scheme, authority, path, query, & fragment parts
|
||||
// Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per
|
||||
// https://tools.ietf.org/html/rfc3986
|
||||
void Parse(const Ch* uri, std::size_t len) {
|
||||
std::size_t start = 0, pos1 = 0, pos2 = 0;
|
||||
Allocate(len);
|
||||
|
||||
// Look for scheme ([^:/?#]+):)?
|
||||
if (start < len) {
|
||||
while (pos1 < len) {
|
||||
if (uri[pos1] == ':') break;
|
||||
pos1++;
|
||||
}
|
||||
if (pos1 != len) {
|
||||
while (pos2 < len) {
|
||||
if (uri[pos2] == '/') break;
|
||||
if (uri[pos2] == '?') break;
|
||||
if (uri[pos2] == '#') break;
|
||||
pos2++;
|
||||
}
|
||||
if (pos1 < pos2) {
|
||||
pos1++;
|
||||
std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch));
|
||||
scheme_[pos1] = '\0';
|
||||
start = pos1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Look for auth (//([^/?#]*))?
|
||||
// Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
|
||||
auth_ = scheme_ + GetSchemeStringLength();
|
||||
auth_++;
|
||||
*auth_ = '\0';
|
||||
if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') {
|
||||
pos2 = start + 2;
|
||||
while (pos2 < len) {
|
||||
if (uri[pos2] == '/') break;
|
||||
if (uri[pos2] == '?') break;
|
||||
if (uri[pos2] == '#') break;
|
||||
pos2++;
|
||||
}
|
||||
std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch));
|
||||
auth_[pos2 - start] = '\0';
|
||||
start = pos2;
|
||||
}
|
||||
// Look for path ([^?#]*)
|
||||
// Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
|
||||
path_ = auth_ + GetAuthStringLength();
|
||||
path_++;
|
||||
*path_ = '\0';
|
||||
if (start < len) {
|
||||
pos2 = start;
|
||||
while (pos2 < len) {
|
||||
if (uri[pos2] == '?') break;
|
||||
if (uri[pos2] == '#') break;
|
||||
pos2++;
|
||||
}
|
||||
if (start != pos2) {
|
||||
std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch));
|
||||
path_[pos2 - start] = '\0';
|
||||
if (path_[0] == '/')
|
||||
RemoveDotSegments(); // absolute path - normalize
|
||||
start = pos2;
|
||||
}
|
||||
}
|
||||
// Look for query (\?([^#]*))?
|
||||
// Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
|
||||
query_ = path_ + GetPathStringLength();
|
||||
query_++;
|
||||
*query_ = '\0';
|
||||
if (start < len && uri[start] == '?') {
|
||||
pos2 = start + 1;
|
||||
while (pos2 < len) {
|
||||
if (uri[pos2] == '#') break;
|
||||
pos2++;
|
||||
}
|
||||
if (start != pos2) {
|
||||
std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch));
|
||||
query_[pos2 - start] = '\0';
|
||||
start = pos2;
|
||||
}
|
||||
}
|
||||
// Look for fragment (#(.*))?
|
||||
// Note need to set, increment, assign in 3 stages to avoid compiler warning bug.
|
||||
frag_ = query_ + GetQueryStringLength();
|
||||
frag_++;
|
||||
*frag_ = '\0';
|
||||
if (start < len && uri[start] == '#') {
|
||||
std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch));
|
||||
frag_[len - start] = '\0';
|
||||
}
|
||||
|
||||
// Re-constitute base_ and uri_
|
||||
base_ = frag_ + GetFragStringLength() + 1;
|
||||
SetBase();
|
||||
uri_ = base_ + GetBaseStringLength() + 1;
|
||||
SetUri();
|
||||
}
|
||||
|
||||
// Reconstitute base
|
||||
void SetBase() {
|
||||
Ch* next = base_;
|
||||
std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch));
|
||||
next+= GetSchemeStringLength();
|
||||
std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch));
|
||||
next+= GetAuthStringLength();
|
||||
std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch));
|
||||
next+= GetPathStringLength();
|
||||
std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch));
|
||||
next+= GetQueryStringLength();
|
||||
*next = '\0';
|
||||
}
|
||||
|
||||
// Reconstitute uri
|
||||
void SetUri() {
|
||||
Ch* next = uri_;
|
||||
std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch));
|
||||
next+= GetBaseStringLength();
|
||||
std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch));
|
||||
next+= GetFragStringLength();
|
||||
*next = '\0';
|
||||
}
|
||||
|
||||
// Copy a part from one GenericUri to another
|
||||
// Return the pointer to the next part to be copied to
|
||||
Ch* CopyPart(Ch* to, Ch* from, std::size_t len) {
|
||||
RAPIDJSON_ASSERT(to != 0);
|
||||
RAPIDJSON_ASSERT(from != 0);
|
||||
std::memcpy(to, from, len * sizeof(Ch));
|
||||
to[len] = '\0';
|
||||
Ch* next = to + len + 1;
|
||||
return next;
|
||||
}
|
||||
|
||||
// Remove . and .. segments from the path_ member.
|
||||
// https://tools.ietf.org/html/rfc3986
|
||||
// This is done in place as we are only removing segments.
|
||||
void RemoveDotSegments() {
|
||||
std::size_t pathlen = GetPathStringLength();
|
||||
std::size_t pathpos = 0; // Position in path_
|
||||
std::size_t newpos = 0; // Position in new path_
|
||||
|
||||
// Loop through each segment in original path_
|
||||
while (pathpos < pathlen) {
|
||||
// Get next segment, bounded by '/' or end
|
||||
size_t slashpos = 0;
|
||||
while ((pathpos + slashpos) < pathlen) {
|
||||
if (path_[pathpos + slashpos] == '/') break;
|
||||
slashpos++;
|
||||
}
|
||||
// Check for .. and . segments
|
||||
if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') {
|
||||
// Backup a .. segment in the new path_
|
||||
// We expect to find a previously added slash at the end or nothing
|
||||
RAPIDJSON_ASSERT(newpos == 0 || path_[newpos - 1] == '/');
|
||||
size_t lastslashpos = newpos;
|
||||
// Make sure we don't go beyond the start segment
|
||||
if (lastslashpos > 1) {
|
||||
// Find the next to last slash and back up to it
|
||||
lastslashpos--;
|
||||
while (lastslashpos > 0) {
|
||||
if (path_[lastslashpos - 1] == '/') break;
|
||||
lastslashpos--;
|
||||
}
|
||||
// Set the new path_ position
|
||||
newpos = lastslashpos;
|
||||
}
|
||||
} else if (slashpos == 1 && path_[pathpos] == '.') {
|
||||
// Discard . segment, leaves new path_ unchanged
|
||||
} else {
|
||||
// Move any other kind of segment to the new path_
|
||||
RAPIDJSON_ASSERT(newpos <= pathpos);
|
||||
std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch));
|
||||
newpos += slashpos;
|
||||
// Add slash if not at end
|
||||
if ((pathpos + slashpos) < pathlen) {
|
||||
path_[newpos] = '/';
|
||||
newpos++;
|
||||
}
|
||||
}
|
||||
// Move to next segment
|
||||
pathpos += slashpos + 1;
|
||||
}
|
||||
path_[newpos] = '\0';
|
||||
}
|
||||
|
||||
Ch* uri_; // Everything
|
||||
Ch* base_; // Everything except fragment
|
||||
Ch* scheme_; // Includes the :
|
||||
Ch* auth_; // Includes the //
|
||||
Ch* path_; // Absolute if starts with /
|
||||
Ch* query_; // Includes the ?
|
||||
Ch* frag_; // Includes the #
|
||||
|
||||
Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_.
|
||||
Allocator* ownAllocator_; //!< Allocator owned by this Uri.
|
||||
};
|
||||
|
||||
//! GenericUri for Value (UTF-8, default allocator).
|
||||
typedef GenericUri<Value> Uri;
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_URI_H_
|
721
include/rapidjson/writer.h
Normal file
721
include/rapidjson/writer.h
Normal file
@ -0,0 +1,721 @@
|
||||
// Tencent is pleased to support the open source community by making RapidJSON available.
|
||||
//
|
||||
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
|
||||
//
|
||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
||||
// in compliance with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://opensource.org/licenses/MIT
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef RAPIDJSON_WRITER_H_
|
||||
#define RAPIDJSON_WRITER_H_
|
||||
|
||||
#include "stream.h"
|
||||
#include "internal/clzll.h"
|
||||
#include "internal/meta.h"
|
||||
#include "internal/stack.h"
|
||||
#include "internal/strfunc.h"
|
||||
#include "internal/dtoa.h"
|
||||
#include "internal/itoa.h"
|
||||
#include "stringbuffer.h"
|
||||
#include <new> // placement new
|
||||
|
||||
#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER)
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_BitScanForward)
|
||||
#endif
|
||||
#ifdef RAPIDJSON_SSE42
|
||||
#include <nmmintrin.h>
|
||||
#elif defined(RAPIDJSON_SSE2)
|
||||
#include <emmintrin.h>
|
||||
#elif defined(RAPIDJSON_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(padded)
|
||||
RAPIDJSON_DIAG_OFF(unreachable-code)
|
||||
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||
#elif defined(_MSC_VER)
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
|
||||
#endif
|
||||
|
||||
RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// WriteFlag
|
||||
|
||||
/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS
|
||||
\ingroup RAPIDJSON_CONFIG
|
||||
\brief User-defined kWriteDefaultFlags definition.
|
||||
|
||||
User can define this as any \c WriteFlag combinations.
|
||||
*/
|
||||
#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS
|
||||
#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags
|
||||
#endif
|
||||
|
||||
//! Combination of writeFlags
|
||||
enum WriteFlag {
|
||||
kWriteNoFlags = 0, //!< No flags are set.
|
||||
kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings.
|
||||
kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN.
|
||||
kWriteNanAndInfNullFlag = 4, //!< Allow writing of Infinity, -Infinity and NaN as null.
|
||||
kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS
|
||||
};
|
||||
|
||||
//! JSON writer
|
||||
/*! Writer implements the concept Handler.
|
||||
It generates JSON text by events to an output os.
|
||||
|
||||
User may programmatically calls the functions of a writer to generate JSON text.
|
||||
|
||||
On the other side, a writer can also be passed to objects that generates events,
|
||||
|
||||
for example Reader::Parse() and Document::Accept().
|
||||
|
||||
\tparam OutputStream Type of output stream.
|
||||
\tparam SourceEncoding Encoding of source string.
|
||||
\tparam TargetEncoding Encoding of output stream.
|
||||
\tparam StackAllocator Type of allocator for allocating memory of stack.
|
||||
\note implements Handler concept
|
||||
*/
|
||||
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags>
|
||||
class Writer {
|
||||
public:
|
||||
typedef typename SourceEncoding::Ch Ch;
|
||||
|
||||
static const int kDefaultMaxDecimalPlaces = 324;
|
||||
|
||||
//! Constructor
|
||||
/*! \param os Output stream.
|
||||
\param stackAllocator User supplied allocator. If it is null, it will create a private one.
|
||||
\param levelDepth Initial capacity of stack.
|
||||
*/
|
||||
explicit
|
||||
Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) :
|
||||
os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
|
||||
|
||||
explicit
|
||||
Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
|
||||
os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {}
|
||||
|
||||
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||
Writer(Writer&& rhs) :
|
||||
os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) {
|
||||
rhs.os_ = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! Reset the writer with a new stream.
|
||||
/*!
|
||||
This function reset the writer with a new stream and default settings,
|
||||
in order to make a Writer object reusable for output multiple JSONs.
|
||||
|
||||
\param os New output stream.
|
||||
\code
|
||||
Writer<OutputStream> writer(os1);
|
||||
writer.StartObject();
|
||||
// ...
|
||||
writer.EndObject();
|
||||
|
||||
writer.Reset(os2);
|
||||
writer.StartObject();
|
||||
// ...
|
||||
writer.EndObject();
|
||||
\endcode
|
||||
*/
|
||||
void Reset(OutputStream& os) {
|
||||
os_ = &os;
|
||||
hasRoot_ = false;
|
||||
level_stack_.Clear();
|
||||
}
|
||||
|
||||
//! Checks whether the output is a complete JSON.
|
||||
/*!
|
||||
A complete JSON has a complete root object or array.
|
||||
*/
|
||||
bool IsComplete() const {
|
||||
return hasRoot_ && level_stack_.Empty();
|
||||
}
|
||||
|
||||
int GetMaxDecimalPlaces() const {
|
||||
return maxDecimalPlaces_;
|
||||
}
|
||||
|
||||
//! Sets the maximum number of decimal places for double output.
|
||||
/*!
|
||||
This setting truncates the output with specified number of decimal places.
|
||||
|
||||
For example,
|
||||
|
||||
\code
|
||||
writer.SetMaxDecimalPlaces(3);
|
||||
writer.StartArray();
|
||||
writer.Double(0.12345); // "0.123"
|
||||
writer.Double(0.0001); // "0.0"
|
||||
writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent)
|
||||
writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent)
|
||||
writer.EndArray();
|
||||
\endcode
|
||||
|
||||
The default setting does not truncate any decimal places. You can restore to this setting by calling
|
||||
\code
|
||||
writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces);
|
||||
\endcode
|
||||
*/
|
||||
void SetMaxDecimalPlaces(int maxDecimalPlaces) {
|
||||
maxDecimalPlaces_ = maxDecimalPlaces;
|
||||
}
|
||||
|
||||
/*!@name Implementation of Handler
|
||||
\see Handler
|
||||
*/
|
||||
//@{
|
||||
|
||||
bool Null() { Prefix(kNullType); return EndValue(WriteNull()); }
|
||||
bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); }
|
||||
bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); }
|
||||
bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); }
|
||||
bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); }
|
||||
bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); }
|
||||
|
||||
//! Writes the given \c double value to the stream
|
||||
/*!
|
||||
\param d The value to be written.
|
||||
\return Whether it is succeed.
|
||||
*/
|
||||
bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); }
|
||||
|
||||
bool RawNumber(const Ch* str, SizeType length, bool copy = false) {
|
||||
RAPIDJSON_ASSERT(str != 0);
|
||||
(void)copy;
|
||||
Prefix(kNumberType);
|
||||
return EndValue(WriteString(str, length));
|
||||
}
|
||||
|
||||
bool String(const Ch* str, SizeType length, bool copy = false) {
|
||||
RAPIDJSON_ASSERT(str != 0);
|
||||
(void)copy;
|
||||
Prefix(kStringType);
|
||||
return EndValue(WriteString(str, length));
|
||||
}
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
bool String(const std::basic_string<Ch>& str) {
|
||||
return String(str.data(), SizeType(str.size()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool StartObject() {
|
||||
Prefix(kObjectType);
|
||||
new (level_stack_.template Push<Level>()) Level(false);
|
||||
return WriteStartObject();
|
||||
}
|
||||
|
||||
bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); }
|
||||
|
||||
#if RAPIDJSON_HAS_STDSTRING
|
||||
bool Key(const std::basic_string<Ch>& str)
|
||||
{
|
||||
return Key(str.data(), SizeType(str.size()));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool EndObject(SizeType memberCount = 0) {
|
||||
(void)memberCount;
|
||||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object
|
||||
RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray); // currently inside an Array, not Object
|
||||
RAPIDJSON_ASSERT(0 == level_stack_.template Top<Level>()->valueCount % 2); // Object has a Key without a Value
|
||||
level_stack_.template Pop<Level>(1);
|
||||
return EndValue(WriteEndObject());
|
||||
}
|
||||
|
||||
bool StartArray() {
|
||||
Prefix(kArrayType);
|
||||
new (level_stack_.template Push<Level>()) Level(true);
|
||||
return WriteStartArray();
|
||||
}
|
||||
|
||||
bool EndArray(SizeType elementCount = 0) {
|
||||
(void)elementCount;
|
||||
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
|
||||
RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
|
||||
level_stack_.template Pop<Level>(1);
|
||||
return EndValue(WriteEndArray());
|
||||
}
|
||||
//@}
|
||||
|
||||
/*! @name Convenience extensions */
|
||||
//@{
|
||||
|
||||
//! Simpler but slower overload.
|
||||
bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); }
|
||||
bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); }
|
||||
|
||||
//@}
|
||||
|
||||
//! Write a raw JSON value.
|
||||
/*!
|
||||
For user to write a stringified JSON as a value.
|
||||
|
||||
\param json A well-formed JSON value. It should not contain null character within [0, length - 1] range.
|
||||
\param length Length of the json.
|
||||
\param type Type of the root of json.
|
||||
*/
|
||||
bool RawValue(const Ch* json, size_t length, Type type) {
|
||||
RAPIDJSON_ASSERT(json != 0);
|
||||
Prefix(type);
|
||||
return EndValue(WriteRawValue(json, length));
|
||||
}
|
||||
|
||||
//! Flush the output stream.
|
||||
/*!
|
||||
Allows the user to flush the output stream immediately.
|
||||
*/
|
||||
void Flush() {
|
||||
os_->Flush();
|
||||
}
|
||||
|
||||
static const size_t kDefaultLevelDepth = 32;
|
||||
|
||||
protected:
|
||||
//! Information for each nested level
|
||||
struct Level {
|
||||
Level(bool inArray_) : valueCount(0), inArray(inArray_) {}
|
||||
size_t valueCount; //!< number of values in this level
|
||||
bool inArray; //!< true if in array, otherwise in object
|
||||
};
|
||||
|
||||
bool WriteNull() {
|
||||
PutReserve(*os_, 4);
|
||||
PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true;
|
||||
}
|
||||
|
||||
bool WriteBool(bool b) {
|
||||
if (b) {
|
||||
PutReserve(*os_, 4);
|
||||
PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e');
|
||||
}
|
||||
else {
|
||||
PutReserve(*os_, 5);
|
||||
PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteInt(int i) {
|
||||
char buffer[11];
|
||||
const char* end = internal::i32toa(i, buffer);
|
||||
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
||||
for (const char* p = buffer; p != end; ++p)
|
||||
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteUint(unsigned u) {
|
||||
char buffer[10];
|
||||
const char* end = internal::u32toa(u, buffer);
|
||||
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
||||
for (const char* p = buffer; p != end; ++p)
|
||||
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteInt64(int64_t i64) {
|
||||
char buffer[21];
|
||||
const char* end = internal::i64toa(i64, buffer);
|
||||
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
||||
for (const char* p = buffer; p != end; ++p)
|
||||
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteUint64(uint64_t u64) {
|
||||
char buffer[20];
|
||||
char* end = internal::u64toa(u64, buffer);
|
||||
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
||||
for (char* p = buffer; p != end; ++p)
|
||||
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteDouble(double d) {
|
||||
if (internal::Double(d).IsNanOrInf()) {
|
||||
if (!(writeFlags & kWriteNanAndInfFlag) && !(writeFlags & kWriteNanAndInfNullFlag))
|
||||
return false;
|
||||
if (writeFlags & kWriteNanAndInfNullFlag) {
|
||||
PutReserve(*os_, 4);
|
||||
PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l');
|
||||
return true;
|
||||
}
|
||||
if (internal::Double(d).IsNan()) {
|
||||
PutReserve(*os_, 3);
|
||||
PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
|
||||
return true;
|
||||
}
|
||||
if (internal::Double(d).Sign()) {
|
||||
PutReserve(*os_, 9);
|
||||
PutUnsafe(*os_, '-');
|
||||
}
|
||||
else
|
||||
PutReserve(*os_, 8);
|
||||
PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
|
||||
PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
|
||||
return true;
|
||||
}
|
||||
|
||||
char buffer[25];
|
||||
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
|
||||
PutReserve(*os_, static_cast<size_t>(end - buffer));
|
||||
for (char* p = buffer; p != end; ++p)
|
||||
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(*p));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteString(const Ch* str, SizeType length) {
|
||||
static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
static const char escape[256] = {
|
||||
#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
//0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00
|
||||
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10
|
||||
0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
|
||||
Z16, Z16, // 30~4F
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50
|
||||
Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF
|
||||
#undef Z16
|
||||
};
|
||||
|
||||
if (TargetEncoding::supportUnicode)
|
||||
PutReserve(*os_, 2 + length * 6); // "\uxxxx..."
|
||||
else
|
||||
PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..."
|
||||
|
||||
PutUnsafe(*os_, '\"');
|
||||
GenericStringStream<SourceEncoding> is(str);
|
||||
while (ScanWriteUnescapedString(is, length)) {
|
||||
const Ch c = is.Peek();
|
||||
if (!TargetEncoding::supportUnicode && static_cast<unsigned>(c) >= 0x80) {
|
||||
// Unicode escaping
|
||||
unsigned codepoint;
|
||||
if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint)))
|
||||
return false;
|
||||
PutUnsafe(*os_, '\\');
|
||||
PutUnsafe(*os_, 'u');
|
||||
if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) {
|
||||
PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]);
|
||||
PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]);
|
||||
PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]);
|
||||
PutUnsafe(*os_, hexDigits[(codepoint ) & 15]);
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF);
|
||||
// Surrogate pair
|
||||
unsigned s = codepoint - 0x010000;
|
||||
unsigned lead = (s >> 10) + 0xD800;
|
||||
unsigned trail = (s & 0x3FF) + 0xDC00;
|
||||
PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]);
|
||||
PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]);
|
||||
PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]);
|
||||
PutUnsafe(*os_, hexDigits[(lead ) & 15]);
|
||||
PutUnsafe(*os_, '\\');
|
||||
PutUnsafe(*os_, 'u');
|
||||
PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]);
|
||||
PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]);
|
||||
PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]);
|
||||
PutUnsafe(*os_, hexDigits[(trail ) & 15]);
|
||||
}
|
||||
}
|
||||
else if ((sizeof(Ch) == 1 || static_cast<unsigned>(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast<unsigned char>(c)])) {
|
||||
is.Take();
|
||||
PutUnsafe(*os_, '\\');
|
||||
PutUnsafe(*os_, static_cast<typename OutputStream::Ch>(escape[static_cast<unsigned char>(c)]));
|
||||
if (escape[static_cast<unsigned char>(c)] == 'u') {
|
||||
PutUnsafe(*os_, '0');
|
||||
PutUnsafe(*os_, '0');
|
||||
PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) >> 4]);
|
||||
PutUnsafe(*os_, hexDigits[static_cast<unsigned char>(c) & 0xF]);
|
||||
}
|
||||
}
|
||||
else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ?
|
||||
Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
|
||||
Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_))))
|
||||
return false;
|
||||
}
|
||||
PutUnsafe(*os_, '\"');
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScanWriteUnescapedString(GenericStringStream<SourceEncoding>& is, size_t length) {
|
||||
return RAPIDJSON_LIKELY(is.Tell() < length);
|
||||
}
|
||||
|
||||
bool WriteStartObject() { os_->Put('{'); return true; }
|
||||
bool WriteEndObject() { os_->Put('}'); return true; }
|
||||
bool WriteStartArray() { os_->Put('['); return true; }
|
||||
bool WriteEndArray() { os_->Put(']'); return true; }
|
||||
|
||||
bool WriteRawValue(const Ch* json, size_t length) {
|
||||
PutReserve(*os_, length);
|
||||
GenericStringStream<SourceEncoding> is(json);
|
||||
while (RAPIDJSON_LIKELY(is.Tell() < length)) {
|
||||
RAPIDJSON_ASSERT(is.Peek() != '\0');
|
||||
if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ?
|
||||
Transcoder<SourceEncoding, TargetEncoding>::Validate(is, *os_) :
|
||||
Transcoder<SourceEncoding, TargetEncoding>::TranscodeUnsafe(is, *os_))))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Prefix(Type type) {
|
||||
(void)type;
|
||||
if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root
|
||||
Level* level = level_stack_.template Top<Level>();
|
||||
if (level->valueCount > 0) {
|
||||
if (level->inArray)
|
||||
os_->Put(','); // add comma if it is not the first element in array
|
||||
else // in object
|
||||
os_->Put((level->valueCount % 2 == 0) ? ',' : ':');
|
||||
}
|
||||
if (!level->inArray && level->valueCount % 2 == 0)
|
||||
RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
|
||||
level->valueCount++;
|
||||
}
|
||||
else {
|
||||
RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root.
|
||||
hasRoot_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the value if it is the top level one.
|
||||
bool EndValue(bool ret) {
|
||||
if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text
|
||||
Flush();
|
||||
return ret;
|
||||
}
|
||||
|
||||
OutputStream* os_;
|
||||
internal::Stack<StackAllocator> level_stack_;
|
||||
int maxDecimalPlaces_;
|
||||
bool hasRoot_;
|
||||
|
||||
private:
|
||||
// Prohibit copy constructor & assignment operator.
|
||||
Writer(const Writer&);
|
||||
Writer& operator=(const Writer&);
|
||||
};
|
||||
|
||||
// Full specialization for StringStream to prevent memory copying
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteInt(int i) {
|
||||
char *buffer = os_->Push(11);
|
||||
const char* end = internal::i32toa(i, buffer);
|
||||
os_->Pop(static_cast<size_t>(11 - (end - buffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteUint(unsigned u) {
|
||||
char *buffer = os_->Push(10);
|
||||
const char* end = internal::u32toa(u, buffer);
|
||||
os_->Pop(static_cast<size_t>(10 - (end - buffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) {
|
||||
char *buffer = os_->Push(21);
|
||||
const char* end = internal::i64toa(i64, buffer);
|
||||
os_->Pop(static_cast<size_t>(21 - (end - buffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
|
||||
char *buffer = os_->Push(20);
|
||||
const char* end = internal::u64toa(u, buffer);
|
||||
os_->Pop(static_cast<size_t>(20 - (end - buffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::WriteDouble(double d) {
|
||||
if (internal::Double(d).IsNanOrInf()) {
|
||||
// Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag).
|
||||
if (!(kWriteDefaultFlags & kWriteNanAndInfFlag))
|
||||
return false;
|
||||
if (kWriteDefaultFlags & kWriteNanAndInfNullFlag) {
|
||||
PutReserve(*os_, 4);
|
||||
PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l');
|
||||
return true;
|
||||
}
|
||||
if (internal::Double(d).IsNan()) {
|
||||
PutReserve(*os_, 3);
|
||||
PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N');
|
||||
return true;
|
||||
}
|
||||
if (internal::Double(d).Sign()) {
|
||||
PutReserve(*os_, 9);
|
||||
PutUnsafe(*os_, '-');
|
||||
}
|
||||
else
|
||||
PutReserve(*os_, 8);
|
||||
PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f');
|
||||
PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y');
|
||||
return true;
|
||||
}
|
||||
|
||||
char *buffer = os_->Push(25);
|
||||
char* end = internal::dtoa(d, buffer, maxDecimalPlaces_);
|
||||
os_->Pop(static_cast<size_t>(25 - (end - buffer)));
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42)
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) {
|
||||
if (length < 16)
|
||||
return RAPIDJSON_LIKELY(is.Tell() < length);
|
||||
|
||||
if (!RAPIDJSON_LIKELY(is.Tell() < length))
|
||||
return false;
|
||||
|
||||
const char* p = is.src_;
|
||||
const char* end = is.head_ + length;
|
||||
const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
|
||||
const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15));
|
||||
if (nextAligned > end)
|
||||
return true;
|
||||
|
||||
while (p != nextAligned)
|
||||
if (*p < 0x20 || *p == '\"' || *p == '\\') {
|
||||
is.src_ = p;
|
||||
return RAPIDJSON_LIKELY(is.Tell() < length);
|
||||
}
|
||||
else
|
||||
os_->PutUnsafe(*p++);
|
||||
|
||||
// The rest of string using SIMD
|
||||
static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' };
|
||||
static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' };
|
||||
static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F };
|
||||
const __m128i dq = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&dquote[0]));
|
||||
const __m128i bs = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&bslash[0]));
|
||||
const __m128i sp = _mm_loadu_si128(reinterpret_cast<const __m128i *>(&space[0]));
|
||||
|
||||
for (; p != endAligned; p += 16) {
|
||||
const __m128i s = _mm_load_si128(reinterpret_cast<const __m128i *>(p));
|
||||
const __m128i t1 = _mm_cmpeq_epi8(s, dq);
|
||||
const __m128i t2 = _mm_cmpeq_epi8(s, bs);
|
||||
const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F
|
||||
const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3);
|
||||
unsigned short r = static_cast<unsigned short>(_mm_movemask_epi8(x));
|
||||
if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped
|
||||
SizeType len;
|
||||
#ifdef _MSC_VER // Find the index of first escaped
|
||||
unsigned long offset;
|
||||
_BitScanForward(&offset, r);
|
||||
len = offset;
|
||||
#else
|
||||
len = static_cast<SizeType>(__builtin_ffs(r) - 1);
|
||||
#endif
|
||||
char* q = reinterpret_cast<char*>(os_->PushUnsafe(len));
|
||||
for (size_t i = 0; i < len; i++)
|
||||
q[i] = p[i];
|
||||
|
||||
p += len;
|
||||
break;
|
||||
}
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s);
|
||||
}
|
||||
|
||||
is.src_ = p;
|
||||
return RAPIDJSON_LIKELY(is.Tell() < length);
|
||||
}
|
||||
#elif defined(RAPIDJSON_NEON)
|
||||
template<>
|
||||
inline bool Writer<StringBuffer>::ScanWriteUnescapedString(StringStream& is, size_t length) {
|
||||
if (length < 16)
|
||||
return RAPIDJSON_LIKELY(is.Tell() < length);
|
||||
|
||||
if (!RAPIDJSON_LIKELY(is.Tell() < length))
|
||||
return false;
|
||||
|
||||
const char* p = is.src_;
|
||||
const char* end = is.head_ + length;
|
||||
const char* nextAligned = reinterpret_cast<const char*>((reinterpret_cast<size_t>(p) + 15) & static_cast<size_t>(~15));
|
||||
const char* endAligned = reinterpret_cast<const char*>(reinterpret_cast<size_t>(end) & static_cast<size_t>(~15));
|
||||
if (nextAligned > end)
|
||||
return true;
|
||||
|
||||
while (p != nextAligned)
|
||||
if (*p < 0x20 || *p == '\"' || *p == '\\') {
|
||||
is.src_ = p;
|
||||
return RAPIDJSON_LIKELY(is.Tell() < length);
|
||||
}
|
||||
else
|
||||
os_->PutUnsafe(*p++);
|
||||
|
||||
// The rest of string using SIMD
|
||||
const uint8x16_t s0 = vmovq_n_u8('"');
|
||||
const uint8x16_t s1 = vmovq_n_u8('\\');
|
||||
const uint8x16_t s2 = vmovq_n_u8('\b');
|
||||
const uint8x16_t s3 = vmovq_n_u8(32);
|
||||
|
||||
for (; p != endAligned; p += 16) {
|
||||
const uint8x16_t s = vld1q_u8(reinterpret_cast<const uint8_t *>(p));
|
||||
uint8x16_t x = vceqq_u8(s, s0);
|
||||
x = vorrq_u8(x, vceqq_u8(s, s1));
|
||||
x = vorrq_u8(x, vceqq_u8(s, s2));
|
||||
x = vorrq_u8(x, vcltq_u8(s, s3));
|
||||
|
||||
x = vrev64q_u8(x); // Rev in 64
|
||||
uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract
|
||||
uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract
|
||||
|
||||
SizeType len = 0;
|
||||
bool escaped = false;
|
||||
if (low == 0) {
|
||||
if (high != 0) {
|
||||
uint32_t lz = internal::clzll(high);
|
||||
len = 8 + (lz >> 3);
|
||||
escaped = true;
|
||||
}
|
||||
} else {
|
||||
uint32_t lz = internal::clzll(low);
|
||||
len = lz >> 3;
|
||||
escaped = true;
|
||||
}
|
||||
if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped
|
||||
char* q = reinterpret_cast<char*>(os_->PushUnsafe(len));
|
||||
for (size_t i = 0; i < len; i++)
|
||||
q[i] = p[i];
|
||||
|
||||
p += len;
|
||||
break;
|
||||
}
|
||||
vst1q_u8(reinterpret_cast<uint8_t *>(os_->PushUnsafe(16)), s);
|
||||
}
|
||||
|
||||
is.src_ = p;
|
||||
return RAPIDJSON_LIKELY(is.Tell() < length);
|
||||
}
|
||||
#endif // RAPIDJSON_NEON
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#if defined(_MSC_VER) || defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
||||
#endif // RAPIDJSON_RAPIDJSON_H_
|
7987
include/stb_image.h
Normal file
7987
include/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
10303
include/stb_image_resize2.h
Normal file
10303
include/stb_image_resize2.h
Normal file
File diff suppressed because it is too large
Load Diff
1724
include/stb_image_write.h
Normal file
1724
include/stb_image_write.h
Normal file
File diff suppressed because it is too large
Load Diff
55
lib/Botan-3.2.0/.clang-format
Normal file
55
lib/Botan-3.2.0/.clang-format
Normal file
@ -0,0 +1,55 @@
|
||||
Language: Cpp
|
||||
Standard: c++20
|
||||
|
||||
BasedOnStyle: Chromium
|
||||
|
||||
ColumnLimit: 120
|
||||
AccessModifierOffset: -3
|
||||
IndentWidth: 3
|
||||
ContinuationIndentWidth: 3
|
||||
ConstructorInitializerIndentWidth: 6
|
||||
|
||||
PointerAlignment: Left
|
||||
ReferenceAlignment: Left
|
||||
QualifierAlignment: Left
|
||||
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^<botan/internal/.*\.h>'
|
||||
Priority: 3
|
||||
CaseSensitive: false
|
||||
- Regex: '^<botan/.*\.h>'
|
||||
Priority: 2
|
||||
CaseSensitive: false
|
||||
- Regex: '^<.*'
|
||||
Priority: 4
|
||||
CaseSensitive: false
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 3
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
CaseSensitive: false
|
||||
|
||||
AttributeMacros: ['BOTAN_FUNC_ISA', 'BOTAN_FUNC_ISA_INLINE', 'BOTAN_FORCE_INLINE']
|
||||
|
||||
BinPackArguments: false
|
||||
BreakStringLiterals: false
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
EmptyLineBeforeAccessModifier: Always
|
||||
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakInheritanceList: AfterComma
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
SpaceBeforeParens: Never
|
||||
IndentPPDirectives: BeforeHash
|
||||
FixNamespaceComments: true
|
||||
SeparateDefinitionBlocks: Always
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
IndentAccessModifiers: true
|
||||
ReflowComments: false
|
||||
RequiresClausePosition: OwnLine
|
||||
IndentRequiresClause: true
|
54
lib/Botan-3.2.0/.github/actions/setup-build-agent/action.yml
vendored
Normal file
54
lib/Botan-3.2.0/.github/actions/setup-build-agent/action.yml
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
# (C) 2022 Jack Lloyd
|
||||
# (C) 2022 René Meusel, Rohde & Schwarz Cybersecurity
|
||||
#
|
||||
# Botan is released under the Simplified BSD License (see license.txt)
|
||||
|
||||
name: Setup Botan Build Agent
|
||||
description: Set up a build agent for building and testing the Botan library
|
||||
|
||||
inputs:
|
||||
target:
|
||||
description: The ci_build.py target going to be built on this agent
|
||||
required: true
|
||||
cache-key:
|
||||
description: The actions/cache key to be used for this runs, caching will be disabled when no key is provided
|
||||
required: false
|
||||
arch:
|
||||
description: Target CPU architecture
|
||||
required: false
|
||||
default: x64
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup Build Agent (Windows)
|
||||
run: ${{ github.action_path }}/../../../src/scripts/ci/setup_gh_actions.ps1 "${{ inputs.target }}" "${{ inputs.arch }}"
|
||||
shell: pwsh
|
||||
if: runner.os == 'Windows'
|
||||
- name: Setup Build Agent (Unix-like)
|
||||
run: ${{ github.action_path }}/../../../src/scripts/ci/setup_gh_actions.sh "${{ inputs.target }}" "${{ inputs.arch }}"
|
||||
shell: bash
|
||||
if: runner.os != 'Windows'
|
||||
|
||||
- name: Check Availability of Compiler Cache
|
||||
run: print("::warning ::No compiler cache available, build times might suffer")
|
||||
shell: python
|
||||
if: env.COMPILER_CACHE_LOCATION == '' && inputs.cache-key != ''
|
||||
- uses: actions/cache@v3
|
||||
if: env.COMPILER_CACHE_LOCATION != '' && inputs.cache-key != ''
|
||||
with:
|
||||
path: ${{ env.COMPILER_CACHE_LOCATION }}
|
||||
key: ${{ inputs.cache-key }}-${{ github.run_id }}
|
||||
restore-keys: ${{ inputs.cache-key }}
|
||||
|
||||
- name: Setup Visual Studio Environment
|
||||
uses: egor-tensin/vs-shell@v2
|
||||
with:
|
||||
arch: ${{ env.VSENV_ARCH }} # set by setup_gh_actions.ps1
|
||||
if: runner.os == 'Windows'
|
||||
|
||||
- name: Install Build Dependencies # after setting up Visual Studio Environment
|
||||
run: ${{ github.action_path }}/../../../src/scripts/ci/setup_gh_actions_after_vcvars.ps1 ${{ inputs.target }}
|
||||
shell: pwsh
|
||||
if: runner.os == 'Windows'
|
1
lib/Botan-3.2.0/.github/codecov.yml
vendored
Symbolic link
1
lib/Botan-3.2.0/.github/codecov.yml
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
../src/scripts/ci/codecov.yml
|
291
lib/Botan-3.2.0/.github/workflows/ci.yml
vendored
Normal file
291
lib/Botan-3.2.0/.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
|
||||
# (C) 2020,2022 Jack Lloyd
|
||||
# (C) 2022 René Meusel, Rohde & Schwarz Cybersecurity
|
||||
#
|
||||
# Botan is released under the Simplified BSD License (see license.txt)
|
||||
|
||||
name: ci
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
# implicitly all other scopes not listed become none
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
# cancel running workflows when new commits are being pushed in pull requests
|
||||
# but not on the master branch
|
||||
concurrency:
|
||||
group: ${{ github.workflow }} @ ${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
windows:
|
||||
name: "Windows"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- target: shared
|
||||
arch: x86_64
|
||||
host_os: windows-2022
|
||||
- target: static
|
||||
arch: x86_64
|
||||
host_os: windows-2022
|
||||
- target: amalgamation
|
||||
arch: x86_64
|
||||
host_os: windows-2022
|
||||
- target: shared
|
||||
arch: x86
|
||||
host_os: windows-2022
|
||||
|
||||
runs-on: ${{ matrix.host_os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Build Agent
|
||||
uses: ./.github/actions/setup-build-agent
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
cache-key: ${{ matrix.host_os }}-msvc-${{ matrix.arch }}-${{ matrix.target }}
|
||||
arch: ${{ matrix.arch }}
|
||||
|
||||
- name: Build and Test Botan
|
||||
run: python3 ./src/scripts/ci_build.py --cc='msvc' --make-tool='ninja' --cpu='${{ matrix.arch }}' --test-results-dir=junit_results ${{ matrix.target }}
|
||||
|
||||
linux:
|
||||
name: "Linux"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- compiler: gcc
|
||||
target: shared
|
||||
- compiler: gcc
|
||||
target: amalgamation
|
||||
- compiler: gcc
|
||||
target: static
|
||||
- compiler: clang
|
||||
target: shared
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Build Agent
|
||||
uses: ./.github/actions/setup-build-agent
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
cache-key: linux-${{ matrix.compiler }}-x86_64-${{ matrix.target }}
|
||||
|
||||
- name: Build and Test Botan
|
||||
run: python3 ./src/scripts/ci_build.py --cc='${{ matrix.compiler }}' --test-results-dir=junit_results ${{ matrix.target }}
|
||||
|
||||
macos:
|
||||
name: "macOS"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- target: shared
|
||||
compiler: clang
|
||||
- target: amalgamation
|
||||
compiler: clang
|
||||
|
||||
runs-on: macos-13
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Build Agent
|
||||
uses: ./.github/actions/setup-build-agent
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
cache-key: macos-${{ matrix.compiler }}-x86_64-${{ matrix.target }}
|
||||
|
||||
- name: Build and Test Botan
|
||||
run: python3 ./src/scripts/ci_build.py --cc='${{ matrix.compiler }}' --test-results-dir=junit_results ${{ matrix.target }}
|
||||
|
||||
clang-tidy:
|
||||
name: "Clang Tidy"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Build Agent
|
||||
uses: ./.github/actions/setup-build-agent
|
||||
with:
|
||||
target: clang-tidy
|
||||
cache-key: linux-x86_64-clang-tidy
|
||||
|
||||
- name: Configure Build
|
||||
run: python3 ./configure.py --cc=clang
|
||||
|
||||
- name: Run Clang Tidy
|
||||
run: |
|
||||
./src/scripts/ci/gh_get_changes_in_pr.py $(git rev-parse HEAD) --api-token=${{ secrets.GITHUB_TOKEN }} | \
|
||||
python3 ./src/scripts/dev_tools/run_clang_tidy.py --verbose --take-file-list-from-stdin --export-fixes-dir=clang_tidy_diagnostics
|
||||
|
||||
- name: Display Clang Tidy Results
|
||||
if: failure()
|
||||
run: ./src/scripts/ci/gh_clang_tidy_fixes_in_pr.py clang_tidy_diagnostics
|
||||
|
||||
analysis:
|
||||
name: "Analysis"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- target: coverage
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: sanitizer
|
||||
compiler: msvc
|
||||
host_os: windows-2022
|
||||
make_tool: ninja
|
||||
- target: sanitizer
|
||||
compiler: clang
|
||||
host_os: ubuntu-22.04
|
||||
- target: sanitizer
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: valgrind
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: fuzzers
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: lint
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: format
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
|
||||
runs-on: ${{ matrix.host_os }}
|
||||
|
||||
env:
|
||||
COVERALLS_REPO_TOKEN: pbLoTMBxC1DFvbws9WfrzVOvfEdEZTcCS
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: ./source
|
||||
|
||||
- name: Fetch BoringSSL fork for BoGo tests
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: randombit/boringssl
|
||||
ref: rene/runner-20230705
|
||||
path: ./boringssl
|
||||
if: matrix.target == 'coverage' || matrix.target == 'sanitizer'
|
||||
|
||||
- name: Setup Build Agent
|
||||
uses: ./source/.github/actions/setup-build-agent
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
cache-key: ${{ matrix.host_os }}-${{ matrix.compiler }}-x86_64-${{ matrix.target }}
|
||||
|
||||
- name: Build and Test Botan
|
||||
run: python3 ./source/src/scripts/ci_build.py --root-dir=${{ github.workspace }}/source --build-dir=${{ github.workspace }}/build --boringssl-dir=${{ github.workspace }}/boringssl --cc='${{ matrix.compiler }}' --make-tool='${{ matrix.make_tool }}' --test-results-dir=junit_results ${{ matrix.target }}
|
||||
|
||||
specials:
|
||||
name: "Special"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- target: examples
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: minimized
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: bsi
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
|
||||
runs-on: ${{ matrix.host_os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: ./source
|
||||
|
||||
- name: Setup Build Agent
|
||||
uses: ./source/.github/actions/setup-build-agent
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
cache-key: ${{ matrix.host_os }}-${{ matrix.compiler }}-x86_64-${{ matrix.target }}
|
||||
|
||||
- name: Build and Test Botan
|
||||
run: python3 ./source/src/scripts/ci_build.py --root-dir=${{ github.workspace }}/source --build-dir=${{ github.workspace }}/build --boringssl-dir=${{ github.workspace }}/boringssl --cc='${{ matrix.compiler }}' --make-tool='${{ matrix.make_tool }}' --test-results-dir=junit_results ${{ matrix.target }}
|
||||
|
||||
x-compile:
|
||||
name: "Cross"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- target: cross-i386
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: cross-arm32
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: cross-arm64
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: cross-ppc64
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: cross-riscv64
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: cross-s390x
|
||||
compiler: gcc
|
||||
host_os: ubuntu-22.04
|
||||
- target: cross-android-arm32
|
||||
compiler: clang
|
||||
host_os: ubuntu-22.04
|
||||
- target: cross-android-arm64
|
||||
compiler: clang
|
||||
host_os: ubuntu-22.04
|
||||
- target: static
|
||||
compiler: gcc
|
||||
host_os: windows-2022
|
||||
make_tool: mingw32-make
|
||||
- target: cross-ios-arm64
|
||||
compiler: clang
|
||||
host_os: macos-13
|
||||
- target: emscripten
|
||||
compiler: emcc
|
||||
host_os: macos-13
|
||||
|
||||
runs-on: ${{ matrix.host_os }}
|
||||
|
||||
env:
|
||||
ANDROID_NDK: android-ndk-r26
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Build Agent
|
||||
uses: ./.github/actions/setup-build-agent
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
cache-key: ${{ matrix.host_os }}-${{ matrix.compiler }}-xcompile-${{ matrix.target }}
|
||||
|
||||
- name: Build and Test Botan
|
||||
run: python3 ./src/scripts/ci_build.py --cc='${{ matrix.compiler }}' --test-results-dir=junit_results ${{ matrix.target }}
|
29
lib/Botan-3.2.0/.github/workflows/cifuzz.yml
vendored
Normal file
29
lib/Botan-3.2.0/.github/workflows/cifuzz.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: CIFuzz
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
# implicitly all other scopes not listed become none
|
||||
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Build Fuzzers
|
||||
id: build
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'botan'
|
||||
dry-run: false
|
||||
- name: Run Fuzzers
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: 'botan'
|
||||
fuzz-seconds: 180
|
||||
dry-run: false
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v1
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out/artifacts
|
70
lib/Botan-3.2.0/.github/workflows/codeql.yml
vendored
Normal file
70
lib/Botan-3.2.0/.github/workflows/codeql.yml
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["master"]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: ["master"]
|
||||
schedule:
|
||||
# runs every day at 4:23 AM UTC
|
||||
- cron: "23 4 * * *"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
codeql_cpp:
|
||||
name: C++
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Build Agent
|
||||
uses: ./.github/actions/setup-build-agent
|
||||
with:
|
||||
target: codeql
|
||||
cache-key: linux-gcc-x86_64-codeql
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: cpp
|
||||
config-file: ./src/configs/codeql.yml
|
||||
|
||||
- name: Build Library
|
||||
run: ./src/scripts/ci_build.py codeql
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: cpp
|
||||
|
||||
codeql_py:
|
||||
name: Python
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: python
|
||||
config-file: ./src/configs/codeql.yml
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: python
|
89
lib/Botan-3.2.0/.github/workflows/nightly.yml
vendored
Normal file
89
lib/Botan-3.2.0/.github/workflows/nightly.yml
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
|
||||
# (C) 2023 Jack Lloyd
|
||||
# (C) 2023 Fabian Albert, Rohde & Schwarz Cybersecurity
|
||||
#
|
||||
# Botan is released under the Simplified BSD License (see license.txt)
|
||||
|
||||
name: nightly
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
# implicitly all other scopes not listed become none
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# runs every day at 3:23 AM UTC
|
||||
- cron: '23 3 * * *'
|
||||
|
||||
jobs:
|
||||
clang_tidy:
|
||||
name: "clang-tidy"
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Build Agent
|
||||
uses: ./.github/actions/setup-build-agent
|
||||
with:
|
||||
target: clang-tidy
|
||||
cache-key: linux-x86_64-clang-tidy
|
||||
|
||||
- name: Install Boost
|
||||
run: sudo apt-get -qq install libboost-dev
|
||||
|
||||
- name: Configure Build
|
||||
run: python3 ./configure.py --cc=clang --build-targets=shared,cli,tests,examples,bogo_shim --build-fuzzers=test --with-boost --with-sqlite --with-zlib --with-lzma --with-bzip2
|
||||
|
||||
- name: Run Clang Tidy
|
||||
run: python3 ./src/scripts/dev_tools/run_clang_tidy.py --verbose
|
||||
|
||||
valgrind:
|
||||
name: "valgrind"
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Build Agent
|
||||
uses: ./.github/actions/setup-build-agent
|
||||
with:
|
||||
target: valgrind-full
|
||||
cache-key: linux-x86_64-valgrind-full
|
||||
|
||||
- name: Valgrind Checks
|
||||
run: python3 ./src/scripts/ci_build.py --cc=gcc --make-tool=make valgrind-full
|
||||
|
||||
tls_anvil_server_test:
|
||||
name: "TLS-Anvil (server)"
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Fetch Botan Repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Build Agent
|
||||
uses: ./.github/actions/setup-build-agent
|
||||
with:
|
||||
target: tlsanvil
|
||||
cache-key: linux-x86_64-tlsanvil
|
||||
|
||||
- name: Build and Test Botan Server with TLS-Anvil
|
||||
run: >
|
||||
python3 ./src/scripts/ci/ci_tlsanvil_test.py
|
||||
--botan-dir .
|
||||
--test-target server
|
||||
--parallel $(nproc)
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: tls-anvil-server-test-results
|
||||
path: |
|
||||
./TestSuiteResults/
|
||||
./logs/
|
||||
|
||||
- name: Check TLS-Anvil Test Results
|
||||
run: python3 ./src/scripts/ci/ci_tlsanvil_check.py --verbose ./TestSuiteResults
|
98
lib/Botan-3.2.0/.gitignore
vendored
Normal file
98
lib/Botan-3.2.0/.gitignore
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
/Makefile
|
||||
CMakeLists.txt*
|
||||
build.ninja
|
||||
.ninja_log
|
||||
libbotan*.so.*
|
||||
*.a
|
||||
*.so
|
||||
*.dylib
|
||||
*.exp
|
||||
*.lib
|
||||
*.pdb
|
||||
*.ilk
|
||||
*.dll
|
||||
*.exe
|
||||
*.manifest
|
||||
build
|
||||
build_deps
|
||||
build.log
|
||||
botan
|
||||
botan-test
|
||||
|
||||
core.*
|
||||
vgcore.*
|
||||
|
||||
# Text file backups (e.g. gedit, joe)
|
||||
*~
|
||||
\#*\#
|
||||
.\#*
|
||||
|
||||
# Editor configuration files (top level)
|
||||
/*.sublime-project
|
||||
/*.sublime-workspace
|
||||
/.editorconfig
|
||||
|
||||
# Archive files
|
||||
*.tgz
|
||||
*.tar
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Patch files
|
||||
*.patch
|
||||
*.diff
|
||||
*.orig
|
||||
*.rej
|
||||
|
||||
# Cache and temporary files
|
||||
*.pyc
|
||||
.DS_Store
|
||||
*.swp
|
||||
/*.cache
|
||||
|
||||
# ctags/etags files
|
||||
/TAGS
|
||||
/tags
|
||||
|
||||
# Amalgamation code
|
||||
botan_all.h
|
||||
botan_all_internal.h
|
||||
botan_all.cpp
|
||||
botan_all_*.cpp
|
||||
|
||||
# Coverage output
|
||||
coverage.info
|
||||
coverage.info.raw
|
||||
coverage/
|
||||
lcov-out/
|
||||
|
||||
/fuzzer_corpus
|
||||
|
||||
# Profiler outputs
|
||||
cachegrind.*
|
||||
callgrind.*
|
||||
|
||||
# Ignore stuff in the top level dir that shouldn't be checked in
|
||||
/*.c
|
||||
/*.cpp
|
||||
/*.h
|
||||
/*.py
|
||||
/*.key
|
||||
/*.pem
|
||||
/*.der
|
||||
/*.ber
|
||||
/*.gpg
|
||||
/*.pub
|
||||
/*.crt
|
||||
/*.txt
|
||||
/*.rst
|
||||
|
||||
# Add back files from the toplevel
|
||||
!/news.rst
|
||||
!/readme.rst
|
||||
!/configure.py
|
||||
!/license.txt
|
||||
|
||||
# cscope
|
||||
cscope.out
|
3570
lib/Botan-3.2.0/configure.py
Executable file
3570
lib/Botan-3.2.0/configure.py
Executable file
File diff suppressed because it is too large
Load Diff
21
lib/Botan-3.2.0/doc/abi.rst
Normal file
21
lib/Botan-3.2.0/doc/abi.rst
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
ABI Stability
|
||||
====================
|
||||
|
||||
Botan uses semantic versioning for the API; if API features are added the minor
|
||||
version increases, whereas if API compatibility breaks occur the major version
|
||||
is increased.
|
||||
|
||||
However no guarantees about ABI are made between releases. Maintaining an ABI
|
||||
compatible release in a complex C++ API is exceedingly expensive in development
|
||||
time; just adding a single member variable or virtual function is enough to
|
||||
cause ABI issues.
|
||||
|
||||
If ABI changes, the soname revision will increase to prevent applications from
|
||||
linking against a potentially incompatible version at runtime.
|
||||
|
||||
If you are concerned about long-term ABI issues, considering using the C API
|
||||
instead; this subset *is* ABI stable.
|
||||
|
||||
You can review a report on ABI changes to Botan at
|
||||
https://abi-laboratory.pro/tracker/timeline/botan/
|
279
lib/Botan-3.2.0/doc/api_ref/bigint.rst
Normal file
279
lib/Botan-3.2.0/doc/api_ref/bigint.rst
Normal file
@ -0,0 +1,279 @@
|
||||
BigInt
|
||||
========================================
|
||||
|
||||
``BigInt`` is Botan's implementation of a multiple-precision integer. Thanks to
|
||||
C++'s operator overloading features, using ``BigInt`` is often quite similar to
|
||||
using a native integer type. The number of functions related to ``BigInt`` is
|
||||
quite large, and not all of them are documented here. You can find the complete
|
||||
declarations in ``botan/bigint.h`` and ``botan/numthry.h``.
|
||||
|
||||
.. cpp:class:: BigInt
|
||||
|
||||
.. cpp:function:: BigInt()
|
||||
|
||||
Create a BigInt with value zero
|
||||
|
||||
.. cpp:function:: BigInt(uint64_t n)
|
||||
|
||||
Create a BigInt with value *n*
|
||||
|
||||
.. cpp:function:: BigInt(const std::string& str)
|
||||
|
||||
Create a BigInt from a string. By default decimal is expected. With an 0x
|
||||
prefix instead it is treated as hexadecimal.
|
||||
|
||||
.. cpp:function:: BigInt(const uint8_t buf[], size_t length)
|
||||
|
||||
Create a BigInt from a binary array (big-endian encoding).
|
||||
|
||||
.. cpp:function:: BigInt(RandomNumberGenerator& rng, size_t bits, bool set_high_bit = true)
|
||||
|
||||
Create a random BigInt of the specified size.
|
||||
|
||||
.. cpp:function:: BigInt operator+(const BigInt& x, const BigInt& y)
|
||||
|
||||
Add ``x`` and ``y`` and return result.
|
||||
|
||||
.. cpp:function:: BigInt operator+(const BigInt& x, word y)
|
||||
|
||||
Add ``x`` and ``y`` and return result.
|
||||
|
||||
.. cpp:function:: BigInt operator+(word x, const BigInt& y)
|
||||
|
||||
Add ``x`` and ``y`` and return result.
|
||||
|
||||
.. cpp:function:: BigInt operator-(const BigInt& x, const BigInt& y)
|
||||
|
||||
Subtract ``y`` from ``x`` and return result.
|
||||
|
||||
.. cpp:function:: BigInt operator-(const BigInt& x, word y)
|
||||
|
||||
Subtract ``y`` from ``x`` and return result.
|
||||
|
||||
.. cpp:function:: BigInt operator*(const BigInt& x, const BigInt& y)
|
||||
|
||||
Multiply ``x`` and ``y`` and return result.
|
||||
|
||||
.. cpp:function:: BigInt operator/(const BigInt& x, const BigInt& y)
|
||||
|
||||
Divide ``x`` by ``y`` and return result.
|
||||
|
||||
.. cpp:function:: BigInt operator%(const BigInt& x, const BigInt& y)
|
||||
|
||||
Divide ``x`` by ``y`` and return remainder.
|
||||
|
||||
.. cpp:function:: word operator%(const BigInt& x, word y)
|
||||
|
||||
Divide ``x`` by ``y`` and return remainder.
|
||||
|
||||
.. cpp:function:: word operator<<(const BigInt& x, size_t n)
|
||||
|
||||
Left shift ``x`` by ``n`` and return result.
|
||||
|
||||
.. cpp:function:: word operator>>(const BigInt& x, size_t n)
|
||||
|
||||
Right shift ``x`` by ``n`` and return result.
|
||||
|
||||
.. cpp:function:: BigInt& operator+=(const BigInt& y)
|
||||
|
||||
Add y to ``*this``
|
||||
|
||||
.. cpp:function:: BigInt& operator+=(word y)
|
||||
|
||||
Add y to ``*this``
|
||||
|
||||
.. cpp:function:: BigInt& operator-=(const BigInt& y)
|
||||
|
||||
Subtract y from ``*this``
|
||||
|
||||
.. cpp:function:: BigInt& operator-=(word y)
|
||||
|
||||
Subtract y from ``*this``
|
||||
|
||||
.. cpp:function:: BigInt& operator*=(const BigInt& y)
|
||||
|
||||
Multiply ``*this`` with y
|
||||
|
||||
.. cpp:function:: BigInt& operator*=(word y)
|
||||
|
||||
Multiply ``*this`` with y
|
||||
|
||||
.. cpp:function:: BigInt& operator/=(const BigInt& y)
|
||||
|
||||
Divide ``*this`` by y
|
||||
|
||||
.. cpp:function:: BigInt& operator%=(const BigInt& y)
|
||||
|
||||
Divide ``*this`` by y and set ``*this`` to the remainder.
|
||||
|
||||
.. cpp:function:: word operator%=(word y)
|
||||
|
||||
Divide ``*this`` by y and set ``*this`` to the remainder.
|
||||
|
||||
.. cpp:function:: word operator<<=(size_t shift)
|
||||
|
||||
Left shift ``*this`` by *shift* bits
|
||||
|
||||
.. cpp:function:: word operator>>=(size_t shift)
|
||||
|
||||
Right shift ``*this`` by *shift* bits
|
||||
|
||||
.. cpp:function:: BigInt& operator++()
|
||||
|
||||
Increment ``*this`` by 1
|
||||
|
||||
.. cpp:function:: BigInt& operator--()
|
||||
|
||||
Decrement ``*this`` by 1
|
||||
|
||||
.. cpp:function:: BigInt operator++(int)
|
||||
|
||||
Postfix increment ``*this`` by 1
|
||||
|
||||
.. cpp:function:: BigInt operator--(int)
|
||||
|
||||
Postfix decrement ``*this`` by 1
|
||||
|
||||
.. cpp:function:: BigInt operator-() const
|
||||
|
||||
Negation operator
|
||||
|
||||
.. cpp:function:: bool operator !() const
|
||||
|
||||
Return true unless ``*this`` is zero
|
||||
|
||||
.. cpp:function:: void clear()
|
||||
|
||||
Set ``*this`` to zero
|
||||
|
||||
.. cpp:function:: size_t bytes() const
|
||||
|
||||
Return number of bytes need to represent value of ``*this``
|
||||
|
||||
.. cpp:function:: size_t bits() const
|
||||
|
||||
Return number of bits need to represent value of ``*this``
|
||||
|
||||
.. cpp:function:: bool is_even() const
|
||||
|
||||
Return true if ``*this`` is even
|
||||
|
||||
.. cpp:function:: bool is_odd() const
|
||||
|
||||
Return true if ``*this`` is odd
|
||||
|
||||
.. cpp:function:: bool is_nonzero() const
|
||||
|
||||
Return true if ``*this`` is not zero
|
||||
|
||||
.. cpp:function:: bool is_zero() const
|
||||
|
||||
Return true if ``*this`` is zero
|
||||
|
||||
.. cpp:function:: void set_bit(size_t n)
|
||||
|
||||
Set bit *n* of ``*this``
|
||||
|
||||
.. cpp:function:: void clear_bit(size_t n)
|
||||
|
||||
Clear bit *n* of ``*this``
|
||||
|
||||
.. cpp:function:: bool get_bit(size_t n) const
|
||||
|
||||
Get bit *n* of ``*this``
|
||||
|
||||
.. cpp:function:: uint32_t to_u32bit() const
|
||||
|
||||
Return value of ``*this`` as a 32-bit integer, if possible.
|
||||
If the integer is negative or not in range, an exception is thrown.
|
||||
|
||||
.. cpp:function:: bool is_negative() const
|
||||
|
||||
Return true if ``*this`` is negative
|
||||
|
||||
.. cpp:function:: bool is_positive() const
|
||||
|
||||
Return true if ``*this`` is negative
|
||||
|
||||
.. cpp:function:: BigInt abs() const
|
||||
|
||||
Return absolute value of ``*this``
|
||||
|
||||
.. cpp:function:: void binary_encode(uint8_t buf[]) const
|
||||
|
||||
Encode this BigInt as a big-endian integer. The sign is ignored.
|
||||
|
||||
.. cpp:function:: void binary_encode(uint8_t buf[], size_t len) const
|
||||
|
||||
Encode this BigInt as a big-endian integer. The sign is ignored.
|
||||
If ``len`` is less than ``bytes()`` then only the low ``len``
|
||||
bytes are output. If ``len`` is greater than ``bytes()`` then
|
||||
the output is padded with leading zeros.
|
||||
|
||||
.. cpp:function:: void binary_decode(uint8_t buf[])
|
||||
|
||||
Decode this BigInt as a big-endian integer.
|
||||
|
||||
.. cpp:function:: std::string to_dec_string() const
|
||||
|
||||
Encode the integer as a decimal string.
|
||||
|
||||
.. cpp:function:: std::string to_hex_string() const
|
||||
|
||||
Encode the integer as a hexadecimal string.
|
||||
|
||||
Number Theory
|
||||
----------------------------------------
|
||||
|
||||
Number theoretic functions available include:
|
||||
|
||||
.. cpp:function:: BigInt gcd(BigInt x, BigInt y)
|
||||
|
||||
Returns the greatest common divisor of x and y
|
||||
|
||||
.. cpp:function:: BigInt lcm(BigInt x, BigInt y)
|
||||
|
||||
Returns an integer z which is the smallest integer such that z % x
|
||||
== 0 and z % y == 0
|
||||
|
||||
.. cpp:function:: BigInt jacobi(BigInt a, BigInt n)
|
||||
|
||||
Return Jacobi symbol of (a|n).
|
||||
|
||||
.. cpp:function:: BigInt inverse_mod(BigInt x, BigInt m)
|
||||
|
||||
Returns the modular inverse of x modulo m, that is, an integer
|
||||
y such that (x*y) % m == 1. If no such y exists, returns zero.
|
||||
|
||||
.. cpp:function:: BigInt power_mod(BigInt b, BigInt x, BigInt m)
|
||||
|
||||
Returns b to the xth power modulo m. If you are doing many
|
||||
exponentiations with a single fixed modulus, it is faster to use a
|
||||
``Power_Mod`` implementation.
|
||||
|
||||
.. cpp:function:: BigInt ressol(BigInt x, BigInt p)
|
||||
|
||||
Returns the square root modulo a prime, that is, returns a number y
|
||||
such that (y*y) % p == x. Returns -1 if no such integer exists.
|
||||
|
||||
.. cpp:function:: bool is_prime(BigInt n, RandomNumberGenerator& rng, \
|
||||
size_t prob = 56, double is_random = false)
|
||||
|
||||
Test *n* for primality using a probabilistic algorithm (Miller-Rabin). With
|
||||
this algorithm, there is some non-zero probability that true will be returned
|
||||
even if *n* is actually composite. Modifying *prob* allows you to decrease the
|
||||
chance of such a false positive, at the cost of increased runtime. Sufficient
|
||||
tests will be run such that the chance *n* is composite is no more than 1 in
|
||||
2\ :sup:`prob`. Set *is_random* to true if (and only if) *n* was randomly
|
||||
chosen (ie, there is no danger it was chosen maliciously) as far fewer tests
|
||||
are needed in that case.
|
||||
|
||||
.. cpp:function:: BigInt random_prime(RandomNumberGenerator& rng, \
|
||||
size_t bits, \
|
||||
BigInt coprime = 1, \
|
||||
size_t equiv = 1, \
|
||||
size_t equiv_mod = 2)
|
||||
|
||||
Return a random prime number of ``bits`` bits long that is
|
||||
relatively prime to ``coprime``, and equivalent to ``equiv`` modulo
|
||||
``equiv_mod``.
|
318
lib/Botan-3.2.0/doc/api_ref/block_cipher.rst
Normal file
318
lib/Botan-3.2.0/doc/api_ref/block_cipher.rst
Normal file
@ -0,0 +1,318 @@
|
||||
Block Ciphers
|
||||
=======================
|
||||
|
||||
Block ciphers are a n-bit permutation for some small n, typically 64 or 128
|
||||
bits. They are a cryptographic primitive used to generate higher level
|
||||
operations such as authenticated encryption.
|
||||
|
||||
.. warning::
|
||||
|
||||
In almost all cases, a bare block cipher is not what you should be using.
|
||||
You probably want an authenticated cipher mode instead (see :ref:`cipher_modes`)
|
||||
This interface is used to build higher level operations (such as cipher
|
||||
modes or MACs), or in the very rare situation where ECB is required,
|
||||
eg for compatibility with an existing system.
|
||||
|
||||
.. cpp:class:: BlockCipher
|
||||
|
||||
.. cpp:function:: static std::unique_ptr<BlockCipher> create(const std::string& algo_spec, \
|
||||
const std::string& provider = "")
|
||||
|
||||
Create a new block cipher object, or else return null.
|
||||
|
||||
.. cpp:function:: static std::unique_ptr<BlockCipher> create_or_throw(const std::string& algo_spec, \
|
||||
const std::string& provider = "")
|
||||
|
||||
Like ``create``, except instead of returning null an exception is thrown
|
||||
if the cipher is not known.
|
||||
|
||||
.. cpp:function:: void set_key(const uint8_t* key, size_t length)
|
||||
|
||||
This sets the key to the value specified. Most algorithms only accept keys
|
||||
of certain lengths. If you attempt to call ``set_key`` with a key length
|
||||
that is not supported, the exception ``Invalid_Key_Length`` will be
|
||||
thrown.
|
||||
|
||||
In all cases, ``set_key`` must be called on an object before any data
|
||||
processing (encryption, decryption, etc) is done by that object. If this
|
||||
is not done, an exception will be thrown.
|
||||
thrown.
|
||||
|
||||
.. cpp:function:: bool valid_keylength(size_t length) const
|
||||
|
||||
This function returns true if and only if *length* is a valid keylength for
|
||||
this algorithm.
|
||||
|
||||
.. cpp:function:: size_t minimum_keylength() const
|
||||
|
||||
Return the smallest key length (in bytes) that is acceptable for the
|
||||
algorithm.
|
||||
|
||||
.. cpp:function:: size_t maximum_keylength() const
|
||||
|
||||
Return the largest key length (in bytes) that is acceptable for the
|
||||
algorithm.
|
||||
|
||||
.. cpp:function:: std::string name() const
|
||||
|
||||
Return a human readable name for this algorithm. This is guaranteed to round-trip with
|
||||
``create`` and ``create_or_throw`` calls, ie create("Foo")->name() == "Foo"
|
||||
|
||||
.. cpp:function:: void clear()
|
||||
|
||||
Zero out the key. The key must be reset before the cipher object can be used.
|
||||
|
||||
.. cpp:function:: std::unique_ptr<BlockCipher> new_object() const
|
||||
|
||||
Return a newly allocated BlockCipher object of the same type as this one.
|
||||
The new object is unkeyed.
|
||||
|
||||
.. cpp:function:: size_t block_size() const
|
||||
|
||||
Return the size (in *bytes*) of the cipher.
|
||||
|
||||
.. cpp:function:: size_t parallelism() const
|
||||
|
||||
Return the parallelism underlying this implementation of the cipher. This
|
||||
value can vary across versions and machines. A return value of N means that
|
||||
encrypting or decrypting with N blocks can operate in parallel.
|
||||
|
||||
.. cpp:function:: size_t parallel_bytes() const
|
||||
|
||||
Returns ``parallelism`` multiplied by the block size as well as a small
|
||||
fudge factor. That's because even ciphers that have no implicit parallelism
|
||||
typically see a small speedup for being called with several blocks due to
|
||||
caching effects.
|
||||
|
||||
.. cpp:function:: std::string provider() const
|
||||
|
||||
Return the provider type. Default value is "base" but can be any arbitrary string.
|
||||
Other example values are "sse2", "avx2", "openssl".
|
||||
|
||||
.. cpp:function:: void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
|
||||
|
||||
Encrypt *blocks* blocks of data, taking the input from the array *in* and
|
||||
placing the ciphertext into *out*. The two pointers may be identical, but
|
||||
should not overlap ranges.
|
||||
|
||||
.. cpp:function:: void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const
|
||||
|
||||
Decrypt *blocks* blocks of data, taking the input from the array *in* and
|
||||
placing the plaintext into *out*. The two pointers may be identical, but
|
||||
should not overlap ranges.
|
||||
|
||||
.. cpp:function:: void encrypt(const uint8_t in[], uint8_t out[]) const
|
||||
|
||||
Encrypt a single block. Equivalent to :cpp:func:`encrypt_n`\ (in, out, 1).
|
||||
|
||||
.. cpp:function:: void encrypt(uint8_t block[]) const
|
||||
|
||||
Encrypt a single block. Equivalent to :cpp:func:`encrypt_n`\ (block, block, 1)
|
||||
|
||||
.. cpp:function:: void decrypt(const uint8_t in[], uint8_t out[]) const
|
||||
|
||||
Decrypt a single block. Equivalent to :cpp:func:`decrypt_n`\ (in, out, 1)
|
||||
|
||||
.. cpp:function:: void decrypt(uint8_t block[]) const
|
||||
|
||||
Decrypt a single block. Equivalent to :cpp:func:`decrypt_n`\ (block, block, 1)
|
||||
|
||||
.. cpp:function:: template<typename Alloc> void encrypt(std::vector<uint8_t, Alloc>& block) const
|
||||
|
||||
Assumes ``block`` is of a multiple of the block size.
|
||||
|
||||
.. cpp:function:: template<typename Alloc> void decrypt(std::vector<uint8_t, Alloc>& block) const
|
||||
|
||||
Assumes ``block`` is of a multiple of the block size.
|
||||
|
||||
Code Example
|
||||
-----------------
|
||||
|
||||
For sheer demonstrative purposes, the following code encrypts a provided single
|
||||
block of plaintext with AES-256 using two different keys.
|
||||
|
||||
.. literalinclude:: /../src/examples/aes.cpp
|
||||
:language: cpp
|
||||
|
||||
Available Ciphers
|
||||
---------------------
|
||||
|
||||
Botan includes a number of block ciphers that are specific to particular countries, as
|
||||
well as a few that are included mostly due to their use in specific protocols such as PGP
|
||||
but not widely used elsewhere. If you are developing new code and have no particular
|
||||
opinion, use AES-256. If you desire an alternative to AES, consider Serpent, SHACAL2 or
|
||||
Threefish.
|
||||
|
||||
.. warning:: Avoid any 64-bit block cipher in new designs. There are
|
||||
combinatoric issues that affect any 64-bit cipher that render it
|
||||
insecure when large amounts of data are processed.
|
||||
|
||||
AES
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Comes in three variants, AES-128, AES-192, and AES-256.
|
||||
|
||||
The standard 128-bit block cipher. Many modern platforms offer hardware
|
||||
acceleration. However, on platforms without hardware support, AES
|
||||
implementations typically are vulnerable to side channel attacks. For x86
|
||||
systems with SSSE3 but without AES-NI, Botan has an implementation which avoids
|
||||
known side channels.
|
||||
|
||||
Available if ``BOTAN_HAS_AES`` is defined.
|
||||
|
||||
ARIA
|
||||
~~~~~~
|
||||
|
||||
South Korean cipher used in industry there. No reason to use it otherwise.
|
||||
|
||||
Available if ``BOTAN_HAS_ARIA`` is defined.
|
||||
|
||||
Blowfish
|
||||
~~~~~~~~~
|
||||
|
||||
A 64-bit cipher popular in the pre-AES era. Very slow key setup. Also used (with
|
||||
bcrypt) for password hashing.
|
||||
|
||||
Available if ``BOTAN_HAS_BLOWFISH`` is defined.
|
||||
|
||||
CAST-128
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A 64-bit cipher, commonly used in OpenPGP.
|
||||
|
||||
Available if ``BOTAN_HAS_CAST128`` is defined.
|
||||
|
||||
Camellia
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Comes in three variants, Camellia-128, Camellia-192, and Camellia-256.
|
||||
|
||||
A Japanese design standardized by ISO, NESSIE and CRYPTREC.
|
||||
Rarely used outside of Japan.
|
||||
|
||||
Available if ``BOTAN_HAS_CAMELLIA`` is defined.
|
||||
|
||||
Cascade
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Creates a block cipher cascade, where each block is encrypted by two ciphers
|
||||
with independent keys. Useful if you're very paranoid. In practice any single
|
||||
good cipher (such as Serpent, SHACAL2, or AES-256) is more than sufficient.
|
||||
|
||||
Available if ``BOTAN_HAS_CASCADE`` is defined.
|
||||
|
||||
DES and 3DES
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Originally designed by IBM and NSA in the 1970s. Today, DES's 56-bit key renders
|
||||
it insecure to any well-resourced attacker. 3DES extends the key length,
|
||||
and is still thought to be secure, modulo the limitation of a 64-bit block.
|
||||
All are somewhat common in some industries such as finance. Avoid in new code.
|
||||
|
||||
Available if ``BOTAN_HAS_DES`` is defined.
|
||||
|
||||
GOST-28147-89
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Aka "Magma". An old 64-bit Russian cipher. Possible security issues, avoid
|
||||
unless compatibility is needed.
|
||||
|
||||
Available if ``BOTAN_HAS_GOST_28147_89`` is defined.
|
||||
|
||||
.. warning::
|
||||
Support for this cipher is deprecated and will be removed in a future major release.
|
||||
|
||||
IDEA
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
An older but still unbroken 64-bit cipher with a 128-bit key. Somewhat common
|
||||
due to its use in PGP. Avoid in new designs.
|
||||
|
||||
Available if ``BOTAN_HAS_IDEA`` is defined.
|
||||
|
||||
Kuznyechik
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 3.2
|
||||
|
||||
Newer Russian national cipher, also known as GOST R 34.12-2015 or "Grasshopper".
|
||||
|
||||
.. warning::
|
||||
|
||||
The sbox of this cipher is supposedly random, but was found to have a
|
||||
mathematical structure which is exceedingly unlikely to have occured by
|
||||
chance. This may indicate the existence of a backdoor or other issue. Avoid
|
||||
using this cipher unless strictly required.
|
||||
|
||||
Available if ``BOTAN_HAS_KUZNYECHIK`` is defined.
|
||||
|
||||
|
||||
Lion
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A "block cipher construction" which can encrypt blocks of nearly arbitrary
|
||||
length. Built from a stream cipher and a hash function. Useful in certain
|
||||
protocols where being able to encrypt large or arbitrary length blocks is
|
||||
necessary.
|
||||
|
||||
Available if ``BOTAN_HAS_LION`` is defined.
|
||||
|
||||
Noekeon
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A fast 128-bit cipher by the designers of AES. Easily secured against side
|
||||
channels. Quite obscure however.
|
||||
|
||||
Available if ``BOTAN_HAS_NOEKEON`` is defined.
|
||||
|
||||
.. warning::
|
||||
Noekeon support is deprecated and will be removed in a future major release.
|
||||
|
||||
SEED
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A older South Korean cipher, widely used in industry there. No reason to choose it otherwise.
|
||||
|
||||
Available if ``BOTAN_HAS_SEED`` is defined.
|
||||
|
||||
SHACAL2
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The 256-bit block cipher used inside SHA-256. Accepts up to a 512-bit key.
|
||||
Fast, especially when SIMD or SHA-2 acceleration instructions are available.
|
||||
Standardized by NESSIE but otherwise obscure.
|
||||
|
||||
Available if ``BOTAN_HAS_SHACAL2`` is defined.
|
||||
|
||||
SM4
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A 128-bit Chinese national cipher, required for use in certain commercial
|
||||
applications in China. Quite slow. Probably no reason to use it outside of legal
|
||||
requirements.
|
||||
|
||||
Available if ``BOTAN_HAS_SM4`` is defined.
|
||||
|
||||
Serpent
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
An AES contender. Widely considered the most conservative design. Fairly slow
|
||||
unless SIMD instructions are available.
|
||||
|
||||
Available if ``BOTAN_HAS_SERPENT`` is defined.
|
||||
|
||||
Threefish-512
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A 512-bit tweakable block cipher that was used in the Skein hash function.
|
||||
Very fast on 64-bit processors.
|
||||
|
||||
Available if ``BOTAN_HAS_THREEFISH_512`` is defined.
|
||||
|
||||
Twofish
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A 128-bit block cipher that was one of the AES finalists. Has a somewhat complicated key
|
||||
setup and a "kitchen sink" design.
|
||||
|
||||
Available if ``BOTAN_HAS_TWOFISH`` is defined.
|
377
lib/Botan-3.2.0/doc/api_ref/cipher_modes.rst
Normal file
377
lib/Botan-3.2.0/doc/api_ref/cipher_modes.rst
Normal file
@ -0,0 +1,377 @@
|
||||
.. _cipher_modes:
|
||||
|
||||
Cipher Modes
|
||||
=====================
|
||||
|
||||
A block cipher by itself, is only able to securely encrypt a single data block.
|
||||
To be able to securely encrypt data of arbitrary length, a mode of operation
|
||||
applies the block cipher's single block operation repeatedly to encrypt
|
||||
an entire message.
|
||||
|
||||
All cipher mode implementations are are derived from the base class
|
||||
:cpp:class:`Cipher_Mode`, which is declared in ``botan/cipher_mode.h``.
|
||||
|
||||
.. warning::
|
||||
Using an unauthenticted cipher mode without combining it with a
|
||||
:ref:`mac` is insecure. Prefer using an :ref:`aead`.
|
||||
|
||||
.. cpp:class:: Cipher_Mode
|
||||
|
||||
.. cpp:function:: void set_key(const uint8_t* key, size_t length)
|
||||
|
||||
Set the symmetric key to be used.
|
||||
|
||||
.. cpp:function:: bool valid_keylength(size_t length) const
|
||||
|
||||
This function returns true if and only if *length* is a valid
|
||||
keylength for the algorithm.
|
||||
|
||||
.. cpp:function:: size_t minimum_keylength() const
|
||||
|
||||
Return the smallest key length (in bytes) that is acceptable for the
|
||||
algorithm.
|
||||
|
||||
.. cpp:function:: size_t maximum_keylength() const
|
||||
|
||||
Return the largest key length (in bytes) that is acceptable for the
|
||||
algorithm.
|
||||
|
||||
.. cpp:function:: size_t default_nonce_length() const
|
||||
|
||||
Return the default (preferable) nonce size for this cipher mode.
|
||||
|
||||
.. cpp:function:: bool valid_nonce_length(size_t nonce_len) const
|
||||
|
||||
Return true if *nonce_len* is a valid length for a nonce with this
|
||||
algorithm.
|
||||
|
||||
.. cpp:function:: bool authenticated() const
|
||||
|
||||
Return true if this cipher mode is authenticated
|
||||
|
||||
.. cpp:function:: size_t tag_size() const
|
||||
|
||||
Return the length in bytes of the authentication tag this algorithm
|
||||
generates. If the mode is not authenticated, this will return 0. If the mode
|
||||
is authenticated, it will return some positive value (typically somewhere
|
||||
between 8 and 16).
|
||||
|
||||
.. cpp:function:: void clear()
|
||||
|
||||
Clear all internal state. The object will act exactly like one which was
|
||||
just allocated.
|
||||
|
||||
.. cpp:function:: void reset()
|
||||
|
||||
Reset all message state. For example if you called :cpp:func:`start_msg`,
|
||||
then :cpp:func:`process` to process some ciphertext, but then encounter an
|
||||
IO error and must abandon the current message, you can call `reset`. The
|
||||
object will retain the key (unlike calling :cpp:func:`clear` which also
|
||||
resets the key) but the nonce and current message state will be erased.
|
||||
|
||||
.. cpp:function:: void start_msg(const uint8_t* nonce, size_t nonce_len)
|
||||
|
||||
Set up for processing a new message. This function must be called with a new
|
||||
random value for each message. For almost all modes (excepting SIV), if the
|
||||
same nonce is ever used twice with the same key, the encryption scheme loses
|
||||
its confidentiality and/or authenticity properties.
|
||||
|
||||
.. cpp:function:: void start(const std::vector<uint8_t> nonce)
|
||||
|
||||
Acts like :cpp:func:`start_msg`\ (nonce.data(), nonce.size()).
|
||||
|
||||
.. cpp:function:: void start(const uint8_t* nonce, size_t nonce_len)
|
||||
|
||||
Acts like :cpp:func:`start_msg`\ (nonce, nonce_len).
|
||||
|
||||
.. cpp:function:: virtual size_t update_granularity() const
|
||||
|
||||
The :cpp:class:`Cipher_Mode` interface requires message processing in multiples of the block size.
|
||||
Returns size of required blocks to update. Will return 1 if the mode implementation
|
||||
does not require buffering.
|
||||
|
||||
.. cpp:function:: virtual size_t ideal_granularity() const
|
||||
|
||||
Returns a multiple of update_granularity sized for ideal performance.
|
||||
|
||||
In fact this is not truly the "ideal" buffer size but just reflects the
|
||||
smallest possible buffer that can reasonably take advantage of available
|
||||
parallelism (due to SIMD execution, etc). If you are concerned about
|
||||
performance, it may be advisable to take this return value and scale it to
|
||||
approximately 4 KB, and use buffers of that size.
|
||||
|
||||
.. cpp:function:: virtual size_t process(uint8_t* msg, size_t msg_len)
|
||||
|
||||
Process msg in place and returns the number of bytes written. *msg* must
|
||||
be a multiple of :cpp:func:`update_granularity`.
|
||||
|
||||
.. cpp:function:: void update(secure_vector<uint8_t>& buffer, size_t offset = 0)
|
||||
|
||||
Continue processing a message in the buffer in place. The passed buffer's
|
||||
size must be a multiple of :cpp:func:`update_granularity`. The first
|
||||
*offset* bytes of the buffer will be ignored.
|
||||
|
||||
.. cpp:function:: size_t minimum_final_size() const
|
||||
|
||||
Returns the minimum size needed for :cpp:func:`finish`. This is used for
|
||||
example when processing an AEAD message, to ensure the tag is available. In
|
||||
that case, the encryption side will return 0 (since the tag is generated,
|
||||
rather than being provided) while the decryption mode will return the size
|
||||
of the tag.
|
||||
|
||||
.. cpp:function:: void finish(secure_vector<uint8_t>& final_block, size_t offset = 0)
|
||||
|
||||
Finalize the message processing with a final block of at least :cpp:func:`minimum_final_size` size.
|
||||
The first *offset* bytes of the passed final block will be ignored.
|
||||
|
||||
Code Example
|
||||
---------------------
|
||||
|
||||
The following code encrypts the specified plaintext using AES-128/CBC
|
||||
with PKCS#7 padding.
|
||||
|
||||
.. warning::
|
||||
This example ignores the requirement to authenticate the ciphertext
|
||||
|
||||
.. note::
|
||||
Simply replacing the string "AES-128/CBC/PKCS7" string in the example below
|
||||
with "AES-128/GCM" suffices to use authenticated encryption.
|
||||
|
||||
.. literalinclude:: /../src/examples/aes_cbc.cpp
|
||||
:language: cpp
|
||||
|
||||
|
||||
Available Unauthenticated Cipher Modes
|
||||
-----------------------------------------
|
||||
|
||||
.. note::
|
||||
CTR and OFB modes are also implemented, but these are treated as
|
||||
:cpp:class:`Stream_Cipher`\s instead.
|
||||
|
||||
CBC
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Available if ``BOTAN_HAS_MODE_CBC`` is defined.
|
||||
|
||||
CBC requires the plaintext be padded using a reversible rule. The following
|
||||
padding schemes are implemented
|
||||
|
||||
PKCS#7 (RFC5652)
|
||||
The last byte in the padded block defines the padding length p, the remaining padding bytes are set to p as well.
|
||||
ANSI X9.23
|
||||
The last byte in the padded block defines the padding length, the remaining padding is filled with 0x00.
|
||||
OneAndZeros (ISO/IEC 7816-4)
|
||||
The first padding byte is set to 0x80, the remaining padding bytes are set to 0x00.
|
||||
|
||||
Ciphertext stealing (CTS) is also implemented. This scheme allows the
|
||||
ciphertext to have the same length as the plaintext, however using CTS
|
||||
requires the input be at least one full block plus one byte. It is
|
||||
also less commonly implemented.
|
||||
|
||||
.. warning::
|
||||
Using CBC with padding without an authentication mode exposes your
|
||||
application to CBC padding oracle attacks, which allow recovering
|
||||
the plaintext of arbitrary messages. Always pair CBC with a MAC such
|
||||
as HMAC (or, preferably, use an AEAD such as GCM).
|
||||
|
||||
CFB
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Available if ``BOTAN_HAS_MODE_CFB`` is defined.
|
||||
|
||||
CFB uses a block cipher to create a self-synchronizing stream cipher. It is used
|
||||
for example in the OpenPGP protocol. There is no reason to prefer it, as it has
|
||||
worse performance characteristics than modes such as CTR or CBC.
|
||||
|
||||
XTS
|
||||
~~~~~~~~~
|
||||
|
||||
Available if ``BOTAN_HAS_MODE_XTS`` is defined.
|
||||
|
||||
XTS is a mode specialized for encrypting disk or database storage
|
||||
where ciphertext expansion is not possible. XTS requires all inputs be
|
||||
at least one full block (16 bytes for AES), however for any acceptable
|
||||
input length, there is no ciphertext expansion.
|
||||
|
||||
.. _aead:
|
||||
|
||||
AEAD Mode
|
||||
---------------------------
|
||||
|
||||
AEAD (Authenticated Encryption with Associated Data) modes provide message
|
||||
encryption, message authentication, and the ability to authenticate additional
|
||||
data that is not included in the ciphertext (such as a sequence number or
|
||||
header). It is a subclass of :cpp:class:`Cipher_Mode`.
|
||||
|
||||
.. cpp:class:: AEAD_Mode
|
||||
|
||||
.. cpp:function:: void set_key(const SymmetricKey& key)
|
||||
|
||||
Set the key
|
||||
|
||||
.. cpp:function:: Key_Length_Specification key_spec() const
|
||||
|
||||
Return the key length specification
|
||||
|
||||
.. cpp:function:: void set_associated_data(const uint8_t ad[], size_t ad_len)
|
||||
|
||||
Set any associated data for this message. For maximum portability between
|
||||
different modes, this must be called after :cpp:func:`set_key` and before
|
||||
:cpp:func:`start`.
|
||||
|
||||
If the associated data does not change, it is not necessary to call this
|
||||
function more than once, even across multiple calls to :cpp:func:`start`
|
||||
and :cpp:func:`finish`.
|
||||
|
||||
.. cpp:function:: void start(const uint8_t nonce[], size_t nonce_len)
|
||||
|
||||
Start processing a message, using *nonce* as the unique per-message
|
||||
value. It does not need to be random, simply unique (per key).
|
||||
|
||||
.. warning::
|
||||
With almost all AEADs, if the same nonce is ever used to encrypt two
|
||||
different messages under the same key, all security is lost. If
|
||||
reliably generating unique nonces is difficult in your environment,
|
||||
use SIV mode which retains security even if nonces are repeated.
|
||||
|
||||
.. cpp:function:: void update(secure_vector<uint8_t>& buffer, size_t offset = 0)
|
||||
|
||||
Continue processing a message. The *buffer* is an in/out parameter and
|
||||
may be resized. In particular, some modes require that all input be
|
||||
consumed before any output is produced; with these modes, *buffer* will
|
||||
be returned empty.
|
||||
|
||||
On input, the buffer must be sized in blocks of size
|
||||
:cpp:func:`update_granularity`. For instance if the update granularity
|
||||
was 64, then *buffer* could be 64, 128, 192, ... bytes.
|
||||
|
||||
The first *offset* bytes of *buffer* will be ignored (this allows in
|
||||
place processing of a buffer that contains an initial plaintext header)
|
||||
|
||||
.. cpp:function:: void finish(secure_vector<uint8_t>& buffer, size_t offset = 0)
|
||||
|
||||
Complete processing a message with a final input of *buffer*, which is
|
||||
treated the same as with :cpp:func:`update`. It must contain at least
|
||||
:cpp:func:`final_minimum_size` bytes.
|
||||
|
||||
Note that if you have the entire message in hand, calling finish without
|
||||
ever calling update is both efficient and convenient.
|
||||
|
||||
.. note::
|
||||
|
||||
During decryption, if the supplied authentication tag does not
|
||||
validate, finish will throw an instance of Invalid_Authentication_Tag
|
||||
(aka Integrity_Failure, which was the name for this exception in
|
||||
versions before 2.10, a typedef is included for compatability).
|
||||
|
||||
If this occurs, all plaintext previously output via calls to update
|
||||
must be destroyed and not used in any way that an attacker could
|
||||
observe the effects of. This could be anything from echoing the
|
||||
plaintext back (perhaps in an error message), or by making an external
|
||||
RPC whose destination or contents depend on the plaintext. The only
|
||||
thing you can do is buffer it, and in the event of an invalid tag,
|
||||
erase the previously decrypted content from memory.
|
||||
|
||||
One simply way to assure this could never happen is to never
|
||||
call update, and instead always marshal the entire message
|
||||
into a single buffer and call finish on it when decrypting.
|
||||
|
||||
.. cpp:function:: size_t update_granularity() const
|
||||
|
||||
The AEAD interface requires :cpp:func:`update` be called with blocks of
|
||||
this size. This will be 1, if the mode can process any length inputs.
|
||||
|
||||
.. cpp:function:: size_t final_minimum_size() const
|
||||
|
||||
The AEAD interface requires :cpp:func:`finish` be called with at least
|
||||
this many bytes (which may be zero, or greater than
|
||||
:cpp:func:`update_granularity`)
|
||||
|
||||
.. cpp:function:: bool valid_nonce_length(size_t nonce_len) const
|
||||
|
||||
Returns true if *nonce_len* is a valid nonce length for this scheme. For
|
||||
EAX and GCM, any length nonces are allowed. OCB allows any value between
|
||||
8 and 15 bytes.
|
||||
|
||||
.. cpp:function:: size_t default_nonce_length() const
|
||||
|
||||
Returns a reasonable length for the nonce, typically either 96
|
||||
bits, or the only supported length for modes which don't
|
||||
support 96 bit nonces.
|
||||
|
||||
|
||||
Available AEAD Modes
|
||||
-------------------------
|
||||
|
||||
If in doubt about what to use, pick ChaCha20Poly1305, AES-256/GCM, or AES-256/SIV.
|
||||
Both ChaCha20Poly1305 and AES with GCM are widely implemented. SIV is somewhat
|
||||
more obscure (and is slower than either GCM or ChaCha20Poly1305), but has
|
||||
excellent security properties.
|
||||
|
||||
ChaCha20Poly1305
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Available if ``BOTAN_HAS_AEAD_CHACHA20_POLY1305`` is defined.
|
||||
|
||||
Unlike the other AEADs which are based on block ciphers, this mode is based on
|
||||
the ChaCha stream cipher and the Poly1305 authentication code. It is very fast
|
||||
on all modern platforms.
|
||||
|
||||
ChaCha20Poly1305 supports 64-bit, 96-bit, and (since 2.8) 192-bit nonces. 64-bit nonces
|
||||
are the "classic" ChaCha20Poly1305 design. 96-bit nonces are used by the IETF standard
|
||||
version of ChaCha20Poly1305. And 192-bit nonces is the XChaCha20Poly1305 construction,
|
||||
which is somewhat less common.
|
||||
|
||||
For best interop use the IETF version with 96-bit nonces. However 96 bits is small enough
|
||||
that it can be dangerous to generate nonces randomly if more than ~ 2^32 messages are
|
||||
encrypted under a single key, since if a nonce is ever reused ChaCha20Poly1305 becomes
|
||||
insecure. It is better to use a counter for the nonce in this case.
|
||||
|
||||
If you are encrypting many messages under a single key and cannot maintain a counter for
|
||||
the nonce, prefer XChaCha20Poly1305 since a 192 bit nonce is large enough that randomly
|
||||
chosen nonces are extremely unlikely to repeat.
|
||||
|
||||
GCM
|
||||
~~~~~
|
||||
|
||||
Available if ``BOTAN_HAS_AEAD_GCM`` is defined.
|
||||
|
||||
NIST standard, commonly used. Requires a 128-bit block cipher. Fairly slow,
|
||||
unless hardware support for carryless multiplies is available.
|
||||
|
||||
OCB
|
||||
~~~~~
|
||||
|
||||
Available if ``BOTAN_HAS_AEAD_OCB`` is defined.
|
||||
|
||||
A block cipher based AEAD. Supports 128-bit, 256-bit and 512-bit block ciphers.
|
||||
This mode is very fast and easily secured against side channels. Adoption has
|
||||
been poor because until 2021 it was patented in the United States. The patent
|
||||
was allowed to lapse in early 2021.
|
||||
|
||||
EAX
|
||||
~~~~~
|
||||
|
||||
Available if ``BOTAN_HAS_AEAD_EAX`` is defined.
|
||||
|
||||
A secure composition of CTR mode and CMAC. Supports 128-bit, 256-bit and 512-bit
|
||||
block ciphers.
|
||||
|
||||
SIV
|
||||
~~~~~~
|
||||
|
||||
Available if ``BOTAN_HAS_AEAD_SIV`` is defined.
|
||||
|
||||
Requires a 128-bit block cipher. Unlike other AEADs, SIV is "misuse resistant";
|
||||
if a nonce is repeated, SIV retains security, with the exception that if the
|
||||
same nonce is used to encrypt the same message multiple times, an attacker can
|
||||
detect the fact that the message was duplicated (this is simply because if both
|
||||
the nonce and the message are reused, SIV will output identical ciphertexts).
|
||||
|
||||
CCM
|
||||
~~~~~
|
||||
|
||||
Available if ``BOTAN_HAS_AEAD_CCM`` is defined.
|
||||
|
||||
A composition of CTR mode and CBC-MAC. Requires a 128-bit block cipher. This is
|
||||
a NIST standard mode, but that is about all to recommend it. Prefer EAX.
|
97
lib/Botan-3.2.0/doc/api_ref/compression.rst
Normal file
97
lib/Botan-3.2.0/doc/api_ref/compression.rst
Normal file
@ -0,0 +1,97 @@
|
||||
Lossless Data Compression
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Some lossless data compression algorithms are available in botan, currently all
|
||||
via third party libraries - these include zlib (including deflate and gzip
|
||||
formats), bzip2, and lzma. Support for these must be enabled at build time;
|
||||
you can check for them using the macros ``BOTAN_HAS_ZLIB``, ``BOTAN_HAS_BZIP2``,
|
||||
and ``BOTAN_HAS_LZMA``.
|
||||
|
||||
.. note::
|
||||
You should always compress *before* you encrypt, because encryption seeks to
|
||||
hide the redundancy that compression is supposed to try to find and remove.
|
||||
|
||||
Compression is done through the ``Compression_Algorithm`` and
|
||||
``Decompression_Algorithm`` classes, both defined in `compression.h`
|
||||
|
||||
Compression and decompression both work in three stages: starting a
|
||||
message (``start``), continuing to process it (``update``), and then
|
||||
finally completing processing the stream (``finish``).
|
||||
|
||||
.. cpp:class:: Compression_Algorithm
|
||||
|
||||
.. cpp:function:: void start(size_t level)
|
||||
|
||||
Initialize the compression engine. This must be done before calling
|
||||
``update`` or ``finish``. The meaning of the `level` parameter varies by
|
||||
the algorithm but generally takes a value between 1 and 9, with higher
|
||||
values implying typically better compression from and more memory and/or
|
||||
CPU time consumed by the compression process. The decompressor can always
|
||||
handle input from any compressor.
|
||||
|
||||
.. cpp:function:: void update(secure_vector<uint8_t>& buf, \
|
||||
size_t offset = 0, bool flush = false)
|
||||
|
||||
Compress the material in the in/out parameter ``buf``. The leading
|
||||
``offset`` bytes of ``buf`` are ignored and remain untouched; this can be
|
||||
useful for ignoring packet headers. If ``flush`` is true, the
|
||||
compression state is flushed, allowing the decompressor to recover the
|
||||
entire message up to this point without having the see the rest of the
|
||||
compressed stream.
|
||||
|
||||
.. cpp::function:: void finish(secure_vector<uint8_t>& buf, size_t offset = 0)
|
||||
|
||||
Finish compressing a message. The ``buf`` and ``offset`` parameters are
|
||||
treated as in ``update``. It is acceptable to call ``start`` followed by
|
||||
``finish`` with the entire message, without any intervening call to
|
||||
``update``.
|
||||
|
||||
.. cpp:class:: Decompression_Algorithm
|
||||
|
||||
.. cpp:function:: void start()
|
||||
|
||||
Initialize the decompression engine. This must be done before calling
|
||||
``update`` or ``finish``. No level is provided here; the decompressor
|
||||
can accept input generated by any compression parameters.
|
||||
|
||||
.. cpp:function:: void update(secure_vector<uint8_t>& buf, \
|
||||
size_t offset = 0)
|
||||
|
||||
Decompress the material in the in/out parameter ``buf``. The leading
|
||||
``offset`` bytes of ``buf`` are ignored and remain untouched; this can be
|
||||
useful for ignoring packet headers.
|
||||
|
||||
This function may throw if the data seems to be invalid.
|
||||
|
||||
.. cpp::function:: void finish(secure_vector<uint8_t>& buf, size_t offset = 0)
|
||||
|
||||
Finish decompressing a message. The ``buf`` and ``offset`` parameters are
|
||||
treated as in ``update``. It is acceptable to call ``start`` followed by
|
||||
``finish`` with the entire message, without any intervening call to
|
||||
``update``.
|
||||
|
||||
This function may throw if the data seems to be invalid.
|
||||
|
||||
The easiest way to get a compressor is via the functions
|
||||
``Compression_Algorithm::create`` and
|
||||
``Decompression_Algorithm::create`` which both accept a string
|
||||
argument which can take values include `zlib` (raw zlib with no
|
||||
checksum), `deflate` (zlib's deflate format), `gzip`, `bz2`, and
|
||||
`lzma`. A null pointer will be returned if the algorithm is
|
||||
unavailable.
|
||||
|
||||
Two older functions for this are
|
||||
|
||||
.. cpp:function:: Compression_Algorithm* make_compressor(std::string type)
|
||||
.. cpp:function:: Decompression_Algorithm* make_decompressor(std::string type)
|
||||
|
||||
which call the relevant ``create`` function and then ``release`` the
|
||||
returned ``unique_ptr``. Avoid these in new code.
|
||||
|
||||
To use a compression algorithm in a `Pipe` use the adapter types
|
||||
`Compression_Filter` and `Decompression_Filter` from `comp_filter.h`. The
|
||||
constructors of both filters take a `std::string` argument (passed to
|
||||
`make_compressor` or `make_decompressor`), the compression filter also takes a
|
||||
`level` parameter. Finally both constructors have a parameter `buf_sz` which
|
||||
specifies the size of the internal buffer that will be used - inputs will be
|
||||
broken into blocks of this size. The default is 4096.
|
41
lib/Botan-3.2.0/doc/api_ref/contents.rst
Normal file
41
lib/Botan-3.2.0/doc/api_ref/contents.rst
Normal file
@ -0,0 +1,41 @@
|
||||
|
||||
API Reference
|
||||
===================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
footguns
|
||||
versions
|
||||
secmem
|
||||
rng
|
||||
hash
|
||||
block_cipher
|
||||
stream_ciphers
|
||||
message_auth_codes
|
||||
cipher_modes
|
||||
pubkey
|
||||
x509
|
||||
tls
|
||||
credentials_manager
|
||||
bigint
|
||||
kdf
|
||||
pbkdf
|
||||
keywrap
|
||||
passhash
|
||||
cryptobox
|
||||
srp
|
||||
psk_db
|
||||
filters
|
||||
fpe
|
||||
tss
|
||||
ecc
|
||||
compression
|
||||
pkcs11
|
||||
tpm
|
||||
otp
|
||||
roughtime
|
||||
zfec
|
||||
ffi
|
||||
env_vars
|
||||
python
|
197
lib/Botan-3.2.0/doc/api_ref/credentials_manager.rst
Normal file
197
lib/Botan-3.2.0/doc/api_ref/credentials_manager.rst
Normal file
@ -0,0 +1,197 @@
|
||||
|
||||
Credentials Manager
|
||||
==================================================
|
||||
|
||||
A ``Credentials_Manager`` is a way to abstract how the application
|
||||
stores credentials. The main user is the :doc:`tls` implementation.
|
||||
|
||||
.. cpp:class:: Credentials_Manager
|
||||
|
||||
.. cpp:function:: std::vector<Certificate_Store*> \
|
||||
trusted_certificate_authorities( \
|
||||
const std::string& type, \
|
||||
const std::string& context)
|
||||
|
||||
Return the list of certificate stores, each of which is assumed
|
||||
to contain (only) trusted certificate authorities. The
|
||||
``Credentials_Manager`` retains ownership of the
|
||||
Certificate_Store pointers.
|
||||
|
||||
.. note::
|
||||
|
||||
It would have been a better API to return a vector of
|
||||
``shared_ptr`` here. This may change in a future major release.
|
||||
|
||||
When *type* is "tls-client", *context* will be the hostname of
|
||||
the server, or empty if the hostname is not known. This allows
|
||||
using a different set of certificate stores in different contexts,
|
||||
for example using the system certificate store unless contacting
|
||||
one particular server which uses a cert issued by an internal CA.
|
||||
|
||||
When *type* is "tls-server", the *context* will again be the
|
||||
hostname of the server, or empty if the client did not send a
|
||||
server name indicator. For TLS servers, these CAs are the ones
|
||||
trusted for signing of client certificates. If you do not want
|
||||
the TLS server to ask for a client cert,
|
||||
``trusted_certificate_authorities`` should return an empty list
|
||||
for *type* "tls-server".
|
||||
|
||||
The default implementation returns an empty list.
|
||||
|
||||
.. cpp:function:: std::vector<X509_Certificate> find_cert_chain( \
|
||||
const std::vector<std::string>& cert_key_types, \
|
||||
const std::vector<X509_DN>& acceptable_CAs, \
|
||||
const std::string& type, \
|
||||
const std::string& context)
|
||||
|
||||
Return the certificate chain to use to identify ourselves. The
|
||||
``acceptable_CAs`` parameter gives a list of CAs the peer trusts.
|
||||
This may be empty.
|
||||
|
||||
.. warning::
|
||||
If this function returns a certificate that is not one of the
|
||||
types given in ``cert_key_types`` confusing handshake
|
||||
failures will result.
|
||||
|
||||
.. cpp:function:: std::vector<X509_Certificate> cert_chain( \
|
||||
const std::vector<std::string>& cert_key_types, \
|
||||
const std::string& type, \
|
||||
const std::string& context)
|
||||
|
||||
Return the certificate chain to use to identify ourselves. Starting in
|
||||
2.5, prefer ``find_cert_chain`` which additionally provides the CA list.
|
||||
|
||||
.. cpp:function:: std::vector<X509_Certificate> cert_chain_single_type( \
|
||||
const std::string& cert_key_type, \
|
||||
const std::string& type, \
|
||||
const std::string& context)
|
||||
|
||||
Return the certificate chain to use to identifier ourselves, if
|
||||
we have one of type *cert_key_type* and we would like to use a
|
||||
certificate in this *type*/*context*.
|
||||
|
||||
For servers *type* will be "tls-server" and the *context* will
|
||||
be the server name that the client requested via SNI (or empty,
|
||||
if the client did not send SNI).
|
||||
|
||||
.. warning::
|
||||
|
||||
To avoid cross-protocol attacks it is recommended that if a server
|
||||
receives an SNI request for a name it does not expect, it should close
|
||||
the connection with an alert. This can be done by throwing an exception
|
||||
from the implementation of this function.
|
||||
|
||||
.. cpp:function:: std::shared_ptr<Private_Key> private_key_for(const X509_Certificate& cert, \
|
||||
const std::string& type, \
|
||||
const std::string& context)
|
||||
|
||||
Return a shared pointer to the private key for this certificate. The
|
||||
*cert* will be the leaf cert of a chain returned previously by
|
||||
``cert_chain`` or ``cert_chain_single_type``.
|
||||
|
||||
In versions before 1.11.34, there was an additional function on `Credentials_Manager`
|
||||
|
||||
.. cpp::function:: void verify_certificate_chain( \
|
||||
const std::string& type, \
|
||||
const std::string& hostname, \
|
||||
const std::vector<X509_Certificate>& cert_chain)
|
||||
|
||||
This function has been replaced by `TLS::Callbacks::tls_verify_cert_chain`.
|
||||
|
||||
SRP Authentication
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
``Credentials_Manager`` contains the hooks used by TLS clients and
|
||||
servers for SRP authentication.
|
||||
|
||||
.. note::
|
||||
|
||||
Support for TLS-SRP is deprecated, and will be removed in a future
|
||||
major release. When that occurs these APIs will be removed. Prefer
|
||||
instead performing a standard TLS handshake, then perform a PAKE
|
||||
authentication inside of (and cryptographically bound to) the TLS
|
||||
channel.
|
||||
|
||||
.. cpp:function:: bool attempt_srp(const std::string& type, \
|
||||
const std::string& context)
|
||||
|
||||
Returns if we should consider using SRP for authentication
|
||||
|
||||
.. cpp:function:: std::string srp_identifier(const std::string& type, \
|
||||
const std::string& context)
|
||||
|
||||
Returns the SRP identifier we'd like to use (used by client)
|
||||
|
||||
.. cpp:function:: std::string srp_password(const std::string& type, \
|
||||
const std::string& context, \
|
||||
const std::string& identifier)
|
||||
|
||||
Returns the password for *identifier* (used by client)
|
||||
|
||||
.. cpp:function:: bool srp_verifier(const std::string& type, \
|
||||
const std::string& context, \
|
||||
const std::string& identifier, \
|
||||
std::string& group_name, \
|
||||
BigInt& verifier, \
|
||||
std::vector<uint8_t>& salt, \
|
||||
bool generate_fake_on_unknown)
|
||||
|
||||
Returns the SRP verifier information for *identifier* (used by server)
|
||||
|
||||
Preshared Keys
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
TLS supports the use of pre shared keys for authentication.
|
||||
|
||||
.. cpp:function:: SymmetricKey psk(const std::string& type, \
|
||||
const std::string& context, \
|
||||
const std::string& identity)
|
||||
|
||||
Return a symmetric key for use with *identity*
|
||||
|
||||
One important special case for ``psk`` is where *type* is
|
||||
"tls-server", *context* is "session-ticket" and *identity* is an
|
||||
empty string. If a key is returned for this case, a TLS server
|
||||
will offer session tickets to clients who can use them, and the
|
||||
returned key will be used to encrypt the ticket. The server is
|
||||
allowed to change the key at any time (though changing the key
|
||||
means old session tickets can no longer be used for resumption,
|
||||
forcing a full re-handshake when the client next connects). One
|
||||
simple approach to add support for session tickets in your server
|
||||
is to generate a random key the first time ``psk`` is called to
|
||||
retrieve the session ticket key, cache it for later use in the
|
||||
``Credentials_Manager``, and simply let it be thrown away when the
|
||||
process terminates. See :rfc:`4507` for more information about TLS
|
||||
session tickets.
|
||||
|
||||
A similar special case exists for DTLS cookie verification. In
|
||||
this case *type* will be "tls-server" and *context* is
|
||||
"dtls-cookie-secret". If no key is returned, then DTLS cookies are
|
||||
not used. Similar to the session ticket key, the DTLS cookie
|
||||
secret can be chosen during server startup and rotated at any time
|
||||
with no ill effect.
|
||||
|
||||
.. warning::
|
||||
|
||||
If DTLS cookies are not used then the server is prone to be
|
||||
abused as a DoS amplifier, where the attacker sends a
|
||||
relatively small client hello in a UDP packet with a forged
|
||||
return address, and then the server replies to the victim with
|
||||
several messages that are larger. This not only hides the
|
||||
attackers address from the victim, but increases their
|
||||
effective bandwidth. This is not an issue when using DTLS over
|
||||
SCTP or TCP.
|
||||
|
||||
.. cpp:function:: std::string psk_identity_hint(const std::string& type, \
|
||||
const std::string& context)
|
||||
|
||||
Returns an identity hint which may be provided to the client. This
|
||||
can help a client understand what PSK to use.
|
||||
|
||||
.. cpp:function:: std::string psk_identity(const std::string& type, \
|
||||
const std::string& context, \
|
||||
const std::string& identity_hint)
|
||||
|
||||
Returns the identity we would like to use given this *type* and
|
||||
*context* and the optional *identity_hint*. Not all servers or
|
||||
protocols will provide a hint.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user