commit 3ff46723b8cfec1d5c04bee8928809930dd20ce5 Author: Scott E. Graves Date: Sat Mar 5 00:30:50 2022 -0600 initial commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..9927ff03 --- /dev/null +++ b/.clang-format @@ -0,0 +1,127 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +... + diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 00000000..09cf22ac --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,38 @@ +--- +Checks: 'clang-diagnostic-*,clang-analyzer-*' +WarningsAsErrors: '' +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: none +User: sgraves +CheckOptions: + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: '0' + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: '1' + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: '1' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-use-nullptr.NullMacros + value: 'NULL' +... + diff --git a/.default_session b/.default_session new file mode 100644 index 00000000..65ed8f6d --- /dev/null +++ b/.default_session @@ -0,0 +1 @@ +{"breakpoints": {"line": {"/data/src/repertory/src/main.cpp": [{"state": "ENABLED", "line": 59, "options": {}}]}, "function": [], "exception": null}, "session": {"user_choices": {}}, "variables": {}} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..709ac792 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +cmake-build-*/ +.idea/ +build*/ +/3rd_party/winfsp-*/tst/passthrough-cpp/passthrough-cpp.VC.db +/src/version.rc +tag_builds/ +/codelite/ +blockstorage_dev_private.pem +compile_commands.json +.clangd/ +tags +/cacert.pem +/arm64_debian*/ +.cache/ +src/common.cpp diff --git a/.jenkins_linux_builds b/.jenkins_linux_builds new file mode 100644 index 00000000..f7fbdfed --- /dev/null +++ b/.jenkins_linux_builds @@ -0,0 +1,283 @@ +pipeline { + agent none + + environment { + BUILD_ARGS = '--build . -j 8' + CONFIGURE_ARGS = '../.. -DCMAKE_BUILD_TYPE=Release -DREPERTORY_ENABLE_S3=ON' + } + + options { + disableConcurrentBuilds() + } + + stages { + stage('arch') { + agent { + dockerfile { + filename 'arch' + dir 'docker/64_bit' + } + } + + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/arch' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/arch' + } + } + + stage('centos7') { + agent { + dockerfile { + filename 'centos7' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/centos7' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/centos7' + } + } + + stage('debian9') { + agent { + dockerfile { + filename 'debian9' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/debian9' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/debian9' + } + } + + stage('debian10') { + agent { + dockerfile { + filename 'debian10' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/debian10' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/debian10' + } + } + + stage('debian11') { + agent { + dockerfile { + filename 'debian11' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/debian11' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/debian11' + } + } + + stage('fedora29') { + agent { + dockerfile { + filename 'fedora29' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora29' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora29' + } + } + + stage('fedora30') { + agent { + dockerfile { + filename 'fedora30' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora30' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora30' + } + } + + stage('fedora31') { + agent { + dockerfile { + filename 'fedora31' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora31' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora31' + } + } + + stage('fedora32') { + agent { + dockerfile { + filename 'fedora32' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora32' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora32' + } + } + + stage('fedora33') { + agent { + dockerfile { + filename 'fedora33' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora33' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora33' + } + } + + stage('fedora34') { + agent { + dockerfile { + filename 'fedora34' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora34' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora34' + } + } + + stage('fedora35') { + agent { + dockerfile { + filename 'fedora35' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora35' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/fedora35' + } + } + + stage('opensuse15') { + agent { + dockerfile { + filename 'opensuse15' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/opensuse15' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/opensuse15' + } + } + + stage('opensuse15.1') { + agent { + dockerfile { + filename 'opensuse15.1' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/opensuse15.1' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/opensuse15.1' + } + } + + stage('opensuse15.2') { + agent { + dockerfile { + filename 'opensuse15.2' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/opensuse15.2' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/opensuse15.2' + } + } + + stage('opensuse15.3') { + agent { + dockerfile { + filename 'opensuse15.3' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/opensuse15.3' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/opensuse15.3' + } + } + + stage('solus') { + agent any + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/solus' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/solus' + } + } + + stage('tumbleweed') { + agent { + dockerfile { + filename 'tumbleweed' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/tumbleweed' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/tumbleweed' + } + } + + stage('ubuntu18.04') { + agent { + dockerfile { + filename 'ubuntu18.04' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/ubuntu18.04' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/ubuntu18.04' + } + } + + stage('ubuntu20.04') { + agent { + dockerfile { + filename 'ubuntu20.04' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/ubuntu20.04' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/ubuntu20.04' + } + } + + stage('ubuntu21.10') { + agent { + dockerfile { + filename 'ubuntu21.10' + dir 'docker/64_bit' + } + } + steps { + cmake arguments: "${env.CONFIGURE_ARGS}", installation: 'InSearchPath', workingDir: 'build/ubuntu21.10' + cmake arguments: "${env.BUILD_ARGS}", installation: 'InSearchPath', workingDir: 'build/ubuntu21.10' + } + } + } +} diff --git a/.jenkins_osx b/.jenkins_osx new file mode 100644 index 00000000..91abffac --- /dev/null +++ b/.jenkins_osx @@ -0,0 +1,27 @@ +#!groovy + +pipeline { + agent any + + options { + disableConcurrentBuilds() + } + + stages { + stage('configure') { + steps { + cmake arguments: '.. -DCMAKE_BUILD_TYPE=Release -DREPERTORY_ENABLE_S3=ON', installation: 'InSearchPath', workingDir: 'build' + } + } + + stage('build') { + environment { + PATH = "/usr/local/bin:${env.PATH}" + } + + steps { + cmake arguments: '--build . -j 8', installation: 'InSearchPath', workingDir: 'build' + } + } + } +} diff --git a/.jenkins_windows b/.jenkins_windows new file mode 100644 index 00000000..0fea9ab0 --- /dev/null +++ b/.jenkins_windows @@ -0,0 +1,23 @@ +#!groovy + +pipeline { + agent any + + options { + disableConcurrentBuilds() + } + + stages { + stage('configure') { + steps { + cmake arguments: '.. -DCMAKE_BUILD_TYPE=Release -DREPERTORY_ENABLE_S3=ON', installation: 'InSearchPath', workingDir: 'build' + } + } + + stage('build') { + steps { + cmake arguments: '--build . --target ALL_BUILD --config Release -j 4', installation: 'InSearchPath', workingDir: 'build' + } + } + } +} diff --git a/.nvimrc b/.nvimrc new file mode 100644 index 00000000..29609842 --- /dev/null +++ b/.nvimrc @@ -0,0 +1,9 @@ +if has('win32') || has('win64') + let &makeprg=".\\scripts\\make_win32.cmd $*" + let g:gtest#gtest_command = "cd build2 && .\\unittests" +else + let &makeprg="./scripts/make_unix.sh $*" + let g:gtest#gtest_command = "cd build && ./unittests" +endif +set path+=.,include/**,src/**,tests/**,3rd_party/json/**,3rd_party/jsonrpcpp-1.1.1/lib/**,3rd_party/ttmath-0.9.3/ttmath/** +autocmd! VimEnter * :VimspectorLoadSession .default_session diff --git a/.vim/coc-settings.json b/.vim/coc-settings.json new file mode 100644 index 00000000..5cbc8a90 --- /dev/null +++ b/.vim/coc-settings.json @@ -0,0 +1,101 @@ +{ + "cSpell.words": [ + "AWSS", + "BDMV", + "Bignum", + "Bodhi", + "CAWSS", + "CRing", + "DACL", + "DCMAKE", + "DENYNO", + "DIRDB", + "DIRECTORYDB", + "DISTRO", + "FOLDERID", + "HKEY", + "ICOMM", + "IDOWNLOAD", + "IDOWNLOADMANAGER", + "IFUSEDRIVE", + "IPROVIDER", + "IREMOTEJSONINSTANCE", + "IWINFSPDRIVE", + "LOCALAPPDATA", + "LPBYTE", + "LPDWORD", + "LPSTR", + "LPTR", + "LPWSTR", + "MOCKWINFSPDRIVE", + "NOCLOSEPROCESS", + "NTFS", + "NTSTATUS", + "OSSP", + "PSECURITY", + "PUINT", + "PVOID", + "PWSTR", + "Plex", + "REMOTEWINFSP", + "REMOTEWINFSPDRIVE", + "RETRYDB", + "Redistributable", + "SDDL", + "SIAPROVIDER", + "STRCASEEQ", + "STREQ", + "STRNE", + "Skynet", + "TEVENTSYSTEM", + "ULARGE", + "WINFSP", + "WINFSPDRIVE", + "aarch", + "centos", + "cmake", + "cpptools", + "csrf", + "ctim", + "dirh", + "dylib", + "fallocate", + "fgetattr", + "fsetattr", + "fsopen", + "fusefs", + "fusermount", + "futimens", + "gtest", + "hwnd", + "iarchive", + "jsonrpcpp", + "kldload", + "kratos", + "libfuse", + "libhttpserver", + "libmicrohttpd", + "libosxfuse", + "netdev", + "nlohmann", + "noappledouble", + "nopath", + "pfsi", + "reventsp", + "rocksdb", + "runas", + "siadir", + "siapath", + "siasky", + "skylink", + "smatch", + "sopen", + "stbuf", + "struct", + "ttmath", + "unmount", + "usermount", + "utimens", + "utimensat" + ] +} diff --git a/.vimspector.json b/.vimspector.json new file mode 100644 index 00000000..5a34117b --- /dev/null +++ b/.vimspector.json @@ -0,0 +1,20 @@ +{ + "configurations": { + "UnixDebug": { + "adapter": "vscode-cpptools", + "configuration": { + "request": "launch", + "program": "${workspaceRoot}/build/debug/repertory", + "args": ["-f", "-sk", "./skynet_mount"], + "cwd": "${workspaceRoot}/build/debug", + "environment": [], + "externalConsole": true, + "MIMode": "gdb", + "stopAtEntry": true, + "logging": { + "engineLogging": false + } + } + } + } +} diff --git a/3rd_party/cacert.pem b/3rd_party/cacert.pem new file mode 100644 index 00000000..632fc248 --- /dev/null +++ b/3rd_party/cacert.pem @@ -0,0 +1,3311 @@ +## +## Bundle of CA Root Certificates +## +## Certificate data from Mozilla as of: Tue Feb 1 04:12:05 2022 GMT +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## +## Conversion done with mk-ca-bundle.pl version 1.28. +## SHA256: 187ef9dc231135324fe78830cf4462f1ecdeab3e6c9d5e38d623391e88dc5d3c +## + + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx +ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 +XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk +lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB +lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy +lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt +66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn +wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh +D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy +BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie +J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud +DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU +a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv +Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 +UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm +VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK ++JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW +IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 +WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X +f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II +4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 +VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT +EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx +OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg +DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij +KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K +DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv +BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp +p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 +nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX +MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM +Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz +uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT +BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj +YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB +BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD +VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 +ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE +AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV +qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s +hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z +POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 +Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp +8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC +bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu +g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p +vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr +qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw +EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN +MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp +c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq +t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C +jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg +vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF +ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR +AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend +jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO +peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR +7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi +GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 +OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov +L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm +5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr +44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf +Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m +Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp +mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk +vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf +KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br +NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj +viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT +BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X +DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3 +aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644 +N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm ++/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH +6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu +MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h +qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5 +FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs +ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc +celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X +CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB +tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 +cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P +4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F +kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L +3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx +/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa +DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP +e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu +WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ +DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub +DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) Főtanúsítvány +======================================== +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG +EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz +MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu +QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ +03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK +ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU ++zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC +PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT +OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK +F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK +0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ +0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB +leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID +AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ +SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG +NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l +Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga +kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q +hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs +g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 +aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 +nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC +ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo +Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z +WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM +BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE +AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky +MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz +IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ +wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa +by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 +zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f +YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 +oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l +EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 +hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 +EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 +jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY +iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI +WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 +JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx +K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ +Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC +4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo +2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz +lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem +OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 +vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- + +TeliaSonera Root CA v1 +====================== +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE +CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 +MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW +VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ +6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA +3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k +B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn +Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH +oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 +F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ +oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 +gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc +TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB +AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW +DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm +zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW +pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV +G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc +c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT +JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 +qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 +Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems +WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +E-Tugra Certification Authority +=============================== +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNVBAYTAlRSMQ8w +DQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamls +ZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN +ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMw +NTEyMDk0OFoXDTIzMDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmEx +QDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxl +cmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQD +DB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA4vU/kwVRHoViVF56C/UYB4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vd +hQd2h8y/L5VMzH2nPbxHD5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5K +CKpbknSFQ9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEoq1+g +ElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3Dk14opz8n8Y4e0ypQ +BaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcHfC425lAcP9tDJMW/hkd5s3kc91r0 +E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsutdEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gz +rt48Ue7LE3wBf4QOXVGUnhMMti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAq +jqFGOjGY5RH8zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn +rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUXU8u3Zg5mTPj5 +dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6Jyr+zE7S6E5UMA8GA1UdEwEB +/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEG +MA0GCSqGSIb3DQEBCwUAA4ICAQAFNzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAK +kEh47U6YA5n+KGCRHTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jO +XKqYGwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c77NCR807 +VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3+GbHeJAAFS6LrVE1Uweo +a2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WKvJUawSg5TB9D0pH0clmKuVb8P7Sd2nCc +dlqMQ1DujjByTd//SffGqWfZbawCEeI6FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEV +KV0jq9BgoRJP3vQXzTLlyb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gT +Dx4JnW2PAJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpDy4Q0 +8ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8dNL/+I5c30jn6PQ0G +C7TbO6Orb1wdtn7os4I07QZcJA== +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 2 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx +MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ +SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F +vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 +2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV +WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy +YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 +r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf +vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR +3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== +-----END CERTIFICATE----- + +Atos TrustedRoot 2011 +===================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU +cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 +MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG +A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV +hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr +54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ +DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 +HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR +z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R +l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ +bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h +k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh +TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 +61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G +3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +QuoVadis Root CA 1 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE +PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm +PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6 +Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN +ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l +g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV +7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX +9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f +iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg +t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI +hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3 +GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct +Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP ++V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh +3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa +wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6 +O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0 +FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV +hMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +QuoVadis Root CA 2 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh +ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY +NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t +oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o +MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l +V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo +L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ +sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD +6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh +lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI +hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K +pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9 +x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz +dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X +U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw +mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD +zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN +JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr +O3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +QuoVadis Root CA 3 G3 +===================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG +A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv +b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN +MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg +RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286 +IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL +Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe +6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3 +I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U +VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7 +5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi +Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM +dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt +rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI +hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS +t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ +TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du +DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib +Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD +hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX +0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW +dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2 +PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +DigiCert Assured ID Root G2 +=========================== +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw +MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH +35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq +bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw +VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP +YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn +lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO +w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv +0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz +d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW +hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M +jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +DigiCert Assured ID Root G3 +=========================== +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD +VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb +RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs +KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF +UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy +YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy +1vUhZscv6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +DigiCert Global Root G2 +======================= +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx +MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ +kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO +3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV +BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM +UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB +o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu +5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr +F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U +WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH +QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/ +iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +DigiCert Global Root G3 +======================= +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD +VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw +MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k +aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C +AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O +YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp +Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y +3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34 +VOKa5Vt8sycX +-----END CERTIFICATE----- + +DigiCert Trusted Root G4 +======================== +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw +HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 +MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp +pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o +k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa +vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY +QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6 +MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm +mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7 +f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH +dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8 +oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY +ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr +yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy +7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah +ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN +5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb +/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa +5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK +G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP +82Z+ +-----END CERTIFICATE----- + +COMODO RSA Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn +dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ +FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+ +5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG +x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX +2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL +OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3 +sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C +GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5 +WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt +rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+ +nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg +tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW +sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp +pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA +zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq +ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52 +7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I +LaZRfyHBNVOFBkpdn627G190 +-----END CERTIFICATE----- + +USERTrust RSA Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE +BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK +ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz +0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j +Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn +RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O ++T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq +/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE +Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM +lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8 +yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+ +eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW +FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ +7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ +Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM +8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi +FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi +yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c +J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw +sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx +Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +USERTrust ECC Certification Authority +===================================== +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC +VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2 +0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez +nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV +HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB +HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu +9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R5 +=========================== +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb +R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD +EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6 +SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS +h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx +uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7 +yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +Staat der Nederlanden EV Root CA +================================ +-----BEGIN CERTIFICATE----- +MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJOTDEeMBwGA1UE +CgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFhdCBkZXIgTmVkZXJsYW5kZW4g +RVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0yMjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5M +MR4wHAYDVQQKDBVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRl +cmxhbmRlbiBFViBSb290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkk +SzrSM4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nCUiY4iKTW +O0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3dZ//BYY1jTw+bbRcwJu+r +0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46prfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8 +Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13lpJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gV +XJrm0w912fxBmJc+qiXbj5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr +08C+eKxCKFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS/ZbV +0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0XcgOPvZuM5l5Tnrmd +74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH1vI4gnPah1vlPNOePqc7nvQDs/nx +fRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrPpx9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwa +ivsnuL8wbqg7MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI +eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u2dfOWBfoqSmu +c0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHSv4ilf0X8rLiltTMMgsT7B/Zq +5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTCwPTxGfARKbalGAKb12NMcIxHowNDXLldRqAN +b/9Zjr7dn3LDWyvfjFvO5QxGbJKyCqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tN +f1zuacpzEPuKqf2evTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi +5Dp6Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIaGl6I6lD4 +WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeLeG9QgkRQP2YGiqtDhFZK +DyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGy +eUN51q1veieQA6TqJIc/2b3Z6fJfUEkc7uzXLg== +-----END CERTIFICATE----- + +IdenTrust Commercial Root CA 1 +============================== +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS +b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES +MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB +IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld +hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/ +mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi +1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C +XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl +3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy +NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV +WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg +xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix +uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI +hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg +ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt +ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV +YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX +feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro +kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe +2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz +Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R +cGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +IdenTrust Public Sector Root CA 1 +================================= +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG +EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv +ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV +UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS +b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy +P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6 +Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI +rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf +qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS +mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn +ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh +LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v +iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL +4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B +Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw +DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A +mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt +GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt +m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx +NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4 +Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI +ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC +ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ +3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +Entrust Root Certification Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy +bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug +b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw +HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT +DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx +OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s +eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP +/vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz +HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU +s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y +TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx +AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6 +0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z +iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi +nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+ +vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO +e4pIb4tF9g== +-----END CERTIFICATE----- + +Entrust Root Certification Authority - EC1 +========================================== +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx +FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn +YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl +ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw +FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs +LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg +dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt +IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy +AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef +9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h +vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8 +kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +CFCA EV ROOT +============ +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE +CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB +IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw +MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD +DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV +BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD +7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN +uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW +ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7 +xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f +py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K +gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol +hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ +tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf +BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q +ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua +4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG +E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX +BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn +aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy +PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX +kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C +ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GB CA +=============================== +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG +EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl +ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw +MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD +VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds +b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX +scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP +rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk +9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o +Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg +GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI +hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD +dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0 +VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui +HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +SZAFIR ROOT CA2 +=============== +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG +A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV +BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ +BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD +VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q +qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK +DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE +2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ +ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi +ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P +AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC +AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5 +O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67 +oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul +4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6 ++/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +Certum Trusted Network CA 2 +=========================== +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE +BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1 +bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y +ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ +TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB +IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9 +7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o +CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b +Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p +uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130 +GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ +9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB +Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye +hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM +BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI +hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW +Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA +L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo +clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM +pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb +w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo +J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm +ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX +is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7 +zAYspsbiDrW5viSP +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2015 +======================================================= +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT +BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0 +aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx +MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg +QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV +BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw +MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv +bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh +iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+ +6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd +FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr +i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F +GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2 +fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu +iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI +hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+ +D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM +d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y +d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn +82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb +davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F +Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt +J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa +JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q +p/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions ECC RootCA 2015 +=========================================================== +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0 +aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw +MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj +IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD +VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290 +Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP +dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK +Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O +BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA +GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn +dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +ISRG Root X1 +============ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE +BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD +EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG +EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT +DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r +Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1 +3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K +b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN +Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ +4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf +1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu +hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH +usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r +OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G +A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY +9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV +0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt +hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw +TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx +e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA +JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD +YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n +JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ +m+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +AC RAIZ FNMT-RCM +================ +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT +AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw +MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD +TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf +qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr +btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL +j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou +08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw +WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT +tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ +47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC +ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa +i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o +dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s +D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ +j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT +Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW ++YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7 +Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d +8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm +5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG +rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +Amazon Root CA 1 +================ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1 +MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH +FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ +gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t +dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce +VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3 +DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM +CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy +8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa +2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2 +xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +Amazon Root CA 2 +================ +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD +VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1 +MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv +bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4 +kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp +N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9 +AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd +fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx +kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS +btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0 +Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN +c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+ +3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw +DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA +A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE +YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW +xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ +gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW +aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV +Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3 +KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi +JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw= +-----END CERTIFICATE----- + +Amazon Root CA 3 +================ +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB +f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr +Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43 +rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc +eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +Amazon Root CA 4 +================ +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG +EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy +NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ +MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN +/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri +83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA +MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1 +AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT +D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr +IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g +TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp +ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD +VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt +c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth +bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11 +IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8 +6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc +wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0 +3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9 +WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU +ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ +KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc +lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R +e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j +q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +GDCA TrustAUTH R5 ROOT +====================== +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04xMjAw +BgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQD +DBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVow +YjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJjDp6L3TQs +AlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBjTnnEt1u9ol2x8kECK62p +OqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+uKU49tm7srsHwJ5uu4/Ts765/94Y9cnrr +pftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ +9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQ +xXABZG12ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsM +R6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZ +D2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4 +oR24qoAATILnsn8JuLwwoC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx +9hoh49pwBiFYFIeFd3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlR +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9 +H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35 +6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd ++PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQ +HtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBD +F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ +8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQXR4EzzffHqhmsYzmIGrv +/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0LcIymSLumoRT2+1hEmRSuqguT +aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +TrustCor RootCert CA-1 +====================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYDVQQGEwJQQTEP +MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig +U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkx +MjMxMTcyMzE2WjCBpDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFu +YW1hIENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUGA1UECwwe +VHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZUcnVzdENvciBSb290Q2Vy +dCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv463leLCJhJrMxnHQFgKq1mq +jQCj/IDHUHuO1CAmujIS2CNUSSUQIpidRtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4 +pQa81QBeCQryJ3pS/C3Vseq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0 +JEsq1pme9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CVEY4h +gLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorWhnAbJN7+KIor0Gqw +/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/DeOxCbeKyKsZn3MzUOcwHwYDVR0j +BBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwDQYJKoZIhvcNAQELBQADggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5 +mDo4Nvu7Zp5I/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf +ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZyonnMlo2HD6C +qFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djtsL1Ac59v2Z3kf9YKVmgenFK+P +3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdNzl/HHk484IkzlQsPpTLWPFp5LBk= +-----END CERTIFICATE----- + +TrustCor RootCert CA-2 +====================== +-----BEGIN CERTIFICATE----- +MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNVBAYTAlBBMQ8w +DQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQwIgYDVQQKDBtUcnVzdENvciBT +eXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0 +eTEfMB0GA1UEAwwWVHJ1c3RDb3IgUm9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEy +MzExNzI2MzlaMIGkMQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5h +bWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U +cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29yIFJvb3RDZXJ0 +IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnIG7CKqJiJJWQdsg4foDSq8Gb +ZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9Nk +RvRUqdw6VC0xK5mC8tkq1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1 +oYxOdqHp2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nKDOOb +XUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hapeaz6LMvYHL1cEksr1 +/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF3wP+TfSvPd9cW436cOGlfifHhi5q +jxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQP +eSghYA2FFn3XVDjxklb9tTNMg9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+Ctg +rKAmrhQhJ8Z3mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh +8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAdBgNVHQ4EFgQU +2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6UnrybPZx9mCAZ5YwwYrIwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/h +Osh80QA9z+LqBrWyOrsGS2h60COXdKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnp +kpfbsEZC89NiqpX+MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv +2wnL/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RXCI/hOWB3 +S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYaZH9bDTMJBzN7Bj8RpFxw +PIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dv +DDqPys/cA8GiCcjl/YBeyGBCARsaU1q7N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYU +RpFHmygk71dSTlxCnKr3Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANE +xdqtvArBAs8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp5KeX +RKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu1uwJ +-----END CERTIFICATE----- + +TrustCor ECA-1 +============== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYDVQQGEwJQQTEP +MA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEkMCIGA1UECgwbVHJ1c3RDb3Ig +U3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3Jp +dHkxFzAVBgNVBAMMDlRydXN0Q29yIEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3Mjgw +N1owgZwxCzAJBgNVBAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5 +MSQwIgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRydXN0Q29y +IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3IgRUNBLTEwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb3w9U73NjKYKtR8aja+3+XzP4Q1HpGjOR +MRegdMTUpwHmspI+ap3tDvl0mEDTPwOABoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23 +xFUfJ3zSCNV2HykVh0A53ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmc +p0yJF4OuowReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/wZ0+ +fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZFZtS6mFjBAgMBAAGj +YzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAfBgNVHSMEGDAWgBREnkj1zG1I1KBL +f/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF +AAOCAQEABT41XBVwm8nHc2FvcivUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u +/ukZMjgDfxT2AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F +hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50soIipX1TH0Xs +J5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BIWJZpTdwHjFGTot+fDz2LYLSC +jaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1WitJ/X5g== +-----END CERTIFICATE----- + +SSL.com Root Certification Authority RSA +======================================== +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxDjAM +BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24x +MTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYw +MjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx +EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NM +LmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2RxFdHaxh3a3by/ZPkPQ/C +Fp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aXqhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8 +P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/ge +oeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkp +k8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3Z +fBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJ +gUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2 +UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi8 +1xtZPCvM8hnIk2snYxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4s +bE6x/c+cCbqiM+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr +dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E+ZUf +ijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAsl +u1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjq +erQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jra6x+3uxj +MxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90IH37hVZkLId6Tngr75qNJ +vTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJBjSWFupwWRoyeXkLtoh/D1JI +Pb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406y +wKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NI +WuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +SSL.com Root Certification Authority ECC +======================================== +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAv +BgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEy +MTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO +BgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI7Z4INcgn64mMU1jrYor+ +8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPgCemB+vNH06NjMGEwHQYDVR0OBBYEFILR +hXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTT +jgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCW +e+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z +5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +SSL.com EV Root Certification Authority RSA R2 +============================================== +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMQ4w +DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9u +MTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MB4XDTE3MDUzMTE4MTQzN1oXDTQyMDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI +DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYD +VQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvqM0fNTPl9fb69LT3w23jh +hqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssufOePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7w +cXHswxzpY6IXFJ3vG2fThVUCAtZJycxa4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTO +Zw+oz12WGQvE43LrrdF9HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+ +B6KjBSYRaZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcAb9Zh +CBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQGp8hLH94t2S42Oim +9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQVPWKchjgGAGYS5Fl2WlPAApiiECto +RHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMOpgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+Slm +JuwgUHfbSguPvuUCYHBBXtSuUDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48 ++qvWBkofZ6aYMBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa49QaAJadz20Zp +qJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBWs47LCp1Jjr+kxJG7ZhcFUZh1 +++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nx +Y/hoLVUE0fKNsKTPvDxeH3jnpaAgcLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2G +guDKBAdRUNf/ktUM79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDz +OFSz/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXtll9ldDz7 +CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEmKf7GUmG6sXP/wwyc5Wxq +lD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKKQbNmC1r7fSOl8hqw/96bg5Qu0T/fkreR +rwU7ZcegbLHNYhLDkBvjJc40vG93drEQw/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1 +hlMYegouCRw2n5H9gooiS9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX +9hwJ1C07mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +SSL.com EV Root Certification Authority ECC +=========================================== +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAMBgNV +BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xNDAy +BgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYw +MjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx +EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NM +LmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMAVIbc/R/fALhBYlzccBYy +3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1KthkuWnBaBu2+8KGwytAJKaNjMGEwHQYDVR0O +BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe +5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJ +N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm +m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +GlobalSign Root CA - R6 +======================= +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UECxMX +R2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9i +YWxTaWduIFJvb3QgQ0EgLSBSNjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFs +U2lnbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQss +grRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1kZguSgMpE +3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxDaNc9PIrFsmbVkJq3MQbF +vuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqM +PKq0pPbzlUoSB239jLKJz9CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+ +azayOeSsJDa38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05O +WgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/hbguy +CLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4nWUx2OVvq+aWh2IMP +0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kN +b7gu3GduyYsRtYQUigAZcIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQE +AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNV +HSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0 +lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrY +BzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFym +Fe944Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr +3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB1 +0jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/T +uTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItK +oZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+t +JDfLRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GC CA +=============================== +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQGEwJD +SDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEo +MCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRa +Fw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQL +ExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4nieUqjFqdr +VCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4Wp2OQ0jnUsYd4XxiWD1Ab +NTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd +BgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0E +AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk +AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +UCA Global G2 Root +================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9vdDAeFw0x +NjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlU +cnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmT +oni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzmVHqUwCoV +8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/RVogvGjqNO7uCEeBHANBS +h6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDcC/Vkw85DvG1xudLeJ1uK6NjGruFZfc8o +LTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/ +R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBe +KW4bHAyvj5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa +4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwc +OxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd97 +8XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFIHEjMz15DD/pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo +5sOASD0Ee/ojL3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0A +Ds0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9 +yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQsnLAX +c47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHo +jhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZk +bxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI+Vg7RE+x +ygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGyYiGqhkCyLmTTX8jjfhFn +RR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1whnw0A== +-----END CERTIFICATE----- + +UCA Extended Validation Root +============================ +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQG +EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9u +IFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8G +A1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrs +iWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvSsPGP2KxF +Rv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aopO2z6+I9tTcg1367r3CTu +eUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dksHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR +59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH +0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KR +el7sFsLzKuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDv +B0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlth +WG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpS +NwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS +3H5aBZ8eNJr34RQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL +BQADggIBADaNl8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cM +aVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4 +dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb ++7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOW +F3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwi +GpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2CxR9GUeOc +GMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmxcmtpzyKEC2IPrNkZAJSi +djzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUINlK73nZfdklJrX+9ZSCyycEr +dhh2n1ax +-----END CERTIFICATE----- + +Certigna Root CA +================ +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UE +BhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAwMiA0ODE0NjMwODEwMDAzNjEZ +MBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0xMzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjda +MFoxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYz +MDgxMDAwMzYxGTAXBgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sOty3tRQgX +stmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9MCiBtnyN6tMbaLOQdLNyz +KNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPuI9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8 +JXrJhFwLrN1CTivngqIkicuQstDuI7pmTLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16 +XdG+RCYyKfHx9WzMfgIhC59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq +4NYKpkDfePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3YzIoej +wpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWTCo/1VTp2lc5ZmIoJ +lXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1kJWumIWmbat10TWuXekG9qxf5kBdI +jzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp/ +/TBt2dzhauH8XwIDAQABo4IBGjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +HQYDVR0OBBYEFBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczovL3d3d3cuY2Vy +dGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilodHRwOi8vY3JsLmNlcnRpZ25h +LmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYraHR0cDovL2NybC5kaGlteW90aXMuY29tL2Nl +cnRpZ25hcm9vdGNhLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOIt +OoldaDgvUSILSo3L6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxP +TGRGHVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH60BGM+RFq +7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncBlA2c5uk5jR+mUYyZDDl3 +4bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdio2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd +8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS +6Cvu5zHbugRqh5jnxV/vfaci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaY +tlu3zM63Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayhjWZS +aX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw3kAP+HwV96LOPNde +E4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- + +emSign Root CA - G1 +=================== +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJJTjET +MBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl +ZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBHMTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgx +ODMwMDBaMGcxCzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVk +aHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQzf2N4aLTN +LnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO8oG0x5ZOrRkVUkr+PHB1 +cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aqd7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHW +DV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhMtTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ +6DqS0hdW5TUaQBw+jSztOd9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrH +hQIDAQABo0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQDAgEG +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31xPaOfG1vR2vjTnGs2 +vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjMwiI/aTvFthUvozXGaCocV685743Q +NcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6dGNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q ++Mri/Tm3R7nrft8EI6/6nAYH6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeih +U80Bv2noWgbyRQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE----- + +emSign ECC Root CA - G3 +======================= +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQGEwJJTjETMBEG +A1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg +MB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4 +MTgzMDAwWjBrMQswCQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11 +ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0WXTsuwYc +58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xySfvalY8L1X44uT6EYGQIr +MgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuBzhccLikenEhjQjAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+D +CBeQyh+KTOgNG3qxrdWBCUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7 +jHvrZQnD+JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- + +emSign Root CA - C1 +=================== +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCVVMx +EzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNp +Z24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UE +BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQD +ExNlbVNpZ24gUm9vdCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+up +ufGZBczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZHdPIWoU/ +Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH3DspVpNqs8FqOp099cGX +OFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvHGPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4V +I5b2P/AgNBbeCsbEBEV5f6f9vtKppa+cxSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleooms +lMuoaJuvimUnzYnu3Yy1aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+ +XJGFehiqTbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD +ggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87/kOXSTKZEhVb3xEp +/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4kqNPEjE2NuLe/gDEo2APJ62gsIq1 +NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrGYQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9 +wC68AivTxEDkigcxHpvOJpkT+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQ +BmIMMMAVSKeoWXzhriKi4gp6D/piq1JM4fHfyr6DDUI= +-----END CERTIFICATE----- + +emSign ECC Root CA - C3 +======================= +-----BEGIN CERTIFICATE----- +MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQGEwJVUzETMBEG +A1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMxIDAeBgNVBAMTF2VtU2lnbiBF +Q0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UE +BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQD +ExdlbVNpZ24gRUNDIFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd +6bciMK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4OjavtisIGJAnB9 +SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0OBBYEFPtaSNCAIEDyqOkA +B2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gA +MGUCMQC02C8Cif22TGK6Q04ThHK1rt0c3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwU +ZOR8loMRnLDRWmFLpg9J0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== +-----END CERTIFICATE----- + +Hongkong Post Root CA 3 +======================= +-----BEGIN CERTIFICATE----- +MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQELBQAwbzELMAkG +A1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJSG9uZyBLb25nMRYwFAYDVQQK +Ew1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2 +MDMwMjI5NDZaFw00MjA2MDMwMjI5NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtv +bmcxEjAQBgNVBAcTCUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMX +SG9uZ2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz +iNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFOdem1p+/l6TWZ5Mwc50tf +jTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mIVoBc+L0sPOFMV4i707mV78vH9toxdCim +5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOe +sL4jpNrcyCse2m5FHomY2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj +0mRiikKYvLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+TtbNe/ +JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZbx39ri1UbSsUgYT2u +y1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+l2oBlKN8W4UdKjk60FSh0Tlxnf0h ++bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YKTE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsG +xVd7GYYKecsAyVKvQv83j+GjHno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwID +AQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e +i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEwDQYJKoZIhvcN +AQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG7BJ8dNVI0lkUmcDrudHr9Egw +W62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCkMpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWld +y8joRTnU+kLBEUx3XZL7av9YROXrgZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov ++BS5gLNdTaqX4fnkGMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDc +eqFS3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJmOzj/2ZQw +9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+l6mc1X5VTMbeRRAc6uk7 +nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6cJfTzPV4e0hz5sy229zdcxsshTrD3mUcY +hcErulWuBurQB7Lcq9CClnXO0lD+mefPL5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB +60PZ2Pierc+xYw5F9KBaLJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fq +dBb9HxEGmpv0 +-----END CERTIFICATE----- + +Entrust Root Certification Authority - G4 +========================================= +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAwgb4xCzAJBgNV +BAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3Qu +bmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1 +dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eSAtIEc0MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYT +AlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eSAtIEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3D +umSXbcr3DbVZwbPLqGgZ2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV +3imz/f3ET+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j5pds +8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAMC1rlLAHGVK/XqsEQ +e9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73TDtTUXm6Hnmo9RR3RXRv06QqsYJn7 +ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNXwbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5X +xNMhIWNlUpEbsZmOeX7m640A2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV +7rtNOzK+mndmnqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 +dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwlN4y6mACXi0mW +Hv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNjc0kCAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9n +MA0GCSqGSIb3DQEBCwUAA4ICAQAS5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4Q +jbRaZIxowLByQzTSGwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht +7LGrhFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/B7NTeLUK +YvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uIAeV8KEsD+UmDfLJ/fOPt +jqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbwH5Lk6rWS02FREAutp9lfx1/cH6NcjKF+ +m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKW +RGhXxNUzzxkvFMSUHHuk2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjA +JOgc47OlIQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk5F6G ++TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuYn/PIjhs4ViFqUZPT +kcpG2om3PVODLAgfi49T3f+sHw== +-----END CERTIFICATE----- + +Microsoft ECC Root Certificate Authority 2017 +============================================= +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQgRUND +IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4 +MjMxNjA0WjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZRogPZnZH6 +thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYbhGBKia/teQ87zvH2RPUB +eMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIy5lycFIM ++Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlf +Xu5gKcs68tvWMoQZP3zVL8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaR +eNtUjGUBiudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +Microsoft RSA Root Certificate Authority 2017 +============================================= +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQg +UlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIw +NzE4MjMwMDIzWjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u +MTYwNAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZNt9GkMml +7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0ZdDMbRnMlfl7rEqUrQ7e +S0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw7 +1VdyvD/IybLeS2v4I2wDwAW9lcfNcztmgGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+ +dkC0zVJhUXAoP8XFWvLJjEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49F +yGcohJUcaDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaGYaRS +MLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6W6IYZVcSn2i51BVr +lMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4KUGsTuqwPN1q3ErWQgR5WrlcihtnJ +0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJ +ClTUFLkqqNfs+avNJVgyeY+QW5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZCLgLNFgVZJ8og +6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OCgMNPOsduET/m4xaRhPtthH80 +dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk ++ONVFT24bcMKpBLBaYVu32TxU5nhSnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex +/2kskZGT4d9Mozd2TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDy +AmH3pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGRxpl/j8nW +ZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiAppGWSZI1b7rCoucL5mxAyE +7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKT +c0QWbej09+CVgI+WXTik9KveCjCHk9hNAHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D +5KbvtwEwXlGjefVwaaZBRA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +e-Szigno Root CA 2017 +===================== +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNVBAYTAkhVMREw +DwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUt +MjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJvb3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZa +Fw00MjA4MjIxMjA3MDZaMHExCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UE +CgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3pp +Z25vIFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtvxie+RJCx +s1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+HWyx7xf58etqjYzBhMA8G +A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSHERUI0arBeAyxr87GyZDv +vzAEwDAfBgNVHSMEGDAWgBSHERUI0arBeAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEA +tVfd14pVCzbhhkT61NlojbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxO +svxyqltZ+efcMQ== +-----END CERTIFICATE----- + +certSIGN Root CA G2 +=================== +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNVBAYTAlJPMRQw +EgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjAeFw0xNzAy +MDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJBgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lH +TiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAMDFdRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05 +N0IwvlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZuIt4Imfk +abBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhpn+Sc8CnTXPnGFiWeI8Mg +wT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKscpc/I1mbySKEwQdPzH/iV8oScLumZfNp +dWO9lfsbl83kqK/20U6o2YpxJM02PbyWxPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91Qqh +ngLjYl/rNUssuHLoPj1PrCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732 +jcZZroiFDsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fxDTvf +95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgyLcsUDFDYg2WD7rlc +z8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6CeWRgKRM+o/1Pcmqr4tTluCRVLERL +iohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1Ud +DgQWBBSCIS1mxteg4BXrzkwJd8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOB +ywaK8SJJ6ejqkX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQlqiCA2ClV9+BB +/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0OJD7uNGzcgbJceaBxXntC6Z5 +8hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+cNywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5 +BiKDUyUM/FHE5r7iOZULJK2v0ZXkltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklW +atKcsWMy5WHgUyIOpwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tU +Sxfj03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZkPuXaTH4M +NMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE1LlSVHJ7liXMvGnjSG4N +0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MXQRBdJ3NghVdJIgc= +-----END CERTIFICATE----- + +Trustwave Global Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV +UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2 +ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0xNzA4MjMxOTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJV +UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2 +ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALldUShLPDeS0YLOvR29 +zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0XznswuvCAAJWX/NKSqIk4cXGIDtiLK0thAf +LdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4Bq +stTnoApTAbqOl5F2brz81Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9o +WN0EACyW80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotPJqX+ +OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1lRtzuzWniTY+HKE40 +Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfwhI0Vcnyh78zyiGG69Gm7DIwLdVcE +uE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm ++9jaJXLE9gCxInm943xZYkqcBW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqj +ifLJS3tBEW1ntwiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1UdDwEB/wQEAwIB +BjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W0OhUKDtkLSGm+J1WE2pIPU/H +PinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfeuyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0H +ZJDmHvUqoai7PF35owgLEQzxPy0QlG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla +4gt5kNdXElE1GYhBaCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5R +vbbEsLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPTMaCm/zjd +zyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qequ5AvzSxnI9O4fKSTx+O +856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxhVicGaeVyQYHTtgGJoC86cnn+OjC/QezH +Yj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu +3R3y4G5OBVixwJAWKqQ9EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP +29FpHOTKyeC2nOnOcXHebD8WpHk= +-----END CERTIFICATE----- + +Trustwave Global ECC P256 Certification Authority +================================================= +-----BEGIN CERTIFICATE----- +MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYDVQQGEwJVUzER +MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy +dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1 +NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH77bOYj +43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoNFWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqm +P62jQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt +0UrrdaVKEJmzsaGLSvcwCgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjz +RM4q3wghDDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 +-----END CERTIFICATE----- + +Trustwave Global ECC P384 Certification Authority +================================================= +-----BEGIN CERTIFICATE----- +MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYDVQQGEwJVUzER +MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy +dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4 +NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuBBAAiA2IABGvaDXU1CDFH +Ba5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJj9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr +/TklZvFe/oyujUF5nQlgziip04pt89ZF1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNV +HQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNn +ADBkAjA3AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsCMGcl +CrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVuSw== +-----END CERTIFICATE----- + +NAVER Global Root Certification Authority +========================================= +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEMBQAwaTELMAkG +A1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRGT1JNIENvcnAuMTIwMAYDVQQD +DClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4 +NDJaFw0zNzA4MTgyMzU5NTlaMGkxCzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVT +UyBQTEFURk9STSBDb3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVAiQqrDZBb +UGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH38dq6SZeWYp34+hInDEW ++j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lEHoSTGEq0n+USZGnQJoViAbbJAh2+g1G7 +XNr4rRVqmfeSVPc0W+m/6imBEtRTkZazkVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2 +aacp+yPOiNgSnABIqKYPszuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4 +Yb8ObtoqvC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHfnZ3z +VHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaGYQ5fG8Ir4ozVu53B +A0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo0es+nPxdGoMuK8u180SdOqcXYZai +cdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3aCJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejy +YhbLgGvtPe31HzClrkvJE+2KAQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNV +HQ4EFgQU0p+I36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoNqo0hV4/GPnrK +21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatjcu3cvuzHV+YwIHHW1xDBE1UB +jCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm+LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bx +hYTeodoS76TiEJd6eN4MUZeoIUCLhr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTg +E34h5prCy8VCZLQelHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTH +D8z7p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8piKCk5XQ +A76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLRLBT/DShycpWbXgnbiUSY +qqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oG +I/hGoiLtk/bdmuYqh7GYVPEi92tF4+KOdh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmg +kpzNNIaRkPpkUZ3+/uul9XXeifdy +-----END CERTIFICATE----- + +AC RAIZ FNMT-RCM SERVIDORES SEGUROS +=================================== +-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQswCQYDVQQGEwJF +UzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgwFgYDVQRhDA9WQVRFUy1RMjgy +NjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1SQ00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4 +MTIyMDA5MzczM1oXDTQzMTIyMDA5MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQt +UkNNMQ4wDAYDVQQLDAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNB +QyBSQUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LHsbI6GA60XYyzZl2hNPk2 +LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oKUm8BA06Oi6NCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqG +SM49BAMDA2kAMGYCMQCuSuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoD +zBOQn5ICMQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJyv+c= +-----END CERTIFICATE----- + +GlobalSign Root R46 +=================== +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUAMEYxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJv +b3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAX +BgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08Es +CVeJOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQGvGIFAha/ +r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud316HCkD7rRlr+/fKYIje +2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo0q3v84RLHIf8E6M6cqJaESvWJ3En7YEt +bWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSEy132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvj +K8Cd+RTyG/FWaha/LIWFzXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD4 +12lPFzYE+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCNI/on +ccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzsx2sZy/N78CsHpdls +eVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqaByFrgY/bxFn63iLABJzjqls2k+g9 +vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEM +BQADggIBAHx47PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti2kM3S+LGteWy +gxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIkpnnpHs6i58FZFZ8d4kuaPp92 +CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRFFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZm +OUdkLG5NrmJ7v2B0GbhWrJKsFjLtrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qq +JZ4d16GLuc1CLgSkZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwye +qiv5u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP4vkYxboz +nxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6N3ec592kD3ZDZopD8p/7 +DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3vouXsXgxT7PntgMTzlSdriVZzH81Xwj3 +QEUxeCp6 +-----END CERTIFICATE----- + +GlobalSign Root E46 +=================== +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYxCzAJBgNVBAYT +AkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJvb3Qg +RTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNV +BAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkB +jtjqR+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGddyXqBPCCj +QjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQxCpCPtsad0kRL +gLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZk +vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+ +CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- + +GLOBALTRUST 2020 +================ +-----BEGIN CERTIFICATE----- +MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQVQx +IzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVT +VCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYxMDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAh +BgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAy +MDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWi +D59bRatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9ZYybNpyrO +VPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3QWPKzv9pj2gOlTblzLmM +CcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPwyJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCm +fecqQjuCgGOlYx8ZzHyyZqjC0203b+J+BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKA +A1GqtH6qRNdDYfOiaxaJSaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9OR +JitHHmkHr96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj04KlG +DfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9MedKZssCz3AwyIDMvU +clOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIwq7ejMZdnrY8XD2zHc+0klGvIg5rQ +mjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1Ud +IwQYMBaAFNwuH9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA +VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJCXtzoRlgHNQIw +4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd6IwPS3BD0IL/qMy/pJTAvoe9 +iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS +8cE54+X1+NZK3TTN+2/BT+MAi1bikvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2 +HcqtbepBEX4tdJP7wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxS +vTOBTI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6CMUO+1918 +oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn4rnvyOL2NSl6dPrFf4IF +YqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+IaFvowdlxfv1k7/9nR4hYJS8+hge9+6jl +gqispdNpQ80xiEmEU5LAsTkbOYMBMMTyqfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== +-----END CERTIFICATE----- + +ANF Secure Server Root CA +========================= +-----BEGIN CERTIFICATE----- +MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNVBAUTCUc2MzI4 +NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lv +bjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNVBAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3Qg +Q0EwHhcNMTkwOTA0MTAwMDM4WhcNMzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEw +MQswCQYDVQQGEwJFUzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQw +EgYDVQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9vdCBDQTCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCjcqQZAZ2cC4Ffc0m6p6zz +BE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9qyGFOtibBTI3/TO80sh9l2Ll49a2pcbnv +T1gdpd50IJeh7WhM3pIXS7yr/2WanvtH2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcv +B2VSAKduyK9o7PQUlrZXH1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXse +zx76W0OLzc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyRp1RM +VwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQzW7i1o0TJrH93PB0j +7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/SiOL9V8BY9KHcyi1Swr1+KuCLH5z +JTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJnLNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe +8TZBAQIvfXOn3kLMTOmJDVb3n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVO +Hj1tyRRM4y5Bu8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj +o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAOBgNVHQ8BAf8E +BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEATh65isagmD9uw2nAalxJ +UqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzx +j6ptBZNscsdW699QIyjlRRA96Gejrw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDt +dD+4E5UGUcjohybKpFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM +5gf0vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjqOknkJjCb +5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ/zo1PqVUSlJZS2Db7v54 +EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ92zg/LFis6ELhDtjTO0wugumDLmsx2d1H +hk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI+PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGy +g77FGr8H6lnco4g175x2MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3 +r5+qPeoott7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= +-----END CERTIFICATE----- + +Certum EC-384 CA +================ +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQswCQYDVQQGEwJQ +TDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2 +MDcyNDU0WhcNNDMwMzI2MDcyNDU0WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERh +dGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx +GTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATEKI6rGFtq +vm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7TmFy8as10CW4kjPMIRBSqn +iBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68KjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFI0GZnQkdjrzife81r1HfS+8EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNo +ADBlAjADVS2m5hjEfO/JUG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0 +QoSZ/6vnnvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE----- + +Certum Trusted Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6MQswCQYDVQQG +EwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0g +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0Ew +HhcNMTgwMzE2MTIxMDEzWhcNNDMwMzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMY +QXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZn0EGze2jusDbCSzBfN8p +fktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/qp1x4EaTByIVcJdPTsuclzxFUl6s1wB52 +HO8AU5853BSlLCIls3Jy/I2z5T4IHhQqNwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2 +fJmItdUDmj0VDT06qKhF8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGt +g/BKEiJ3HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGamqi4 +NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi7VdNIuJGmj8PkTQk +fVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSFytKAQd8FqKPVhJBPC/PgP5sZ0jeJ +P/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0PqafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSY +njYJdmZm/Bo/6khUHL4wvYBQv3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHK +HRzQ+8S1h9E6Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 +vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQADggIBAEii1QAL +LtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4WxmB82M+w85bj/UvXgF2Ez8s +ALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvozMrnadyHncI013nR03e4qllY/p0m+jiGPp2K +h2RX5Rc64vmNueMzeMGQ2Ljdt4NR5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8 +CYyqOhNf6DR5UMEQGfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA +4kZf5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq0Uc9Nneo +WWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7DP78v3DSk+yshzWePS/Tj +6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTMqJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmT +OPQD8rv7gmsHINFSH5pkAnuYZttcTVoP0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZck +bxJF0WddCajJFdr60qZfE2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb +-----END CERTIFICATE----- + +TunTrust Root CA +================ +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQELBQAwYTELMAkG +A1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUgQ2VydGlmaWNhdGlvbiBFbGVj +dHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJvb3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQw +NDI2MDg1NzU2WjBhMQswCQYDVQQGEwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBD +ZXJ0aWZpY2F0aW9uIEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZn56eY+hz +2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd2JQDoOw05TDENX37Jk0b +bjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgFVwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7 +NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZGoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAd +gjH8KcwAWJeRTIAAHDOFli/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViW +VSHbhlnUr8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2eY8f +Tpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIbMlEsPvLfe/ZdeikZ +juXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISgjwBUFfyRbVinljvrS5YnzWuioYas +DXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwS +VXAkPcvCFDVDXSdOvsC9qnyW5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI +04Y+oXNZtPdEITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 +90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+zxiD2BkewhpMl +0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYuQEkHDVneixCwSQXi/5E/S7fd +Ao74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRY +YdZ2vyJ/0Adqp2RT8JeNnYA/u8EH22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJp +adbGNjHh/PqAulxPxOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65x +xBzndFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5Xc0yGYuP +jCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7bnV2UqL1g52KAdoGDDIzM +MEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQCvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9z +ZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZHu/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3r +AZ3r2OvEhJn7wAzMMujjd9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= +-----END CERTIFICATE----- + +HARICA TLS RSA Root CA 2021 +=========================== +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG +EwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u +cyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0EgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUz +OFoXDTQ1MDIxMzEwNTUzN1owbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRl +bWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNB +IFJvb3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569lmwVnlskN +JLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE4VGC/6zStGndLuwRo0Xu +a2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uva9of08WRiFukiZLRgeaMOVig1mlDqa2Y +Ulhu2wr7a89o+uOkXjpFc5gH6l8Cct4MpbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K +5FrZx40d/JiZ+yykgmvwKh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEv +dmn8kN3bLW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcYAuUR +0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqBAGMUuTNe3QvboEUH +GjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYqE613TBoYm5EPWNgGVMWX+Ko/IIqm +haZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHrW2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQ +CPxrvrNQKlr9qEgYRtaQQJKQCoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAUX15QvWiWkKQU +EapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3f5Z2EMVGpdAgS1D0NTsY9FVq +QRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxajaH6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxD +QpSbIPDRzbLrLFPCU3hKTwSUQZqPJzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcR +j88YxeMn/ibvBZ3PzzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5 +vZStjBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0/L5H9MG0 +qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pTBGIBnfHAT+7hOtSLIBD6 +Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79aPib8qXPMThcFarmlwDB31qlpzmq6YR/ +PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YWxw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnn +kf3/W9b3raYvAwtt41dU63ZTGI0RmLo= +-----END CERTIFICATE----- + +HARICA TLS ECC Root CA 2021 +=========================== +-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQswCQYDVQQGEwJH +UjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBD +QTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoX +DTQ1MDIxMzExMDEwOVowbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWlj +IGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJv +b3QgQ0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7KKrxcm1l +AEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9YSTHMmE5gEYd103KUkE+b +ECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW +0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAi +rcJRQO9gcS3ujwLEXQNwSaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/Qw +CZ61IygNnxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1Ud +DgQWBBRlzeurNR4APn7VdMActHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4w +gZswgZgGBFUdIAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j +b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABCAG8AbgBhAG4A +bwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAwADEANzAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9miWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL +4QjbEwj4KKE1soCzC1HA01aajTNFSa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDb +LIpgD7dvlAceHabJhfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1il +I45PVf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZEEAEeiGaP +cjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV1aUsIC+nmCjuRfzxuIgA +LI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2tCsvMo2ebKHTEm9caPARYpoKdrcd7b/+A +lun4jWq9GJAd/0kakFI3ky88Al2CdgtR5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH +9IBk9W6VULgRfhVwOEqwf9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpf +NIbnYrX9ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNKGbqE +ZycPvEJdvSRUDewdcAZfpLz6IHxV +-----END CERTIFICATE----- + +vTrus ECC Root CA +================= +-----BEGIN CERTIFICATE----- +MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMwRzELMAkGA1UE +BhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBS +b290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDczMTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAa +BgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+c +ToL0v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUde4BdS49n +TPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIwV53dVvHH4+m4SVBrm2nDb+zDfSXkV5UT +QJtS0zvzQBm8JsctBp61ezaf9SXUY2sAAjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQL +YgmRWAD5Tfs0aNoJrSEGGJTO +-----END CERTIFICATE----- + +vTrus Root CA +============= +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQELBQAwQzELMAkG +A1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xFjAUBgNVBAMTDXZUcnVzIFJv +b3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMxMDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoG +A1UEChMTaVRydXNDaGluYSBDby4sTHRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZots +SKYcIrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykUAyyNJJrI +ZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+GrPSbcKvdmaVayqwlHeF +XgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z98Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KA +YPxMvDVTAWqXcoKv8R1w6Jz1717CbMdHflqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70 +kLJrxLT5ZOrpGgrIDajtJ8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2 +AXPKBlim0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZNpGvu +/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQUqqzApVg+QxMaPnu +1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHWOXSuTEGC2/KmSNGzm/MzqvOmwMVO +9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMBAAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYg +scasGrz2iTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOC +AgEAKbqSSaet8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd +nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1jbhd47F18iMjr +jld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvMKar5CKXiNxTKsbhm7xqC5PD4 +8acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIivTDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJn +xDHO2zTlJQNgJXtxmOTAGytfdELSS8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554Wg +icEFOwE30z9J4nfrI8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4 +sEb9b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNBUvupLnKW +nyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1PTi07NEPhmg4NpGaXutIc +SkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929vensBxXVsFy6K2ir40zSbofitzmdHxghm+H +l3s= +-----END CERTIFICATE----- + +ISRG Root X2 +============ +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJV +UzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElT +UkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVT +MSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNS +RyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0H +ttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppb +d9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtF +cP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5 +U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- + +HiPKI Root CA - G1 +================== +-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xGzAZBgNVBAMMEkhpUEtJ +IFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRaFw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYT +AlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kg +Um9vdCBDQSAtIEcxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0 +o9QwqNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twvVcg3Px+k +wJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6lZgRZq2XNdZ1AYDgr/SE +YYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnzQs7ZngyzsHeXZJzA9KMuH5UHsBffMNsA +GJZMoYFL3QRtU6M9/Aes1MU3guvklQgZKILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfd +hSi8MEyr48KxRURHH+CKFgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj +1jOXTyFjHluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDry+K4 +9a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ/W3c1pzAtH2lsN0/ +Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgMa/aOEmem8rJY5AIJEzypuxC00jBF +8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQD +AgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi +7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqcSE5XCV0vrPSl +tJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6FzaZsT0pPBWGTMpWmWSBUdGSquE +wx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9TcXzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07Q +JNBAsNB1CI69aO4I1258EHBGG3zgiLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv +5wiZqAxeJoBF1PhoL5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+Gpz +jLrFNe85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wrkkVbbiVg +hUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+vhV4nYWBSipX3tUZQ9rb +yltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQUYDksswBVLuT1sw5XxJFBAJw/6KXf6vb/ +yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== +-----END CERTIFICATE----- + +GlobalSign ECC Root CA - R4 +=========================== +-----BEGIN CERTIFICATE----- +MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYDVQQLExtHbG9i +YWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgwMTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9i +YWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds +b2JhbFNpZ24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkW +ymOxuYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNVHQ8BAf8E +BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/+wpu+74zyTyjhNUwCgYI +KoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147bmF0774BxL4YSFlhgjICICadVGNA3jdg +UM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm +-----END CERTIFICATE----- + +GTS Root R1 +=========== +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM +f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7raKb0 +xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnWr4+w +B7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXW +nOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk +9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zq +kUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92wO1A +K/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om3xPX +V2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDW +cfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQAD +ggIBAJ+qQibbC5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuyh6f88/qBVRRi +ClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM47HLwEXWdyzRSjeZ2axfG34ar +J45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8JZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYci +NuaCp+0KueIHoI17eko8cdLiA6EfMgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5me +LMFrUKTX5hgUvYU/Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJF +fbdT6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ0E6yove+ +7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm2tIMPNuzjsmhDYAPexZ3 +FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bbbP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3 +gm3c +-----END CERTIFICATE----- + +GTS Root R2 +=========== +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV +UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg +UjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE +ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv +CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo7JUl +e3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWIm8Wb +a96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS ++LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7M +kogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJG +r61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RWIr9q +S34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73VululycslaVNV +J1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy5okL +dWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQAD +ggIBAB/Kzt3HvqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 +0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyCB19m3H0Q/gxh +swWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2uNmSRXbBoGOqKYcl3qJfEycel +/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMgyALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVn +jWQye+mew4K6Ki3pHrTgSAai/GevHyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y5 +9PYjJbigapordwj6xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M +7YNRTOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924SgJPFI/2R8 +0L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV7LXTWtiBmelDGDfrs7vR +WGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjW +HYbL +-----END CERTIFICATE----- + +GTS Root R3 +=========== +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJVUzEi +MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMw +HhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZ +R29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjO +PQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout +736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24CejQjBA +MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP0/Eq +Er24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azT +L818+FsuVbu/3ZL3pAzcMeGiAjEA/JdmZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV +11RZt+cRLInUue4X +-----END CERTIFICATE----- + +GTS Root R4 +=========== +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJVUzEi +MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQw +HhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZ +R29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjO +PQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu +hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqjQjBA +MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV2Py1 +PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/C +r8deVl5c1RxYIigL9zC2L7F8AjEA8GE8p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh +4rsUecrNIdSUtUlD +-----END CERTIFICATE----- diff --git a/3rd_party/config.guess b/3rd_party/config.guess new file mode 100644 index 00000000..5772f193 --- /dev/null +++ b/3rd_party/config.guess @@ -0,0 +1,1667 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2020 Free Software Foundation, Inc. + +timestamp='2020-01-01' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +# +# Please send patches to . + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2020 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c89 c99 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD="$driver" + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if test -f /.attbin/uname ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "$UNAME_SYSTEM" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + set_cc_for_build + cat <<-EOF > "$dummy.c" + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`" + + # If ldd exists, use it to detect musl libc. + if command -v ldd >/dev/null && \ + ldd --version 2>&1 | grep -q ^musl + then + LIBC=musl + fi + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + "/sbin/$sysctl" 2>/dev/null || \ + "/usr/sbin/$sysctl" 2>/dev/null || \ + echo unknown)` + case "$UNAME_MACHINE_ARCH" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + machine="${arch}${endian}"-unknown + ;; + *) machine="$UNAME_MACHINE_ARCH"-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently (or will in the future) and ABI. + case "$UNAME_MACHINE_ARCH" in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case "$UNAME_MACHINE_ARCH" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "$UNAME_VERSION" in + Debian*) + release='-gnu' + ;; + *) + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "$machine-${os}${release}${abi-}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" + exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" + exit ;; + *:MidnightBSD:*:*) + echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" + exit ;; + *:ekkoBSD:*:*) + echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" + exit ;; + *:SolidBSD:*:*) + echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" + exit ;; + *:OS108:*:*) + echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE" + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd"$UNAME_RELEASE" + exit ;; + *:MirBSD:*:*) + echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" + exit ;; + *:Sortix:*:*) + echo "$UNAME_MACHINE"-unknown-sortix + exit ;; + *:Twizzler:*:*) + echo "$UNAME_MACHINE"-unknown-twizzler + exit ;; + *:Redox:*:*) + echo "$UNAME_MACHINE"-unknown-redox + exit ;; + mips:OSF1:*.*) + echo mips-dec-osf1 + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE=alpha ;; + "EV4.5 (21064)") + UNAME_MACHINE=alpha ;; + "LCA4 (21066/21068)") + UNAME_MACHINE=alpha ;; + "EV5 (21164)") + UNAME_MACHINE=alphaev5 ;; + "EV5.6 (21164A)") + UNAME_MACHINE=alphaev56 ;; + "EV5.6 (21164PC)") + UNAME_MACHINE=alphapca56 ;; + "EV5.7 (21164PC)") + UNAME_MACHINE=alphapca57 ;; + "EV6 (21264)") + UNAME_MACHINE=alphaev6 ;; + "EV6.7 (21264A)") + UNAME_MACHINE=alphaev67 ;; + "EV6.8CB (21264C)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8AL (21264B)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8CX (21264D)") + UNAME_MACHINE=alphaev68 ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE=alphaev69 ;; + "EV7 (21364)") + UNAME_MACHINE=alphaev7 ;; + "EV7.9 (21364A)") + UNAME_MACHINE=alphaev79 ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`" + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo "$UNAME_MACHINE"-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo "$UNAME_MACHINE"-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix"$UNAME_RELEASE" + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux"$UNAME_RELEASE" + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + set_cc_for_build + SUN_ARCH=i386 + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH=x86_64 + fi + fi + echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`" + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos"$UNAME_RELEASE" + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos"$UNAME_RELEASE" + ;; + sun4) + echo sparc-sun-sunos"$UNAME_RELEASE" + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos"$UNAME_RELEASE" + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint"$UNAME_RELEASE" + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint"$UNAME_RELEASE" + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint"$UNAME_RELEASE" + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint"$UNAME_RELEASE" + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint"$UNAME_RELEASE" + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint"$UNAME_RELEASE" + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten"$UNAME_RELEASE" + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten"$UNAME_RELEASE" + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix"$UNAME_RELEASE" + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix"$UNAME_RELEASE" + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix"$UNAME_RELEASE" + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos"$UNAME_RELEASE" + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ] + then + if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \ + [ "$TARGET_BINARY_INTERFACE"x = x ] + then + echo m88k-dg-dgux"$UNAME_RELEASE" + else + echo m88k-dg-dguxbcs"$UNAME_RELEASE" + fi + else + echo i586-dg-dgux"$UNAME_RELEASE" + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`" + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" + fi + echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/lslpp ] ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" + fi + echo "$IBM_ARCH"-ibm-aix"$IBM_REV" + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + case "$UNAME_MACHINE" in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "$sc_cpu_version" in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "$sc_kernel_bits" in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "$HP_ARCH" = "" ]; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ "$HP_ARCH" = hppa2.0w ] + then + set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH=hppa2.0w + else + HP_ARCH=hppa64 + fi + fi + echo "$HP_ARCH"-hp-hpux"$HPUX_REV" + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux"$HPUX_REV" + exit ;; + 3050*:HI-UX:*:*) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo "$UNAME_MACHINE"-unknown-osf1mk + else + echo "$UNAME_MACHINE"-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi"$UNAME_RELEASE" + exit ;; + *:BSD/OS:*:*) + echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" + exit ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi + else + echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf + fi + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case "$UNAME_PROCESSOR" in + amd64) + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; + esac + echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" + exit ;; + i*:CYGWIN*:*) + echo "$UNAME_MACHINE"-pc-cygwin + exit ;; + *:MINGW64*:*) + echo "$UNAME_MACHINE"-pc-mingw64 + exit ;; + *:MINGW*:*) + echo "$UNAME_MACHINE"-pc-mingw32 + exit ;; + *:MSYS*:*) + echo "$UNAME_MACHINE"-pc-msys + exit ;; + i*:PW*:*) + echo "$UNAME_MACHINE"-pc-pw32 + exit ;; + *:Interix*:*) + case "$UNAME_MACHINE" in + x86) + echo i586-pc-interix"$UNAME_RELEASE" + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix"$UNAME_RELEASE" + exit ;; + IA64) + echo ia64-unknown-interix"$UNAME_RELEASE" + exit ;; + esac ;; + i*:UWIN*:*) + echo "$UNAME_MACHINE"-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-pc-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + exit ;; + *:GNU:*:*) + # the GNU system + echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`" + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" + exit ;; + *:Minix:*:*) + echo "$UNAME_MACHINE"-unknown-minix + exit ;; + aarch64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + arm*:Linux:*:*) + set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi + else + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + cris:Linux:*:*) + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" + exit ;; + crisv32:Linux:*:*) + echo "$UNAME_MACHINE"-axis-linux-"$LIBC" + exit ;; + e2k:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + frv:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + hexagon:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + i*86:Linux:*:*) + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" + exit ;; + ia64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + k1om:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + m32r*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + m68*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 + sed 's/^ //' << EOF > "$dummy.c" + #undef CPU + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + MIPS_ENDIAN=el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + MIPS_ENDIAN= + #else + MIPS_ENDIAN= + #endif + #endif +EOF + eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } + ;; + mips64el:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-"$LIBC" + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-"$LIBC" + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-"$LIBC" + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; + PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; + *) echo hppa-unknown-linux-"$LIBC" ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-"$LIBC" + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-"$LIBC" + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-"$LIBC" + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-"$LIBC" + exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" + exit ;; + sh64*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + sh*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + tile*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + vax:Linux:*:*) + echo "$UNAME_MACHINE"-dec-linux-"$LIBC" + exit ;; + x86_64:Linux:*:*) + echo "$UNAME_MACHINE"-pc-linux-"$LIBC" + exit ;; + xtensa*:Linux:*:*) + echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo "$UNAME_MACHINE"-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo "$UNAME_MACHINE"-unknown-stop + exit ;; + i*86:atheos:*:*) + echo "$UNAME_MACHINE"-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo "$UNAME_MACHINE"-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos"$UNAME_RELEASE" + exit ;; + i*86:*DOS:*:*) + echo "$UNAME_MACHINE"-pc-msdosdjgpp + exit ;; + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" + else + echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}" + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" + else + echo "$UNAME_MACHINE"-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configure will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos"$UNAME_RELEASE" + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos"$UNAME_RELEASE" + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos"$UNAME_RELEASE" + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos"$UNAME_RELEASE" + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv"$UNAME_RELEASE" + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo "$UNAME_MACHINE"-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo "$UNAME_MACHINE"-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux"$UNAME_RELEASE" + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv"$UNAME_RELEASE" + else + echo mips-unknown-sysv"$UNAME_RELEASE" + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux"$UNAME_RELEASE" + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux"$UNAME_RELEASE" + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux"$UNAME_RELEASE" + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux"$UNAME_RELEASE" + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux"$UNAME_RELEASE" + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux"$UNAME_RELEASE" + exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux"$UNAME_RELEASE" + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody"$UNAME_RELEASE" + exit ;; + *:Rhapsody:*:*) + echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build + fi + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE + fi + echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = x86; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-*:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSR-*:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSV-*:NONSTOP_KERNEL:*:*) + echo nsv-tandem-nsk"$UNAME_RELEASE" + exit ;; + NSX-*:NONSTOP_KERNEL:*:*) + echo nsx-tandem-nsk"$UNAME_RELEASE" + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + # shellcheck disable=SC2154 + if test "$cputype" = 386; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo "$UNAME_MACHINE"-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux"$UNAME_RELEASE" + exit ;; + *:DragonFly:*:*) + echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`" + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "$UNAME_MACHINE" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`" + exit ;; + i*86:rdos:*:*) + echo "$UNAME_MACHINE"-pc-rdos + exit ;; + i*86:AROS:*:*) + echo "$UNAME_MACHINE"-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo "$UNAME_MACHINE"-unknown-esx + exit ;; + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs + exit ;; + *:Unleashed:*:*) + echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE" + exit ;; +esac + +# No uname command or uname output not recognized. +set_cc_for_build +cat > "$dummy.c" < +#include +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); +#endif + +#if defined (vax) +#if !defined (ultrix) +#include +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } + +echo "$0: unable to guess system type" >&2 + +case "$UNAME_MACHINE:$UNAME_SYSTEM" in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 </dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: \ No newline at end of file diff --git a/3rd_party/config.sub b/3rd_party/config.sub new file mode 100644 index 00000000..a27949a6 --- /dev/null +++ b/3rd_party/config.sub @@ -0,0 +1,1793 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2020 Free Software Foundation, Inc. + +timestamp='2020-01-01' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2020 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo "$1" + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Split fields of configuration type +# shellcheck disable=SC2162 +IFS="-" read field1 field2 field3 field4 <&2 + exit 1 + ;; + *-*-*-*) + basic_machine=$field1-$field2 + os=$field3-$field4 + ;; + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \ + | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \ + | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ + | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ + | storm-chaos* | os2-emx* | rtmk-nova*) + basic_machine=$field1 + os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + os=linux-android + ;; + *) + basic_machine=$field1-$field2 + os=$field3 + ;; + esac + ;; + *-*) + # A lone config we happen to match not fitting any pattern + case $field1-$field2 in + decstation-3100) + basic_machine=mips-dec + os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* \ + | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ + | ultra | tti* | harris | dolphin | highlevel | gould \ + | cbm | ns | masscomp | apple | axis | knuth | cray \ + | microblaze* | sim | cisco \ + | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + os= + ;; + *) + basic_machine=$field1 + os=$field2 + ;; + esac + ;; + esac + ;; + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + os=bsd + ;; + a29khif) + basic_machine=a29k-amd + os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=scout + ;; + alliant) + basic_machine=fx80-alliant + os= + ;; + altos | altos3068) + basic_machine=m68k-altos + os= + ;; + am29k) + basic_machine=a29k-none + os=bsd + ;; + amdahl) + basic_machine=580-amdahl + os=sysv + ;; + amiga) + basic_machine=m68k-unknown + os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=bsd + ;; + aros) + basic_machine=i386-pc + os=aros + ;; + aux) + basic_machine=m68k-apple + os=aux + ;; + balance) + basic_machine=ns32k-sequent + os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=linux + ;; + cegcc) + basic_machine=arm-unknown + os=cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=bsd + ;; + convex-c2) + basic_machine=c2-convex + os=bsd + ;; + convex-c32) + basic_machine=c32-convex + os=bsd + ;; + convex-c34) + basic_machine=c34-convex + os=bsd + ;; + convex-c38) + basic_machine=c38-convex + os=bsd + ;; + cray) + basic_machine=j90-cray + os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + os= + ;; + da30) + basic_machine=m68k-da30 + os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + os= + ;; + delta88) + basic_machine=m88k-motorola + os=sysv3 + ;; + dicos) + basic_machine=i686-pc + os=dicos + ;; + djgpp) + basic_machine=i586-pc + os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=ose + ;; + gmicro) + basic_machine=tron-gmicro + os=sysv + ;; + go32) + basic_machine=i386-pc + os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=hms + ;; + harris) + basic_machine=m88k-harris + os=sysv3 + ;; + hp300 | hp300hpux) + basic_machine=m68k-hp + os=hpux + ;; + hp300bsd) + basic_machine=m68k-hp + os=bsd + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=proelf + ;; + i386mach) + basic_machine=i386-mach + os=mach + ;; + isi68 | isi) + basic_machine=m68k-isi + os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + os=sysv + ;; + merlin) + basic_machine=ns32k-utek + os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + os=coff + ;; + morphos) + basic_machine=powerpc-unknown + os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=moxiebox + ;; + msdos) + basic_machine=i386-pc + os=msdos + ;; + msys) + basic_machine=i686-pc + os=msys + ;; + mvs) + basic_machine=i370-ibm + os=mvs + ;; + nacl) + basic_machine=le32-unknown + os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=newsos + ;; + news1000) + basic_machine=m68030-sony + os=newsos + ;; + necv70) + basic_machine=v70-nec + os=sysv + ;; + nh3000) + basic_machine=m68k-harris + os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=cxux + ;; + nindy960) + basic_machine=i960-intel + os=nindy + ;; + mon960) + basic_machine=i960-intel + os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=ose + ;; + os68k) + basic_machine=m68k-none + os=os68k + ;; + paragon) + basic_machine=i860-intel + os=osf + ;; + parisc) + basic_machine=hppa-unknown + os=linux + ;; + pw32) + basic_machine=i586-unknown + os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=rdos + ;; + rdos32) + basic_machine=i386-pc + os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=coff + ;; + sa29200) + basic_machine=a29k-amd + os=udi + ;; + sei) + basic_machine=mips-sei + os=seiux + ;; + sequent) + basic_machine=i386-sequent + os= + ;; + sps7) + basic_machine=m68k-bull + os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + os= + ;; + stratus) + basic_machine=i860-stratus + os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + os= + ;; + sun2os3) + basic_machine=m68000-sun + os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + os= + ;; + sun3os3) + basic_machine=m68k-sun + os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + os= + ;; + sun4os3) + basic_machine=sparc-sun + os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + os= + ;; + sv1) + basic_machine=sv1-cray + os=unicos + ;; + symmetry) + basic_machine=i386-sequent + os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=unicos + ;; + t90) + basic_machine=t90-cray + os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + os=tpf + ;; + udi29k) + basic_machine=a29k-amd + os=udi + ;; + ultra3) + basic_machine=a29k-nyu + os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=none + ;; + vaxv) + basic_machine=vax-dec + os=sysv + ;; + vms) + basic_machine=vax-dec + os=vms + ;; + vsta) + basic_machine=i386-pc + os=vsta + ;; + vxworks960) + basic_machine=i960-wrs + os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=vxworks + ;; + xbox) + basic_machine=i686-pc + os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + os=unicos + ;; + *) + basic_machine=$1 + os= + ;; + esac + ;; +esac + +# Decode 1-component or ad-hoc basic machines +case $basic_machine in + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond + ;; + op50n) + cpu=hppa1.1 + vendor=oki + ;; + op60c) + cpu=hppa1.1 + vendor=oki + ;; + ibm*) + cpu=i370 + vendor=ibm + ;; + orion105) + cpu=clipper + vendor=highlevel + ;; + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple + ;; + pmac | pmac-mpw) + cpu=powerpc + vendor=apple + ;; + + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + cpu=m68000 + vendor=att + ;; + 3b*) + cpu=we32k + vendor=att + ;; + bluegene*) + cpu=powerpc + vendor=ibm + os=cnk + ;; + decsystem10* | dec10*) + cpu=pdp10 + vendor=dec + os=tops10 + ;; + decsystem20* | dec20*) + cpu=pdp10 + vendor=dec + os=tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + cpu=m68k + vendor=motorola + ;; + dpx2*) + cpu=m68k + vendor=bull + os=sysv3 + ;; + encore | umax | mmax) + cpu=ns32k + vendor=encore + ;; + elxsi) + cpu=elxsi + vendor=elxsi + os=${os:-bsd} + ;; + fx2800) + cpu=i860 + vendor=alliant + ;; + genix) + cpu=ns32k + vendor=ns + ;; + h3050r* | hiux*) + cpu=hppa1.1 + vendor=hitachi + os=hiuxwe2 + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + cpu=m68000 + vendor=hp + ;; + hp9k3[2-9][0-9]) + cpu=m68k + vendor=hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + i*86v32) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=sysv32 + ;; + i*86v4*) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=sysv4 + ;; + i*86v) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=sysv + ;; + i*86sol2) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=solaris2 + ;; + j90 | j90-cray) + cpu=j90 + vendor=cray + os=${os:-unicos} + ;; + iris | iris4d) + cpu=mips + vendor=sgi + case $os in + irix*) + ;; + *) + os=irix4 + ;; + esac + ;; + miniframe) + cpu=m68000 + vendor=convergent + ;; + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + os=mint + ;; + news-3600 | risc-news) + cpu=mips + vendor=sony + os=newsos + ;; + next | m*-next) + cpu=m68k + vendor=next + case $os in + openstep*) + ;; + nextstep*) + ;; + ns2*) + os=nextstep2 + ;; + *) + os=nextstep3 + ;; + esac + ;; + np1) + cpu=np1 + vendor=gould + ;; + op50n-* | op60c-*) + cpu=hppa1.1 + vendor=oki + os=proelf + ;; + pa-hitachi) + cpu=hppa1.1 + vendor=hitachi + os=hiuxwe2 + ;; + pbd) + cpu=sparc + vendor=tti + ;; + pbb) + cpu=m68k + vendor=tti + ;; + pc532) + cpu=ns32k + vendor=pc532 + ;; + pn) + cpu=pn + vendor=gould + ;; + power) + cpu=power + vendor=ibm + ;; + ps2) + cpu=i386 + vendor=ibm + ;; + rm[46]00) + cpu=mips + vendor=siemens + ;; + rtpc | rtpc-*) + cpu=romp + vendor=ibm + ;; + sde) + cpu=mipsisa32 + vendor=sde + os=${os:-elf} + ;; + simso-wrs) + cpu=sparclite + vendor=wrs + os=vxworks + ;; + tower | tower-32) + cpu=m68k + vendor=ncr + ;; + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu + ;; + w65) + cpu=w65 + vendor=wdc + ;; + w89k-*) + cpu=hppa1.1 + vendor=winbond + os=proelf + ;; + none) + cpu=none + vendor=none + ;; + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine + ;; + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` + ;; + + *-*) + # shellcheck disable=SC2162 + IFS="-" read cpu vendor <&2 + exit 1 + ;; + esac + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $vendor in + digital*) + vendor=dec + ;; + commodore*) + vendor=cbm + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x$os != x ] +then +case $os in + # First match some system type aliases that might get confused + # with valid system types. + # solaris* is a basic system type, with this one exception. + auroraux) + os=auroraux + ;; + bluegene*) + os=cnk + ;; + solaris1 | solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + solaris) + os=solaris2 + ;; + unixware*) + os=sysv4.2uw + ;; + gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # es1800 is here to avoid being matched by es* (a different OS) + es1800*) + os=ose + ;; + # Some version numbers need modification + chorusos*) + os=chorusos + ;; + isc) + os=isc2.2 + ;; + sco6) + os=sco5v6 + ;; + sco5) + os=sco3.2v5 + ;; + sco4) + os=sco3.2v4 + ;; + sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + ;; + sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + scout) + # Don't match below + ;; + sco*) + os=sco3.2v2 + ;; + psos*) + os=psos + ;; + # Now accept the basic system types. + # The portable systems comes first. + # Each alternative MUST end in a * to match a version number. + # sysv* is not here because it comes later, after sysvr4. + gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ + | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | kopensolaris* | plan9* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* | twizzler* \ + | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ + | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ + | knetbsd* | mirbsd* | netbsd* \ + | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \ + | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ + | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \ + | chorusrdb* | cegcc* | glidix* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ + | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \ + | linux-newlib* | linux-musl* | linux-uclibc* \ + | uxpv* | beos* | mpeix* | udk* | moxiebox* \ + | interix* | uwin* | mks* | rhapsody* | darwin* \ + | openstep* | oskit* | conix* | pw32* | nonstopux* \ + | storm-chaos* | tops10* | tenex* | tops20* | its* \ + | os2* | vos* | palmos* | uclinux* | nucleus* \ + | morphos* | superux* | rtmk* | windiss* \ + | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ + | skyos* | haiku* | rdos* | toppers* | drops* | es* \ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ + | nsk* | powerunix) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + qnx*) + case $cpu in + x86 | i*86) + ;; + *) + os=nto-$os + ;; + esac + ;; + hiux*) + os=hiuxwe2 + ;; + nto-qnx*) + ;; + nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + sim | xray | os68k* | v88r* \ + | windows* | osx | abug | netware* | os9* \ + | macos* | mpw* | magic* | mmixware* | mon960* | lnews*) + ;; + linux-dietlibc) + os=linux-dietlibc + ;; + linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + lynx*178) + os=lynxos178 + ;; + lynx*5) + os=lynxos5 + ;; + lynx*) + os=lynxos + ;; + mac*) + os=`echo "$os" | sed -e 's|mac|macos|'` + ;; + opened*) + os=openedition + ;; + os400*) + os=os400 + ;; + sunos5*) + os=`echo "$os" | sed -e 's|sunos5|solaris2|'` + ;; + sunos6*) + os=`echo "$os" | sed -e 's|sunos6|solaris3|'` + ;; + wince*) + os=wince + ;; + utek*) + os=bsd + ;; + dynix*) + os=bsd + ;; + acis*) + os=aos + ;; + atheos*) + os=atheos + ;; + syllable*) + os=syllable + ;; + 386bsd) + os=bsd + ;; + ctix* | uts*) + os=sysv + ;; + nova*) + os=rtmk-nova + ;; + ns2) + os=nextstep2 + ;; + # Preserve the version number of sinix5. + sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + sinix*) + os=sysv4 + ;; + tpf*) + os=tpf + ;; + triton*) + os=sysv3 + ;; + oss*) + os=sysv3 + ;; + svr4*) + os=sysv4 + ;; + svr3) + os=sysv3 + ;; + sysvr4) + os=sysv4 + ;; + # This must come after sysvr4. + sysv*) + ;; + ose*) + os=ose + ;; + *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) + os=mint + ;; + zvmoe) + os=zvmoe + ;; + dicos*) + os=dicos + ;; + pikeos*) + # Until real need of OS specific support for + # particular features comes up, bare metal + # configurations are quite functional. + case $cpu in + arm*) + os=eabi + ;; + *) + os=elf + ;; + esac + ;; + nacl*) + ;; + ios) + ;; + none) + ;; + *-eabi) + ;; + *) + echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $cpu-$vendor in + score-*) + os=elf + ;; + spu-*) + os=elf + ;; + *-acorn) + os=riscix1.2 + ;; + arm*-rebel) + os=linux + ;; + arm*-semi) + os=aout + ;; + c4x-* | tic4x-*) + os=coff + ;; + c8051-*) + os=elf + ;; + clipper-intergraph) + os=clix + ;; + hexagon-*) + os=elf + ;; + tic54x-*) + os=coff + ;; + tic55x-*) + os=coff + ;; + tic6x-*) + os=coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=tops20 + ;; + pdp11-*) + os=none + ;; + *-dec | vax-*) + os=ultrix4.2 + ;; + m68*-apollo) + os=domain + ;; + i386-sun) + os=sunos4.0.2 + ;; + m68000-sun) + os=sunos3 + ;; + m68*-cisco) + os=aout + ;; + mep-*) + os=elf + ;; + mips*-cisco) + os=elf + ;; + mips*-*) + os=elf + ;; + or32-*) + os=coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=sysv3 + ;; + sparc-* | *-sun) + os=sunos4.1.1 + ;; + pru-*) + os=elf + ;; + *-be) + os=beos + ;; + *-ibm) + os=aix + ;; + *-knuth) + os=mmixware + ;; + *-wec) + os=proelf + ;; + *-winbond) + os=proelf + ;; + *-oki) + os=proelf + ;; + *-hp) + os=hpux + ;; + *-hitachi) + os=hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=sysv + ;; + *-cbm) + os=amigaos + ;; + *-dg) + os=dgux + ;; + *-dolphin) + os=sysv3 + ;; + m68k-ccur) + os=rtu + ;; + m88k-omron*) + os=luna + ;; + *-next) + os=nextstep + ;; + *-sequent) + os=ptx + ;; + *-crds) + os=unos + ;; + *-ns) + os=genix + ;; + i370-*) + os=mvs + ;; + *-gould) + os=sysv + ;; + *-highlevel) + os=bsd + ;; + *-encore) + os=bsd + ;; + *-sgi) + os=irix + ;; + *-siemens) + os=sysv4 + ;; + *-masscomp) + os=rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=uxpv + ;; + *-rom68k) + os=coff + ;; + *-*bug) + os=coff + ;; + *-apple) + os=macos + ;; + *-atari*) + os=mint + ;; + *-wrs) + os=vxworks + ;; + *) + os=none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +case $vendor in + unknown) + case $os in + riscix*) + vendor=acorn + ;; + sunos*) + vendor=sun + ;; + cnk*|-aix*) + vendor=ibm + ;; + beos*) + vendor=be + ;; + hpux*) + vendor=hp + ;; + mpeix*) + vendor=hp + ;; + hiux*) + vendor=hitachi + ;; + unos*) + vendor=crds + ;; + dgux*) + vendor=dg + ;; + luna*) + vendor=omron + ;; + genix*) + vendor=ns + ;; + clix*) + vendor=intergraph + ;; + mvs* | opened*) + vendor=ibm + ;; + os400*) + vendor=ibm + ;; + ptx*) + vendor=sequent + ;; + tpf*) + vendor=ibm + ;; + vxsim* | vxworks* | windiss*) + vendor=wrs + ;; + aux*) + vendor=apple + ;; + hms*) + vendor=hitachi + ;; + mpw* | macos*) + vendor=apple + ;; + *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) + vendor=atari + ;; + vos*) + vendor=stratus + ;; + esac + ;; +esac + +echo "$cpu-$vendor-$os" +exit + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: \ No newline at end of file diff --git a/3rd_party/glue/pthread.h b/3rd_party/glue/pthread.h new file mode 100644 index 00000000..f6c13c45 --- /dev/null +++ b/3rd_party/glue/pthread.h @@ -0,0 +1,38 @@ +#ifndef REPERTORY_PTHREAD_H +#define REPERTORY_PTHREAD_H +#ifdef _WIN32 + +#include +#include + +#define pthread_mutex_t std::mutex * +#define pthread_cond_t std::condition_variable * + +static void pthread_mutex_init(pthread_mutex_t *mtx, void *) { *mtx = new std::mutex(); } + +static void pthread_mutex_destroy(pthread_mutex_t *mtx) { + delete *mtx; + *mtx = nullptr; +} + +static void pthread_mutex_lock(pthread_mutex_t *mtx) { (*mtx)->lock(); } + +static void pthread_mutex_unlock(pthread_mutex_t *mtx) { (*mtx)->unlock(); } + +static void pthread_cond_init(pthread_cond_t *cond, void *) { + *cond = new std::condition_variable(); +} + +static void pthread_cond_destroy(pthread_cond_t *cond) { + delete *cond; + *cond = nullptr; +} + +static void pthread_cond_signal(pthread_cond_t *cond) { (*cond)->notify_one(); } + +static void pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mtx) { + std::unique_lock l(**mtx); + (*cond)->wait(l); +} +#endif // _WIN32 +#endif // REPERTORY_PTHREAD_H diff --git a/3rd_party/glue/strings.h b/3rd_party/glue/strings.h new file mode 100644 index 00000000..0d0183ee --- /dev/null +++ b/3rd_party/glue/strings.h @@ -0,0 +1,9 @@ +#ifndef REPERTORY_STRINGS_H +#define REPERTORY_STRINGS_H +#ifdef _WIN32 + +#define strncasecmp _strnicmp +#define strcasecmp _stricmp + +#endif // _WIN32 +#endif // REPERTORY_STRINGS_H diff --git a/3rd_party/glue/sys/socket.h b/3rd_party/glue/sys/socket.h new file mode 100644 index 00000000..1a4b3519 --- /dev/null +++ b/3rd_party/glue/sys/socket.h @@ -0,0 +1,6 @@ +#ifndef REPERTORY_SOCKET_H +#define REPERTORY_SOCKET_H +#ifdef _WIN32 + +#endif // _WIN32 +#endif // REPERTORY_SOCKET_H diff --git a/3rd_party/glue/sys/time.h b/3rd_party/glue/sys/time.h new file mode 100644 index 00000000..11328352 --- /dev/null +++ b/3rd_party/glue/sys/time.h @@ -0,0 +1,8 @@ +#ifndef REPERTORY_TIME_H +#define REPERTORY_TIME_H +#ifdef _WIN32 + + + +#endif // _WIN32 +#endif // REPERTORY_TIME_H diff --git a/3rd_party/glue/unistd.h b/3rd_party/glue/unistd.h new file mode 100644 index 00000000..18c79870 --- /dev/null +++ b/3rd_party/glue/unistd.h @@ -0,0 +1,8 @@ +#ifndef REPERTORY_UNISTD_H +#define REPERTORY_UNISTD_H +#ifdef _WIN32 + + + +#endif // _WIN32 +#endif // REPERTORY_UNISTD_H diff --git a/3rd_party/json/json.hpp b/3rd_party/json/json.hpp new file mode 100644 index 00000000..408d83cb --- /dev/null +++ b/3rd_party/json/json.hpp @@ -0,0 +1,22109 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.10.5 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2022 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file doc/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 10 +#define NLOHMANN_JSON_VERSION_PATCH 5 + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include + + +#include +#include + +// #include + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include + + +#include // exception +#include // runtime_error +#include // to_string +#include // vector + +// #include + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +namespace nlohmann +{ +namespace detail +{ +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +} +} // namespace detail +} // namespace nlohmann + +// #include + + +#include +// #include + + +#include // declval, pair +// #include + + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + +// #include + + +#include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann + + +// https://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; +} // namespace detail +} // namespace nlohmann + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1914 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow disabling exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow overriding assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); +#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { Type nlohmann_json_default_obj; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +inline void replace_substring(std::string& s, const std::string& f, + const std::string& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +inline std::string escape(std::string s) +{ + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +static void unescape(std::string& s) +{ + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/// @brief general exception of the @ref basic_json class +/// @sa https://json.nlohmann.me/api/basic_json/exception/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing) + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + template + static std::string diagnostics(const BasicJsonType& leaf_element) + { +#if JSON_DIAGNOSTICS + std::vector tokens; + for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (¤t->m_parent->m_value.array->operator[](i) == current) + { + tokens.emplace_back(std::to_string(i)); + break; + } + } + break; + } + + case value_t::object: + { + for (const auto& element : *current->m_parent->m_value.object) + { + if (&element.second == current) + { + tokens.emplace_back(element.first.c_str()); + break; + } + } + break; + } + + case value_t::null: // LCOV_EXCL_LINE + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + } + } + + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }) + ") "; +#else + static_cast(leaf_element); + return ""; +#endif + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/// @brief exception indicating a parse error +/// @sa https://json.nlohmann.me/api/basic_json/parse_error/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + template + static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + exception::diagnostics(context) + what_arg; + return {id_, pos.chars_read_total, w.c_str()}; + } + + template + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + exception::diagnostics(context) + what_arg; + return {id_, byte_, w.c_str()}; + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/// @brief exception indicating errors with iterators +/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/ +class invalid_iterator : public exception +{ + public: + template + static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/// @brief exception indicating executing a member function with a wrong type +/// @sa https://json.nlohmann.me/api/basic_json/type_error/ +class type_error : public exception +{ + public: + template + static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/// @brief exception indicating access out of the defined range +/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/ +class out_of_range : public exception +{ + public: + template + static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/// @brief exception indicating other library errors +/// @sa https://json.nlohmann.me/api/basic_json/other_error/ +class other_error : public exception +{ + public: + template + static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; // NOLINT(readability-redundant-declaration) + +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct identity_tag {}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple + +// #include + + +// #include + + +#include // random_access_iterator_tag + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +// #include + + +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); +} // namespace nlohmann + +// #include + + +// #include + + +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); +} // namespace nlohmann + +// #include + +// #include + +// #include +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +/// a class to store JSON values +/// @sa https://json.nlohmann.me/api/basic_json/ +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector> +class basic_json; + +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template +class json_pointer; + +/*! +@brief default specialization +@sa https://json.nlohmann.me/api/json/ +*/ +using json = basic_json<>; + +/// @brief a minimal map-like container that preserves insertion order +/// @sa https://json.nlohmann.me/api/ordered_map/ +template +struct ordered_map; + +/// @brief specialization that maintains the insertion order of object keys +/// @sa https://json.nlohmann.me/api/ordered_json/ +using ordered_json = basic_json; + +} // namespace nlohmann + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +namespace nlohmann +{ +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B1 { }; +template +struct conjunction +: std::conditional, B1>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < + is_detected::value&& + is_iterator_traits>>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 + !std::is_same>::value >> +{ + static constexpr bool value = + is_constructible>::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 +!std::is_same>::value&& + is_complete_type < + detected_t>::value >> +{ + using value_type = range_value_t; + + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +// a naive helper to check if a type is an ordered_map (exploits the fact that +// ordered_map inherits capacity() from std::vector) +template +struct is_ordered_map +{ + using one = char; + + struct two + { + char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + }; + + template static one test( decltype(&C::capacity) ) ; + template static two test(...); + + enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) +}; + +// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) +template < typename T, typename U, enable_if_t < !std::is_same::value, int > = 0 > +T conditional_static_cast(U value) +{ + return static_cast(value); +} + +template::value, int> = 0> +T conditional_static_cast(U value) +{ + return value; +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#if JSON_HAS_EXPERIMENTAL_FILESYSTEM +#include +namespace nlohmann::detail +{ +namespace std_fs = std::experimental::filesystem; +} // namespace nlohmann::detail +#elif JSON_HAS_FILESYSTEM +#include +namespace nlohmann::detail +{ +namespace std_fs = std::filesystem; +} // namespace nlohmann::detail +#endif + +namespace nlohmann +{ +namespace detail +{ +template +void from_json(const BasicJsonType& j, typename std::nullptr_t& n) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_null())) + { + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j)); + } + n = nullptr; +} + +// overloads for basic_json template parameters +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < std::is_arithmetic::value&& + !std::is_same::value, + int > = 0 > +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::binary: + case value_t::discarded: + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + } +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) + { + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j)); + } + b = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + s = *j.template get_ptr(); +} + +template < + typename BasicJsonType, typename ConstructibleStringType, + enable_if_t < + is_constructible_string_type::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ConstructibleStringType& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + + s = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, EnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} + +// forward_list doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + l.clear(); + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType & i) + { + return i.template get(); + }); +} + +// valarray doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + l.resize(j.size()); + std::transform(j.begin(), j.end(), std::begin(l), + [](const BasicJsonType & elem) + { + return elem.template get(); + }); +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) +{ + arr = *j.template get_ptr(); +} + +template +auto from_json_array_impl(const BasicJsonType& j, std::array& arr, + priority_tag<2> /*unused*/) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template::value, + int> = 0> +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) +-> decltype( + arr.reserve(std::declval()), + j.template get(), + void()) +{ + using std::end; + + ConstructibleArrayType ret; + ret.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(ret, end(ret)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template::value, + int> = 0> +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, + priority_tag<0> /*unused*/) +{ + using std::end; + + ConstructibleArrayType ret; + std::transform( + j.begin(), j.end(), std::inserter(ret, end(ret)), + [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template < typename BasicJsonType, typename ConstructibleArrayType, + enable_if_t < + is_constructible_array_type::value&& + !is_constructible_object_type::value&& + !is_constructible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) +-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), +j.template get(), +void()) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + from_json_array_impl(j, arr, priority_tag<3> {}); +} + +template < typename BasicJsonType, typename T, std::size_t... Idx > +std::array from_json_inplace_array_impl(BasicJsonType&& j, + identity_tag> /*unused*/, index_sequence /*unused*/) +{ + return { { std::forward(j).at(Idx).template get()... } }; +} + +template < typename BasicJsonType, typename T, std::size_t N > +auto from_json(BasicJsonType&& j, identity_tag> tag) +-> decltype(from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j)); + } + + bin = *j.template get_ptr(); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j)); + } + + ConstructibleObjectType ret; + const auto* inner_object = j.template get_ptr(); + using value_type = typename ConstructibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(ret, ret.begin()), + [](typename BasicJsonType::object_t::value_type const & p) + { + return value_type(p.first, p.second.template get()); + }); + obj = std::move(ret); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < + std::is_arithmetic::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::string: + case value_t::binary: + case value_t::discarded: + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + } +} + +template +std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get()...); +} + +template < typename BasicJsonType, class A1, class A2 > +std::pair from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) +{ + return {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get()}; +} + +template +void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) +{ + p = from_json_tuple_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); +} + +template +std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) +{ + return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); +} + +template +void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) +{ + t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); +} + +template +auto from_json(BasicJsonType&& j, TupleRelated&& t) +-> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); +} + +template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::unordered_map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +template +void from_json(const BasicJsonType& j, std_fs::path& p) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + p = *j.template get_ptr(); +} +#endif + +struct from_json_fn +{ + template + auto operator()(const BasicJsonType& j, T&& val) const + noexcept(noexcept(from_json(j, std::forward(val)))) + -> decltype(from_json(j, std::forward(val))) + { + return from_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `from_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) +{ +constexpr const auto& from_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) +} // namespace +} // namespace nlohmann + +// #include + + +#include // copy +#include // begin, end +#include // string +#include // tuple, get +#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type +#include // move, forward, declval, pair +#include // valarray +#include // vector + +// #include + +// #include + + +#include // size_t +#include // input_iterator_tag +#include // string, to_string +#include // tuple_size, get, tuple_element +#include // move + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +void int_to_string( string_type& target, std::size_t value ) +{ + // For ADL + using std::to_string; + target = to_string(value); +} +template class iteration_proxy_value +{ + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_value; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; + + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable string_type array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const string_type empty_str{}; + + public: + explicit iteration_proxy_value(IteratorType it) noexcept + : anchor(std::move(it)) + {} + + /// dereference operator (needed for range-based for) + iteration_proxy_value& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_value& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_value& o) const + { + return anchor == o.anchor; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_value& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + const string_type& key() const + { + JSON_ASSERT(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + if (array_index != array_index_last) + { + int_to_string( array_index_str, array_index ); + array_index_last = array_index; + } + return array_index_str; + } + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return empty_str; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } +}; + +/// proxy class for the items() function +template class iteration_proxy +{ + private: + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_value begin() noexcept + { + return iteration_proxy_value(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_value end() noexcept + { + return iteration_proxy_value(container.end()); + } +}; +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) +{ + return i.key(); +} +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) +{ + return i.value(); +} +} // namespace detail +} // namespace nlohmann + +// The Addition to the STD Namespace is required to add +// Structured Bindings Support to the iteration_proxy_value class +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +namespace std +{ +#if defined(__clang__) + // Fix: https://github.com/nlohmann/json/issues/1401 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +class tuple_size<::nlohmann::detail::iteration_proxy_value> + : public std::integral_constant {}; + +template +class tuple_element> +{ + public: + using type = decltype( + get(std::declval < + ::nlohmann::detail::iteration_proxy_value> ())); +}; +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} // namespace std + +// #include + +// #include + +// #include + + +#if JSON_HAS_EXPERIMENTAL_FILESYSTEM +#include +namespace nlohmann::detail +{ +namespace std_fs = std::experimental::filesystem; +} // namespace nlohmann::detail +#elif JSON_HAS_FILESYSTEM +#include +namespace nlohmann::detail +{ +namespace std_fs = std::filesystem; +} // namespace nlohmann::detail +#endif + +namespace nlohmann +{ +namespace detail +{ +////////////////// +// constructors // +////////////////// + +/* + * Note all external_constructor<>::construct functions need to call + * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an + * allocated value (e.g., a string). See bug issue + * https://github.com/nlohmann/json/issues/2865 for more information. + */ + +template struct external_constructor; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleStringType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleStringType& str) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value.string = j.template create(str); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(b); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(std::move(b)); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = arr; + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.set_parents(); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); + j.set_parent(j.m_value.array->back()); + } + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.set_parents(); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value = obj; + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.set_parents(); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < !std::is_same::value, int > = 0 > + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.set_parents(); + j.assert_invariant(); + } +}; + +///////////// +// to_json // +///////////// + +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) +{ + external_constructor::construct(j, std::move(s)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, EnumType e) noexcept +{ + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); +} + +template +void to_json(BasicJsonType& j, const std::vector& e) +{ + external_constructor::construct(j, e); +} + +template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < is_compatible_array_type::value&& + !is_compatible_object_type::value&& + !is_compatible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} + +template +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) +{ + external_constructor::construct(j, bin); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::valarray& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < is_compatible_object_type::value&& !is_basic_json::value, int > = 0 > +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +{ + external_constructor::construct(j, obj); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) +{ + external_constructor::construct(j, std::move(obj)); +} + +template < + typename BasicJsonType, typename T, std::size_t N, + enable_if_t < !std::is_constructible::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + int > = 0 > +void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + external_constructor::construct(j, arr); +} + +template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > +void to_json(BasicJsonType& j, const std::pair& p) +{ + j = { p.first, p.second }; +} + +// for https://github.com/nlohmann/json/pull/1134 +template>::value, int> = 0> +void to_json(BasicJsonType& j, const T& b) +{ + j = { {b.key(), b.value()} }; +} + +template +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) +{ + j = { std::get(t)... }; +} + +template::value, int > = 0> +void to_json(BasicJsonType& j, const T& t) +{ + to_json_tuple_impl(j, t, make_index_sequence::value> {}); +} + +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +template +void to_json(BasicJsonType& j, const std_fs::path& p) +{ + j = p.string(); +} +#endif + +struct to_json_fn +{ + template + auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `to_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) +{ +constexpr const auto& to_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) +} // namespace +} // namespace nlohmann + +// #include + +// #include + + +namespace nlohmann +{ + +/// @sa https://json.nlohmann.me/api/adl_serializer/ +template +struct adl_serializer +{ + /// @brief convert a JSON value to any value type + /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/ + template + static auto from_json(BasicJsonType && j, TargetType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + -> decltype(::nlohmann::from_json(std::forward(j), val), void()) + { + ::nlohmann::from_json(std::forward(j), val); + } + + /// @brief convert a JSON value to any value type + /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/ + template + static auto from_json(BasicJsonType && j) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), detail::identity_tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) + { + return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); + } + + /// @brief convert any value type to a JSON value + /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/ + template + static auto to_json(BasicJsonType& j, TargetType && val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; +} // namespace nlohmann + +// #include + + +#include // uint8_t, uint64_t +#include // tie +#include // move + +namespace nlohmann +{ + +/// @brief an internal type for a backed binary type +/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/ +template +class byte_container_with_subtype : public BinaryType +{ + public: + using container_type = BinaryType; + using subtype_type = std::uint64_t; + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype() noexcept(noexcept(container_type())) + : container_type() + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b))) + : container_type(b) + , m_subtype(subtype_) + , m_has_subtype(true) + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + , m_subtype(subtype_) + , m_has_subtype(true) + {} + + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast(*this), m_subtype, m_has_subtype) == + std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); + } + + bool operator!=(const byte_container_with_subtype& rhs) const + { + return !(rhs == *this); + } + + /// @brief sets the binary subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/ + void set_subtype(subtype_type subtype_) noexcept + { + m_subtype = subtype_; + m_has_subtype = true; + } + + /// @brief return the binary subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/ + constexpr subtype_type subtype() const noexcept + { + return m_has_subtype ? m_subtype : static_cast(-1); + } + + /// @brief return whether the value has a subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /// @brief clears the binary subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + subtype_type m_subtype = 0; + bool m_has_subtype = false; +}; + +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + + +#include // uint8_t +#include // size_t +#include // hash + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +// boost::hash_combine +inline std::size_t combine(std::size_t seed, std::size_t h) noexcept +{ + seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); + return seed; +} + +/*! +@brief hash a JSON value + +The hash function tries to rely on std::hash where possible. Furthermore, the +type of the JSON value is taken into account to have different hash values for +null, 0, 0U, and false, etc. + +@tparam BasicJsonType basic_json specialization +@param j JSON value to hash +@return hash value of j +*/ +template +std::size_t hash(const BasicJsonType& j) +{ + using string_t = typename BasicJsonType::string_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + + const auto type = static_cast(j.type()); + switch (j.type()) + { + case BasicJsonType::value_t::null: + case BasicJsonType::value_t::discarded: + { + return combine(type, 0); + } + + case BasicJsonType::value_t::object: + { + auto seed = combine(type, j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } + + case BasicJsonType::value_t::array: + { + auto seed = combine(type, j.size()); + for (const auto& element : j) + { + seed = combine(seed, hash(element)); + } + return seed; + } + + case BasicJsonType::value_t::string: + { + const auto h = std::hash {}(j.template get_ref()); + return combine(type, h); + } + + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_unsigned: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_float: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::binary: + { + auto seed = combine(type, j.get_binary().size()); + const auto h = std::hash {}(j.get_binary().has_subtype()); + seed = combine(seed, h); + seed = combine(seed, static_cast(j.get_binary().subtype())); + for (const auto byte : j.get_binary()) + { + seed = combine(seed, std::hash {}(byte)); + } + return seed; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return 0; // LCOV_EXCL_LINE + } +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // generate_n +#include // array +#include // ldexp +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // snprintf +#include // memcpy +#include // back_inserter +#include // numeric_limits +#include // char_traits, string +#include // make_pair, move +#include // vector + +// #include + +// #include + + +#include // array +#include // size_t +#include // strlen +#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next +#include // shared_ptr, make_shared, addressof +#include // accumulate +#include // string, char_traits +#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer +#include // pair, declval + +#ifndef JSON_NO_IO + #include // FILE * + #include // istream +#endif // JSON_NO_IO + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// the supported input formats +enum class input_format_t { json, cbor, msgpack, ubjson, bson }; + +//////////////////// +// input adapters // +//////////////////// + +#ifndef JSON_NO_IO +/*! +Input adapter for stdio file access. This adapter read only 1 byte and do not use any + buffer. This adapter is a very low level adapter. +*/ +class file_input_adapter +{ + public: + using char_type = char; + + JSON_HEDLEY_NON_NULL(2) + explicit file_input_adapter(std::FILE* f) noexcept + : m_file(f) + {} + + // make class move-only + file_input_adapter(const file_input_adapter&) = delete; + file_input_adapter(file_input_adapter&&) noexcept = default; + file_input_adapter& operator=(const file_input_adapter&) = delete; + file_input_adapter& operator=(file_input_adapter&&) = delete; + ~file_input_adapter() = default; + + std::char_traits::int_type get_character() noexcept + { + return std::fgetc(m_file); + } + + private: + /// the file pointer to read from + std::FILE* m_file; +}; + + +/*! +Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at +beginning of input. Does not support changing the underlying std::streambuf +in mid-input. Maintains underlying std::istream and std::streambuf to support +subsequent use of standard std::istream operations to process any input +characters following those used in parsing the JSON input. Clears the +std::istream flags; any input errors (e.g., EOF) will be detected by the first +subsequent call for input from the std::istream. +*/ +class input_stream_adapter +{ + public: + using char_type = char; + + ~input_stream_adapter() + { + // clear stream flags; we use underlying streambuf I/O, do not + // maintain ifstream flags, except eof + if (is != nullptr) + { + is->clear(is->rdstate() & std::ios::eofbit); + } + } + + explicit input_stream_adapter(std::istream& i) + : is(&i), sb(i.rdbuf()) + {} + + // delete because of pointer members + input_stream_adapter(const input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&&) = delete; + + input_stream_adapter(input_stream_adapter&& rhs) noexcept + : is(rhs.is), sb(rhs.sb) + { + rhs.is = nullptr; + rhs.sb = nullptr; + } + + // std::istream/std::streambuf use std::char_traits::to_int_type, to + // ensure that std::char_traits::eof() and the character 0xFF do not + // end up as the same value, e.g. 0xFFFFFFFF. + std::char_traits::int_type get_character() + { + auto res = sb->sbumpc(); + // set eof manually, as we don't use the istream interface. + if (JSON_HEDLEY_UNLIKELY(res == std::char_traits::eof())) + { + is->clear(is->rdstate() | std::ios::eofbit); + } + return res; + } + + private: + /// the associated input stream + std::istream* is = nullptr; + std::streambuf* sb = nullptr; +}; +#endif // JSON_NO_IO + +// General-purpose iterator-based adapter. It might not be as fast as +// theoretically possible for some containers, but it is extremely versatile. +template +class iterator_input_adapter +{ + public: + using char_type = typename std::iterator_traits::value_type; + + iterator_input_adapter(IteratorType first, IteratorType last) + : current(std::move(first)), end(std::move(last)) + {} + + typename std::char_traits::int_type get_character() + { + if (JSON_HEDLEY_LIKELY(current != end)) + { + auto result = std::char_traits::to_int_type(*current); + std::advance(current, 1); + return result; + } + + return std::char_traits::eof(); + } + + private: + IteratorType current; + IteratorType end; + + template + friend struct wide_string_input_helper; + + bool empty() const + { + return current == end; + } +}; + + +template +struct wide_string_input_helper; + +template +struct wide_string_input_helper +{ + // UTF-32 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-32 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (wc <= 0xFFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else if (wc <= 0x10FFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xF0u | ((static_cast(wc) >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } +}; + +template +struct wide_string_input_helper +{ + // UTF-16 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-16 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (0xD800 > wc || wc >= 0xE000) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else + { + if (JSON_HEDLEY_UNLIKELY(!input.empty())) + { + const auto wc2 = static_cast(input.get_character()); + const auto charcode = 0x10000u + (((static_cast(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } + } +}; + +// Wraps another input apdater to convert wide character types into individual bytes. +template +class wide_string_input_adapter +{ + public: + using char_type = char; + + wide_string_input_adapter(BaseInputAdapter base) + : base_adapter(base) {} + + typename std::char_traits::int_type get_character() noexcept + { + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) + { + fill_buffer(); + + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index == 0); + } + + // use buffer + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; + } + + private: + BaseInputAdapter base_adapter; + + template + void fill_buffer() + { + wide_string_input_helper::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + } + + /// a buffer for UTF-8 bytes + std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; + + /// index to the utf8_codes array for the next valid byte + std::size_t utf8_bytes_index = 0; + /// number of valid bytes in the utf8_codes array + std::size_t utf8_bytes_filled = 0; +}; + + +template +struct iterator_input_adapter_factory +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using adapter_type = iterator_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(std::move(first), std::move(last)); + } +}; + +template +struct is_iterator_of_multibyte +{ + using value_type = typename std::iterator_traits::value_type; + enum + { + value = sizeof(value_type) > 1 + }; +}; + +template +struct iterator_input_adapter_factory::value>> +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using base_adapter_type = iterator_input_adapter; + using adapter_type = wide_string_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(base_adapter_type(std::move(first), std::move(last))); + } +}; + +// General purpose iterator-based input +template +typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) +{ + using factory_type = iterator_input_adapter_factory; + return factory_type::create(first, last); +} + +// Convenience shorthand from container to iterator +// Enables ADL on begin(container) and end(container) +// Encloses the using declarations in namespace for not to leak them to outside scope + +namespace container_input_adapter_factory_impl +{ + +using std::begin; +using std::end; + +template +struct container_input_adapter_factory {}; + +template +struct container_input_adapter_factory< ContainerType, + void_t()), end(std::declval()))>> + { + using adapter_type = decltype(input_adapter(begin(std::declval()), end(std::declval()))); + + static adapter_type create(const ContainerType& container) +{ + return input_adapter(begin(container), end(container)); +} + }; + +} // namespace container_input_adapter_factory_impl + +template +typename container_input_adapter_factory_impl::container_input_adapter_factory::adapter_type input_adapter(const ContainerType& container) +{ + return container_input_adapter_factory_impl::container_input_adapter_factory::create(container); +} + +#ifndef JSON_NO_IO +// Special cases with fast paths +inline file_input_adapter input_adapter(std::FILE* file) +{ + return file_input_adapter(file); +} + +inline input_stream_adapter input_adapter(std::istream& stream) +{ + return input_stream_adapter(stream); +} + +inline input_stream_adapter input_adapter(std::istream&& stream) +{ + return input_stream_adapter(stream); +} +#endif // JSON_NO_IO + +using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); + +// Null-delimited strings, and the like. +template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + !std::is_array::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > +contiguous_bytes_input_adapter input_adapter(CharT b) +{ + auto length = std::strlen(reinterpret_cast(b)); + const auto* ptr = reinterpret_cast(b); + return input_adapter(ptr, ptr + length); +} + +template +auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + return input_adapter(array, array + N); +} + +// This class only handles inputs of input_buffer_adapter type. +// It's required so that expressions like {ptr, len} can be implicitly cast +// to the correct adapter. +class span_input_adapter +{ + public: + template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > + span_input_adapter(CharT b, std::size_t l) + : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} + + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + span_input_adapter(IteratorType first, IteratorType last) + : ia(input_adapter(first, last)) {} + + contiguous_bytes_input_adapter&& get() + { + return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg) + } + + private: + contiguous_bytes_input_adapter ia; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include +#include // string +#include // move +#include // vector + +// #include + +// #include + + +namespace nlohmann +{ + +/*! +@brief SAX interface + +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template +struct json_sax +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @brief a null value was read + @return whether parsing should proceed + */ + virtual bool null() = 0; + + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; + + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; + + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; + + /*! + @brief a floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; + + /*! + @brief a string value was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string value. + */ + virtual bool string(string_t& val) = 0; + + /*! + @brief a binary value was read + @param[in] val binary value + @return whether parsing should proceed + @note It is safe to move the passed binary value. + */ + virtual bool binary(binary_t& val) = 0; + + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; + + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; + + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; + + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; + + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; + + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; + + json_sax() = default; + json_sax(const json_sax&) = default; + json_sax(json_sax&&) noexcept = default; + json_sax& operator=(const json_sax&) = default; + json_sax& operator=(json_sax&&) noexcept = default; + virtual ~json_sax() = default; +}; + + +namespace detail +{ +/*! +@brief SAX implementation to create a JSON value from SAX events + +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. + +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. + +@tparam BasicJsonType the JSON type +*/ +template +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @param[in,out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + + if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool key(string_t& val) + { + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; + } + + bool end_object() + { + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + + if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool end_array() + { + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + */ + template + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward(v)); + return &root; + } + + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::forward(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + JSON_ASSERT(ref_stack.back()->is_object()); + JSON_ASSERT(object_element); + *object_element = BasicJsonType(std::forward(v)); + return object_element; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +template +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser(BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } + + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_callback_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + // check object limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + + // check callback for key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); + + // add discarded value at given key and store the reference for later + if (keep && ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } + + return true; + } + + bool end_object() + { + if (ref_stack.back()) + { + if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + else + { + ref_stack.back()->set_parents(); + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) + { + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } + } + + return true; + } + + bool start_array(std::size_t len) + { + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); + + // check array limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool end_array() + { + bool keep = true; + + if (ref_stack.back()) + { + keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (keep) + { + ref_stack.back()->set_parents(); + } + else + { + // discard array + *ref_stack.back() = discarded; + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->pop_back(); + } + + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template + std::pair handle_value(Value&& v, const bool skip_callback = false) + { + JSON_ASSERT(!keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (!keep_stack.back()) + { + return {false, nullptr}; + } + + // create value + auto value = BasicJsonType(std::forward(v)); + + // check callback + const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (!keep) + { + return {false, nullptr}; + } + + if (ref_stack.empty()) + { + root = std::move(value); + return {true, &root}; + } + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (!ref_stack.back()) + { + return {false, nullptr}; + } + + // we now only expect arrays and objects + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } + + // object + JSON_ASSERT(ref_stack.back()->is_object()); + // check if we should store an element for the current key + JSON_ASSERT(!key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (!store_element) + { + return {false, nullptr}; + } + + JSON_ASSERT(object_element); + *object_element = std::move(value); + return {true, object_element}; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// stack to manage which values to keep + std::vector keep_stack {}; + /// stack to manage which object keys to keep + std::vector key_keep_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; +}; + +template +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + bool null() + { + return true; + } + + bool boolean(bool /*unused*/) + { + return true; + } + + bool number_integer(number_integer_t /*unused*/) + { + return true; + } + + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } + + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } + + bool string(string_t& /*unused*/) + { + return true; + } + + bool binary(binary_t& /*unused*/) + { + return true; + } + + bool start_object(std::size_t /*unused*/ = static_cast(-1)) + { + return true; + } + + bool key(string_t& /*unused*/) + { + return true; + } + + bool end_object() + { + return true; + } + + bool start_array(std::size_t /*unused*/ = static_cast(-1)) + { + return true; + } + + bool end_array() + { + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) + { + return false; + } +}; +} // namespace detail + +} // namespace nlohmann + +// #include + + +#include // array +#include // localeconv +#include // size_t +#include // snprintf +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // char_traits, string +#include // move +#include // vector + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////// +// lexer // +/////////// + +template +class lexer_base +{ + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; + + /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + // LCOV_EXCL_START + default: // catch non-enum values + return "unknown token"; + // LCOV_EXCL_STOP + } + } +}; +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer : public lexer_base +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + using token_type = typename lexer_base::token_type; + + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast(get_decimal_point())) + {} + + // delete because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~lexer() = default; + + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept + { + const auto* loc = localeconv(); + JSON_ASSERT(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } + + ///////////////////// + // scan functions + ///////////////////// + + /*! + @brief get codepoint from 4 hex characters following `\u` + + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) + + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. + + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() + { + // this function only makes sense after reading `\u` + JSON_ASSERT(current == 'u'); + int codepoint = 0; + + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) + { + get(); + + if (current >= '0' && current <= '9') + { + codepoint += static_cast((static_cast(current) - 0x30u) << factor); + } + else if (current >= 'A' && current <= 'F') + { + codepoint += static_cast((static_cast(current) - 0x37u) << factor); + } + else if (current >= 'a' && current <= 'f') + { + codepoint += static_cast((static_cast(current) - 0x57u) << factor); + } + else + { + return -1; + } + } + + JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); + return codepoint; + } + + /*! + @brief check if the next byte(s) are inside a given range + + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. + + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively + + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. + + @return true if and only if no range violation was detected + */ + bool next_byte_in_range(std::initializer_list ranges) + { + JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) + { + get(); + if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + + /*! + @brief scan a string literal + + This function scans a string according to Sect. 7 of RFC 8259. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. + + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise + + @note In case of errors, variable error_message contains a textual + description. + */ + token_type scan_string() + { + // reset token_buffer (ignore opening quote) + reset(); + + // we entered the function by reading an open quote + JSON_ASSERT(current == '\"'); + + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } + + // closing quote + case '\"': + { + return token_type::value_string; + } + + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 + + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if code point is a high surrogate + if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) + { + const int codepoint2 = get_codepoint(); + + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast( + // high surrogate occupies the most significant 22 bits + (static_cast(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result, so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } + + // result of the above calculation yields a proper codepoint + JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); + + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(static_cast(codepoint)); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + + break; + } + + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } + + break; + } + + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } + + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } + + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } + + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } + + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } + + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } + + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } + + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } + + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } + + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } + + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } + + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } + + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } + + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } + + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } + + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } + + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } + + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } + + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } + + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } + + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } + + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } + + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } + + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } + + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } + + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } + + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } + + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } + + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } + + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } + + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } + + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } + + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } + + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } + + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + switch (get()) + { + // single-line comments skip input until a newline or EOF is read + case '/': + { + while (true) + { + switch (get()) + { + case '\n': + case '\r': + case std::char_traits::eof(): + case '\0': + return true; + + default: + break; + } + } + } + + // multi-line comments skip input until */ is read + case '*': + { + while (true) + { + switch (get()) + { + case std::char_traits::eof(): + case '\0': + { + error_message = "invalid comment; missing closing '*/'"; + return false; + } + + case '*': + { + switch (get()) + { + case '/': + return true; + + default: + { + unget(); + continue; + } + } + } + + default: + continue; + } + } + } + + // unexpected character after reading '/' + default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; + return false; + } + } + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept + { + f = std::strtof(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); + } + + /*! + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 8259. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 8259. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() // lgtm [cpp/use-of-goto] + { + // reset token_buffer to store the number's bytes + reset(); + + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; + + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } + + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } + +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } + +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + goto scan_number_done; + } + +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); + + char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + errno = 0; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } + + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + return token_type::value_float; + } + + /*! + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success + */ + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char_type* literal_text, const std::size_t length, + token_type return_type) + { + JSON_ASSERT(std::char_traits::to_char_type(current) == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) + { + if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; + } + + ///////////////////// + // input management + ///////////////////// + + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } + + /* + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `std::char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. + + @return character read from the input + */ + char_int_type get() + { + ++position.chars_read_total; + ++position.chars_read_current_line; + + if (next_unget) + { + // just reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia.get_character(); + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + token_string.push_back(std::char_traits::to_char_type(current)); + } + + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } + + return current; + } + + /*! + @brief unget current character (read it again on next get) + + We implement unget by setting variable next_unget to true. The input is not + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. + */ + void unget() + { + next_unget = true; + + --position.chars_read_total; + + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) + { + if (position.lines_read > 0) + { + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + JSON_ASSERT(!token_string.empty()); + token_string.pop_back(); + } + } + + /// add a character to token_buffer + void add(char_int_type c) + { + token_buffer.push_back(static_cast(c)); + } + + public: + ///////////////////// + // value getters + ///////////////////// + + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } + + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } + + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } + + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() + { + return token_buffer; + } + + ///////////////////// + // diagnostics + ///////////////////// + + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; + } + + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) + { + if (static_cast(c) <= '\x1F') + { + // escape control characters + std::array cs{{}}; + static_cast((std::snprintf)(cs.data(), cs.size(), "", static_cast(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + result += cs.data(); + } + else + { + // add character as is + result.push_back(static_cast(c)); + } + } + + return result; + } + + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } + + ///////////////////// + // actual scanner + ///////////////////// + + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped + */ + bool skip_bom() + { + if (get() == 0xEF) + { + // check if we completely parse the BOM + return get() == 0xBB && get() == 0xBF; + } + + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } + + void skip_whitespace() + { + do + { + get(); + } + while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); + } + + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 && !skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } + + // read next character and ignore whitespace + skip_whitespace(); + + // ignore comments + while (ignore_comments && current == '/') + { + if (!scan_comment()) + { + return token_type::parse_error; + } + + // skip following whitespace + skip_whitespace(); + } + + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + { + std::array true_literal = {{static_cast('t'), static_cast('r'), static_cast('u'), static_cast('e')}}; + return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); + } + case 'f': + { + std::array false_literal = {{static_cast('f'), static_cast('a'), static_cast('l'), static_cast('s'), static_cast('e')}}; + return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); + } + case 'n': + { + std::array null_literal = {{static_cast('n'), static_cast('u'), static_cast('l'), static_cast('l')}}; + return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); + } + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } + + private: + /// input adapter + InputAdapterType ia; + + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; + + /// the current character + char_int_type current = std::char_traits::eof(); + + /// whether the next get() call should just return current + bool next_unget = false; + + /// the start position of the current token + position_t position {}; + + /// raw input token string (for error messages) + std::vector token_string {}; + + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; + + /// a description of occurred lexer errors + const char* error_message = ""; + + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; + + /// the decimal point + const char_int_type decimal_point_char = '.'; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // declval +#include // string + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +using null_function_t = decltype(std::declval().null()); + +template +using boolean_function_t = + decltype(std::declval().boolean(std::declval())); + +template +using number_integer_function_t = + decltype(std::declval().number_integer(std::declval())); + +template +using number_unsigned_function_t = + decltype(std::declval().number_unsigned(std::declval())); + +template +using number_float_function_t = decltype(std::declval().number_float( + std::declval(), std::declval())); + +template +using string_function_t = + decltype(std::declval().string(std::declval())); + +template +using binary_function_t = + decltype(std::declval().binary(std::declval())); + +template +using start_object_function_t = + decltype(std::declval().start_object(std::declval())); + +template +using key_function_t = + decltype(std::declval().key(std::declval())); + +template +using end_object_function_t = decltype(std::declval().end_object()); + +template +using start_array_function_t = + decltype(std::declval().start_array(std::declval())); + +template +using end_array_function_t = decltype(std::declval().end_array()); + +template +using parse_error_function_t = decltype(std::declval().parse_error( + std::declval(), std::declval(), + std::declval())); + +template +struct is_sax +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static constexpr bool value = + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value; +}; + +template +struct is_sax_static_asserts +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static_assert(is_detected_exact::value, + "Missing/invalid function: bool null()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_integer(number_integer_t)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_unsigned(number_unsigned_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool number_float(number_float_t, const string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool string(string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool binary(binary_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_object(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool key(string_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_object()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_array(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_array()"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool parse_error(std::size_t, const " + "std::string&, const exception&)"); +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/// how to treat CBOR tags +enum class cbor_tag_handler_t +{ + error, ///< throw a parse_error exception in case of a tag + ignore, ///< ignore tags + store ///< store tags as binary type +}; + +/*! +@brief determine system byte order + +@return true if and only if system's byte order is little endian + +@note from https://stackoverflow.com/a/1001328/266378 +*/ +static inline bool little_endianness(int num = 1) noexcept +{ + return *reinterpret_cast(&num) == 1; +} + + +/////////////////// +// binary reader // +/////////////////// + +/*! +@brief deserialization of CBOR, MessagePack, and UBJSON values +*/ +template> +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using json_sax_t = SAX; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(InputAdapterType&& adapter) noexcept : ia(std::move(adapter)) + { + (void)detail::is_sax_static_asserts {}; + } + + // make class move-only + binary_reader(const binary_reader&) = delete; + binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + binary_reader& operator=(const binary_reader&) = delete; + binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~binary_reader() = default; + + /*! + @param[in] format the binary format to parse + @param[in] sax_ a SAX event processor + @param[in] strict whether to expect the input to be consumed completed + @param[in] tag_handler how to treat CBOR tags + + @return whether parsing was successful + */ + JSON_HEDLEY_NON_NULL(3) + bool sax_parse(const input_format_t format, + json_sax_t* sax_, + const bool strict = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + sax = sax_; + bool result = false; + + switch (format) + { + case input_format_t::bson: + result = parse_bson_internal(); + break; + + case input_format_t::cbor: + result = parse_cbor_internal(true, tag_handler); + break; + + case input_format_t::msgpack: + result = parse_msgpack_internal(); + break; + + case input_format_t::ubjson: + result = parse_ubjson_internal(); + break; + + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + + // strict mode: next byte must be EOF + if (result && strict) + { + if (format == input_format_t::ubjson) + { + get_ignore_noop(); + } + else + { + get(); + } + + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) + { + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType())); + } + } + + return result; + } + + private: + ////////// + // BSON // + ////////// + + /*! + @brief Reads in a BSON-object and passes it to the SAX-parser. + @return whether a valid BSON-value was passed to the SAX parser + */ + bool parse_bson_internal() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false))) + { + return false; + } + + return sax->end_object(); + } + + /*! + @brief Parses a C-style string from the BSON input. + @param[in,out] result A reference to the string variable where the read + string is to be stored. + @return `true` if the \x00-byte indicating the end of the string was + encountered before the EOF; false` indicates an unexpected EOF. + */ + bool get_bson_cstr(string_t& result) + { + auto out = std::back_inserter(result); + while (true) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring"))) + { + return false; + } + if (current == 0x00) + { + return true; + } + *out++ = static_cast(current); + } + } + + /*! + @brief Parses a zero-terminated string of length @a len from the BSON + input. + @param[in] len The length (including the zero-byte at the end) of the + string to be read. + @param[in,out] result A reference to the string variable where the read + string is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 1 + @return `true` if the string was successfully parsed + */ + template + bool get_bson_string(const NumberType len, string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 1)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType())); + } + + return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); + } + + /*! + @brief Parses a byte array input of length @a len from the BSON input. + @param[in] len The length of the byte array to be read. + @param[in,out] result A reference to the binary variable where the read + array is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 0 + @return `true` if the byte array was successfully parsed + */ + template + bool get_bson_binary(const NumberType len, binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 0)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType())); + } + + // All BSON binary values have a subtype + std::uint8_t subtype{}; + get_number(input_format_t::bson, subtype); + result.set_subtype(subtype); + + return get_binary(input_format_t::bson, len, result); + } + + /*! + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_internal(const char_int_type element_type, + const std::size_t element_type_parse_position) + { + switch (element_type) + { + case 0x01: // double + { + double number{}; + return get_number(input_format_t::bson, number) && sax->number_float(static_cast(number), ""); + } + + case 0x02: // string + { + std::int32_t len{}; + string_t value; + return get_number(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value); + } + + case 0x03: // object + { + return parse_bson_internal(); + } + + case 0x04: // array + { + return parse_bson_array(); + } + + case 0x05: // binary + { + std::int32_t len{}; + binary_t value; + return get_number(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value); + } + + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } + + case 0x0A: // null + { + return sax->null(); + } + + case 0x10: // int32 + { + std::int32_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + case 0x12: // int64 + { + std::int64_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array cr{{}}; + static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType())); + } + } + } + + /*! + @brief Read a BSON element list (as specified in the BSON-spec) + + The same binary layout is used for objects and arrays, hence it must be + indicated with the argument @a is_array which one is expected + (true --> array, false --> object). + + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; + + while (auto element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } + + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key))) + { + return false; + } + + if (!is_array && !sax->key(key)) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } + + // get_bson_cstr only appends + key.clear(); + } + + return true; + } + + /*! + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true) or whether the last read character should + be considered instead (false) + @param[in] tag_handler how CBOR tags should be treated + + @return whether a valid CBOR value was passed to the SAX parser + */ + bool parse_cbor_internal(const bool get_char, + const cbor_tag_handler_t tag_handler) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::cbor, "value"); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return sax->number_unsigned(static_cast(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) + - static_cast(number)); + } + + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: // Binary data (one-byte uint8_t for n follows) + case 0x59: // Binary data (two-byte uint16_t for n follow) + case 0x5A: // Binary data (four-byte uint32_t for n follow) + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + case 0x5F: // Binary data (indefinite length) + { + binary_t b; + return get_cbor_binary(b) && sax->binary(b); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) && sax->string(s); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(detail::conditional_static_cast(len), tag_handler); + } + + case 0x9F: // array (indefinite length) + return get_cbor_array(static_cast(-1), tag_handler); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0xB8: // map (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(detail::conditional_static_cast(len), tag_handler); + } + + case 0xBF: // map (indefinite length) + return get_cbor_object(static_cast(-1), tag_handler); + + case 0xC6: // tagged item + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD8: // tagged item (1 bytes follow) + case 0xD9: // tagged item (2 bytes follow) + case 0xDA: // tagged item (4 bytes follow) + case 0xDB: // tagged item (8 bytes follow) + { + switch (tag_handler) + { + case cbor_tag_handler_t::error: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + + case cbor_tag_handler_t::ignore: + { + // ignore binary subtype + switch (current) + { + case 0xD8: + { + std::uint8_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xD9: + { + std::uint16_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDA: + { + std::uint32_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDB: + { + std::uint64_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + default: + break; + } + return parse_cbor_internal(true, tag_handler); + } + + case cbor_tag_handler_t::store: + { + binary_t b; + // use binary subtype and store in binary container + switch (current) + { + case 0xD8: + { + std::uint8_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xD9: + { + std::uint16_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xDA: + { + std::uint32_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xDB: + { + std::uint64_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + default: + return parse_cbor_internal(true, tag_handler); + } + get(); + return get_cbor_binary(b) && sax->binary(b); + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + case 0xF4: // false + return sax->boolean(false); + + case 0xF5: // true + return sax->boolean(true); + + case 0xF6: // null + return sax->null(); + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + float number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + double number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + default: // anything else (0xFF is handled inside the other types) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (!get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + } + + /*! + @brief reads a CBOR byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into the byte array. + Additionally, CBOR's byte arrays with indefinite lengths are supported. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_cbor_binary(binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary"))) + { + return false; + } + + switch (current) + { + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + { + return get_binary(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x58: // Binary data (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x59: // Binary data (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5A: // Binary data (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5F: // Binary data (indefinite length) + { + while (get() != 0xFF) + { + binary_t chunk; + if (!get_cbor_binary(chunk)) + { + return false; + } + result.insert(result.end(), chunk.begin(), chunk.end()); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType())); + } + } + } + + /*! + @param[in] len the length of the array or static_cast(-1) for an + array of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + if (len != static_cast(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or static_cast(-1) for an + object of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + if (len != 0) + { + string_t key; + if (len != static_cast(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + + /*! + @return whether a valid MessagePack value was passed to the SAX parser + */ + bool parse_msgpack_internal() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return sax->number_unsigned(static_cast(current)); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + return get_msgpack_object(static_cast(static_cast(current) & 0x0Fu)); + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + return get_msgpack_array(static_cast(static_cast(current) & 0x0Fu)); + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) && sax->string(s); + } + + case 0xC0: // nil + return sax->null(); + + case 0xC2: // false + return sax->boolean(false); + + case 0xC3: // true + return sax->boolean(true); + + case 0xC4: // bin 8 + case 0xC5: // bin 16 + case 0xC6: // bin 32 + case 0xC7: // ext 8 + case 0xC8: // ext 16 + case 0xC9: // ext 32 + case 0xD4: // fixext 1 + case 0xD5: // fixext 2 + case 0xD6: // fixext 4 + case 0xD7: // fixext 8 + case 0xD8: // fixext 16 + { + binary_t b; + return get_msgpack_binary(b) && sax->binary(b); + } + + case 0xCA: // float 32 + { + float number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCB: // float 64 + { + double number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCC: // uint 8 + { + std::uint8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCD: // uint 16 + { + std::uint16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCE: // uint 32 + { + std::uint32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCF: // uint 64 + { + std::uint64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xD0: // int 8 + { + std::int8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD1: // int 16 + { + std::int16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD2: // int 32 + { + std::int32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD3: // int 64 + { + std::int64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xDC: // array 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDD: // array 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDE: // map 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + case 0xDF: // map 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + // negative fixint + case 0xE0: + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return sax->number_integer(static_cast(current)); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_msgpack_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); + } + + case 0xD9: // str 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDA: // str 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDB: // str 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + } + + /*! + @brief reads a MessagePack byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into a byte array. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_msgpack_binary(binary_t& result) + { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) + { + result.set_subtype(static_cast(subtype)); + return true; + }; + + switch (current) + { + case 0xC4: // bin 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC5: // bin 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC6: // bin 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC7: // ext 8 + { + std::uint8_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC8: // ext 16 + { + std::uint16_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC9: // ext 32 + { + std::uint32_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xD4: // fixext 1 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 1, result) && + assign_and_return_true(subtype); + } + + case 0xD5: // fixext 2 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 2, result) && + assign_and_return_true(subtype); + } + + case 0xD6: // fixext 4 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 4, result) && + assign_and_return_true(subtype); + } + + case 0xD7: // fixext 8 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 8, result) && + assign_and_return_true(subtype); + } + + case 0xD8: // fixext 16 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 16, result) && + assign_and_return_true(subtype); + } + + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + /*! + @param[in] len the length of the array + @return whether array creation completed + */ + bool get_msgpack_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object + @return whether object creation completed + */ + bool get_msgpack_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + key.clear(); + } + + return sax->end_object(); + } + + //////////// + // UBJSON // + //////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser + */ + bool parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + + /*! + @brief reads a UBJSON string + + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. + + @param[out] result created string + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether string creation completed + */ + bool get_ubjson_string(string_t& result, const bool get_char = true) + { + if (get_char) + { + get(); // TODO(niels): may we ignore N here? + } + + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + + switch (current) + { + case 'U': + { + std::uint8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'i': + { + std::int8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'I': + { + std::int16_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'l': + { + std::int32_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'L': + { + std::int64_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + default: + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + + /*! + @param[out] result determined size + @return whether size determination completed + */ + bool get_ubjson_size_value(std::size_t& result) + { + switch (get_ignore_noop()) + { + case 'U': + { + std::uint8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'i': + { + std::int8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char + return true; + } + + case 'I': + { + std::int16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'l': + { + std::int32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'L': + { + std::int64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType())); + } + } + } + + /*! + @brief determine the type and size for a container + + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. + + @param[out] result pair of the size and the type + + @return whether pair creation completed + */ + bool get_ubjson_size_type(std::pair& result) + { + result.first = string_t::npos; // size + result.second = 0; // type + + get_ignore_noop(); + + if (current == '$') + { + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type"))) + { + return false; + } + + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType())); + } + + return get_ubjson_size_value(result.first); + } + + if (current == '#') + { + return get_ubjson_size_value(result.first); + } + + return true; + } + + /*! + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const char_int_type prefix) + { + switch (prefix) + { + case std::char_traits::eof(): // EOF + return unexpect_eof(input_format_t::ubjson, "value"); + + case 'T': // true + return sax->boolean(true); + case 'F': // false + return sax->boolean(false); + + case 'Z': // null + return sax->null(); + + case 'U': + { + std::uint8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number); + } + + case 'i': + { + std::int8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'I': + { + std::int16_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'l': + { + std::int32_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'L': + { + std::int64_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'd': + { + float number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'D': + { + double number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'H': + { + return get_ubjson_high_precision_number(); + } + + case 'C': // char + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char"))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(current > 127)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType())); + } + string_t s(1, static_cast(current)); + return sax->string(s); + } + + case 'S': // string + { + string_t s; + return get_ubjson_string(s) && sax->string(s); + } + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @return whether array creation completed + */ + bool get_ubjson_array() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + { + return false; + } + + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } + } + + return sax->end_array(); + } + + /*! + @return whether object creation completed + */ + bool get_ubjson_object() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + string_t key; + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + { + return false; + } + + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } + } + + return sax->end_object(); + } + + // Note, no reader for UBJSON binary types is implemented because they do + // not exist + + bool get_ubjson_high_precision_number() + { + // get size of following number string + std::size_t size{}; + auto res = get_ubjson_size_value(size); + if (JSON_HEDLEY_UNLIKELY(!res)) + { + return res; + } + + // get number string + std::vector number_vector; + for (std::size_t i = 0; i < size; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number"))) + { + return false; + } + number_vector.push_back(static_cast(current)); + } + + // parse number string + using ia_type = decltype(detail::input_adapter(number_vector)); + auto number_lexer = detail::lexer(detail::input_adapter(number_vector), false); + const auto result_number = number_lexer.scan(); + const auto number_string = number_lexer.get_token_string(); + const auto result_remainder = number_lexer.scan(); + + using token_type = typename detail::lexer_base::token_type; + + if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) + { + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + } + + switch (result_number) + { + case token_type::value_integer: + return sax->number_integer(number_lexer.get_number_integer()); + case token_type::value_unsigned: + return sax->number_unsigned(number_lexer.get_number_unsigned()); + case token_type::value_float: + return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); + case token_type::uninitialized: + case token_type::literal_true: + case token_type::literal_false: + case token_type::literal_null: + case token_type::value_string: + case token_type::begin_array: + case token_type::begin_object: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::parse_error: + case token_type::end_of_input: + case token_type::literal_or_value: + default: + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + } + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits::eof()` in that case. + + @return character read from the input + */ + char_int_type get() + { + ++chars_read; + return current = ia.get_character(); + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + char_int_type get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianness, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. + */ + template + bool get_number(const input_format_t format, NumberType& result) + { + // step 1: read input into array with system's byte order + std::array vec{}; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + + /*! + @brief create a string by reading characters from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes + + @return whether string creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @brief create a byte array by reading bytes from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of bytes to read + @param[out] result byte array created by reading @a len bytes + + @return whether byte array creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of memory. + */ + template + bool get_binary(const input_format_t format, + const NumberType len, + binary_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) + @return whether the last read character is not EOF + */ + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const + { + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) + { + return sax->parse_error(chars_read, "", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType())); + } + return true; + } + + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array cr{{}}; + static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return std::string{cr.data()}; + } + + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; + + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; + + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; + + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; + + case input_format_t::bson: + error_msg += "BSON"; + break; + + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + + return error_msg + " " + context + ": " + detail; + } + + private: + /// input adapter + InputAdapterType ia; + + /// the current character + char_int_type current = std::char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); + + /// the SAX parser + json_sax_t* sax = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +enum class parse_event_t : std::uint8_t +{ + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value +}; + +template +using parser_callback_t = + std::function; + +/*! +@brief syntax analysis + +This class implements a recursive descent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + /// a parser reading from an input adapter + explicit parser(InputAdapterType&& adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) + { + // read first token + get_token(); + } + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + if (callback) + { + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser sdp(result, allow_exceptions); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } + + result.assert_invariant(); + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + json_sax_acceptor sax_acceptor; + return sax_parse(&sax_acceptor, strict); + } + + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts {}; + const bool result = sax_parse_internal(sax); + + // strict mode: next byte must be EOF + if (result && strict && (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + return result; + } + + private: + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) + { + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + + while (true) + { + if (!skip_to_state_evaluation) + { + // invariant: get_token() was called before each iteration + switch (last_token) + { + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + } + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType())); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(!sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType())); + } + + case token_type::uninitialized: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::end_of_input: + case token_type::literal_or_value: + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType())); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + continue; + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType())); + } + + // states.back() is false -> object + + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + } + + // parse values + get_token(); + continue; + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType())); + } + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (!context.empty()) + { + error_msg += "while parsing " + context + " "; + } + + error_msg += "- "; + + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + } + + return error_msg; + } + + private: + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +// #include + + +#include // ptrdiff_t +#include // numeric_limits + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + JSON_PRIVATE_UNLESS_TESTED: + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type) + { + auto result = *this; + ++m_it; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type) + { + auto result = *this; + --m_it; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template class iteration_proxy; +template class iteration_proxy_value; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) +{ + /// the iterator with BasicJsonType of different const-ness + using other_iter_impl = iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + /// allow basic_json to access private members + friend other_iter_impl; + friend BasicJsonType; + friend iteration_proxy; + friend iteration_proxy_value; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + iter_impl() = default; + ~iter_impl() = default; + iter_impl(iter_impl&&) noexcept = default; + iter_impl& operator=(iter_impl&&) noexcept = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + if (&other != this) + { + m_object = other.m_object; + m_it = other.m_it; + } + return *this; + } + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl::type>& other) noexcept // NOLINT(cert-oop54-cpp) + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) // NOLINT(readability-const-return-type) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) // NOLINT(readability-const-return-type) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator==(const IterImpl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator!=(const IterImpl& other) const + { + return !operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object)); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return !other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return !operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return !operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object)); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const typename object_t::key_type& key() const + { + JSON_ASSERT(m_object != nullptr); + + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object)); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + JSON_PRIVATE_UNLESS_TESTED: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // ptrdiff_t +#include // reverse_iterator +#include // declval + +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type) + { + return static_cast(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type) + { + return static_cast(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // all_of +#include // isdigit +#include // max +#include // accumulate +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ + +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: + /// @brief create JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /// @brief return a string representation of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/to_string/ + std::string to_string() const + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }); + } + + /// @brief return a string representation of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/ + operator std::string() const + { + return to_string(); + } + + /// @brief append another JSON pointer at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } + + /// @brief append an unescaped reference token at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(std::string token) + { + push_back(std::move(token)); + return *this; + } + + /// @brief append an array index at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(std::size_t array_idx) + { + return *this /= std::to_string(array_idx); + } + + /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) + { + return json_pointer(lhs) /= rhs; + } + + /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, std::string token) // NOLINT(performance-unnecessary-value-param) + { + return json_pointer(lhs) /= std::move(token); + } + + /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx) + { + return json_pointer(lhs) /= array_idx; + } + + /// @brief returns the parent of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/ + json_pointer parent_pointer() const + { + if (empty()) + { + return *this; + } + + json_pointer res = *this; + res.pop_back(); + return res; + } + + /// @brief remove last reference token + /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/ + void pop_back() + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + reference_tokens.pop_back(); + } + + /// @brief return last reference token + /// @sa https://json.nlohmann.me/api/json_pointer/back/ + const std::string& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + return reference_tokens.back(); + } + + /// @brief append an unescaped token at the end of the reference pointer + /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ + void push_back(const std::string& token) + { + reference_tokens.push_back(token); + } + + /// @brief append an unescaped token at the end of the reference pointer + /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ + void push_back(std::string&& token) + { + reference_tokens.push_back(std::move(token)); + } + + /// @brief return whether pointer points to the root document + /// @sa https://json.nlohmann.me/api/json_pointer/empty/ + bool empty() const noexcept + { + return reference_tokens.empty(); + } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit + @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type + */ + static typename BasicJsonType::size_type array_index(const std::string& s) + { + using size_type = typename BasicJsonType::size_type; + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType())); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType())); + } + + std::size_t processed_chars = 0; + unsigned long long res = 0; // NOLINT(runtime/int) + JSON_TRY + { + res = std::stoull(s, &processed_chars); + } + JSON_CATCH(std::out_of_range&) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + } + + // check if the string was completely read + if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + } + + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) + { + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE + } + + return static_cast(res); + } + + JSON_PRIVATE_UNLESS_TESTED: + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + private: + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + auto* result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->type()) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + result = &result->operator[](array_index(reference_token)); + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j)); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) + { + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums || reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](array_index(reference_token)); + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range", *ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr)); + } + + // use unchecked array access + ptr = &ptr->operator[](array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range", *ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + if (!ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) + { + // invalid char + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) + { + if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) + { + // first char should be between '1' and '9' + return false; + } + for (std::size_t i = 1; i < reference_token.size(); i++) + { + if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) + { + // other char should be between '0' and '9' + return false; + } + } + } + + const auto idx = array_index(reference_token); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // we do not expect primitive values if there is still a + // reference token to process + return false; + } + } + } + + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType())); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + JSON_ASSERT(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || + (reference_token[pos + 1] != '0' && + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType())); + } + } + + // finally, store the reference token + detail::unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + detail::escape(element.first), element.second, result); + } + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(!value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value)); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second)); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + /*! + @brief compares two JSON pointers for equality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + /*! + @brief compares two JSON pointers for inequality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return !(lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens; +}; +} // namespace nlohmann + +// #include + + +#include +#include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)) + {} + + json_ref(const value_type& value) + : value_ref(&value) + {} + + json_ref(std::initializer_list init) + : owned_value(init) + {} + + template < + class... Args, + enable_if_t::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward(args)...) + {} + + // class should be movable only + json_ref(json_ref&&) noexcept = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; + + value_type moved_or_copied() const + { + if (value_ref == nullptr) + { + return std::move(owned_value); + } + return *value_ref; + } + + value_type const& operator*() const + { + return value_ref ? *value_ref : owned_value; + } + + value_type const* operator->() const + { + return &** this; + } + + private: + mutable value_type owned_value = nullptr; + value_type const* value_ref = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + +// #include + + +#include // reverse +#include // array +#include // isnan, isinf +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // memcpy +#include // numeric_limits +#include // string +#include // move + +// #include + +// #include + +// #include + + +#include // copy +#include // size_t +#include // back_inserter +#include // shared_ptr, make_shared +#include // basic_string +#include // vector + +#ifndef JSON_NO_IO + #include // streamsize + #include // basic_ostream +#endif // JSON_NO_IO + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// abstract output adapter interface +template struct output_adapter_protocol +{ + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, std::size_t length) = 0; + virtual ~output_adapter_protocol() = default; + + output_adapter_protocol() = default; + output_adapter_protocol(const output_adapter_protocol&) = default; + output_adapter_protocol(output_adapter_protocol&&) noexcept = default; + output_adapter_protocol& operator=(const output_adapter_protocol&) = default; + output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default; +}; + +/// a type to simplify interfaces +template +using output_adapter_t = std::shared_ptr>; + +/// output adapter for byte vectors +template> +class output_vector_adapter : public output_adapter_protocol +{ + public: + explicit output_vector_adapter(std::vector& vec) noexcept + : v(vec) + {} + + void write_character(CharType c) override + { + v.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + std::copy(s, s + length, std::back_inserter(v)); + } + + private: + std::vector& v; +}; + +#ifndef JSON_NO_IO +/// output adapter for output streams +template +class output_stream_adapter : public output_adapter_protocol +{ + public: + explicit output_stream_adapter(std::basic_ostream& s) noexcept + : stream(s) + {} + + void write_character(CharType c) override + { + stream.put(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + stream.write(s, static_cast(length)); + } + + private: + std::basic_ostream& stream; +}; +#endif // JSON_NO_IO + +/// output adapter for basic_string +template> +class output_string_adapter : public output_adapter_protocol +{ + public: + explicit output_string_adapter(StringType& s) noexcept + : str(s) + {} + + void write_character(CharType c) override + { + str.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + str.append(s, length); + } + + private: + StringType& str; +}; + +template> +class output_adapter +{ + public: + template> + output_adapter(std::vector& vec) + : oa(std::make_shared>(vec)) {} + +#ifndef JSON_NO_IO + output_adapter(std::basic_ostream& s) + : oa(std::make_shared>(s)) {} +#endif // JSON_NO_IO + + output_adapter(StringType& s) + : oa(std::make_shared>(s)) {} + + operator output_adapter_t() + { + return oa; + } + + private: + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary writer // +/////////////////// + +/*! +@brief serialization to CBOR and MessagePack values +*/ +template +class binary_writer +{ + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; + + public: + /*! + @brief create a binary writer + + @param[in] adapter output adapter to write to + */ + explicit binary_writer(output_adapter_t adapter) : oa(std::move(adapter)) + { + JSON_ASSERT(oa); + } + + /*! + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + void write_bson(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + { + write_bson_object(*j.m_value.object); + break; + } + + case value_t::null: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j)); + } + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_cbor(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + oa->write_character(to_char_type(0xF6)); + break; + } + + case value_t::boolean: + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xF5) + : to_char_type(0xF4)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_integer)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + write_number(static_cast(0x20 + positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x38)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x39)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x3A)); + write_number(static_cast(positive_number)); + } + else + { + oa->write_character(to_char_type(0x3B)); + write_number(static_cast(positive_number)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) + { + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_unsigned)); + } + break; + } + + case value_t::number_float: + { + if (std::isnan(j.m_value.number_float)) + { + // NaN is 0xf97e00 in CBOR + oa->write_character(to_char_type(0xF9)); + oa->write_character(to_char_type(0x7E)); + oa->write_character(to_char_type(0x00)); + } + else if (std::isinf(j.m_value.number_float)) + { + // Infinity is 0xf97c00, -Infinity is 0xf9fc00 + oa->write_character(to_char_type(0xf9)); + oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(to_char_type(0x00)); + } + else + { + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); + } + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + write_number(static_cast(0x60 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x78)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x79)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + write_number(static_cast(0x80 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x98)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x99)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_cbor(el); + } + break; + } + + case value_t::binary: + { + if (j.m_value.binary->has_subtype()) + { + if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xd8)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xd9)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xda)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xdb)); + write_number(static_cast(j.m_value.binary->subtype())); + } + } + + // step 1: write control byte and the binary array size + const auto N = j.m_value.binary->size(); + if (N <= 0x17) + { + write_number(static_cast(0x40 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x58)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x59)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + write_number(static_cast(0xA0 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB8)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBA)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBB)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_cbor(el.first); + write_cbor(el.second); + } + break; + } + + case value_t::discarded: + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_msgpack(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: // nil + { + oa->write_character(to_char_type(0xC0)); + break; + } + + case value_t::boolean: // true and false + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xC3) + : to_char_type(0xC2)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we used + // the code from the value_t::number_unsigned case here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 8 + oa->write_character(to_char_type(0xD0)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 16 + oa->write_character(to_char_type(0xD1)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 32 + oa->write_character(to_char_type(0xD2)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 64 + oa->write_character(to_char_type(0xD3)); + write_number(static_cast(j.m_value.number_integer)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + break; + } + + case value_t::number_float: + { + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + write_number(static_cast(0xA0 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 8 + oa->write_character(to_char_type(0xD9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 16 + oa->write_character(to_char_type(0xDA)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 32 + oa->write_character(to_char_type(0xDB)); + write_number(static_cast(N)); + } + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 15) + { + // fixarray + write_number(static_cast(0x90 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 16 + oa->write_character(to_char_type(0xDC)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 32 + oa->write_character(to_char_type(0xDD)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_msgpack(el); + } + break; + } + + case value_t::binary: + { + // step 0: determine if the binary type has a set subtype to + // determine whether or not to use the ext or fixext types + const bool use_ext = j.m_value.binary->has_subtype(); + + // step 1: write control byte and the byte string length + const auto N = j.m_value.binary->size(); + if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type{}; + bool fixed = true; + if (use_ext) + { + switch (N) + { + case 1: + output_type = 0xD4; // fixext 1 + break; + case 2: + output_type = 0xD5; // fixext 2 + break; + case 4: + output_type = 0xD6; // fixext 4 + break; + case 8: + output_type = 0xD7; // fixext 8 + break; + case 16: + output_type = 0xD8; // fixext 16 + break; + default: + output_type = 0xC7; // ext 8 + fixed = false; + break; + } + + } + else + { + output_type = 0xC4; // bin 8 + fixed = false; + } + + oa->write_character(to_char_type(output_type)); + if (!fixed) + { + write_number(static_cast(N)); + } + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + + // step 1.5: if this is an ext type, write the subtype + if (use_ext) + { + write_number(static_cast(j.m_value.binary->subtype())); + } + + // step 2: write the byte string + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + write_number(static_cast(0x80 | (N & 0xF))); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 16 + oa->write_character(to_char_type(0xDE)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 32 + oa->write_character(to_char_type(0xDF)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_msgpack(el.first); + write_msgpack(el.second); + } + break; + } + + case value_t::discarded: + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + @param[in] use_count whether to use '#' prefixes (optimized format) + @param[in] use_type whether to use '$' prefixes (optimized format) + @param[in] add_prefix whether prefixes need to be used for this value + */ + void write_ubjson(const BasicJsonType& j, const bool use_count, + const bool use_type, const bool add_prefix = true) + { + switch (j.type()) + { + case value_t::null: + { + if (add_prefix) + { + oa->write_character(to_char_type('Z')); + } + break; + } + + case value_t::boolean: + { + if (add_prefix) + { + oa->write_character(j.m_value.boolean + ? to_char_type('T') + : to_char_type('F')); + } + break; + } + + case value_t::number_integer: + { + write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); + break; + } + + case value_t::number_unsigned: + { + write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); + break; + } + + case value_t::number_float: + { + write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); + break; + } + + case value_t::string: + { + if (add_prefix) + { + oa->write_character(to_char_type('S')); + } + write_number_with_ubjson_prefix(j.m_value.string->size(), true); + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + bool prefix_required = true; + if (use_type && !j.m_value.array->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin() + 1, j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.array->size(), true); + } + + for (const auto& el : *j.m_value.array) + { + write_ubjson(el, use_count, use_type, prefix_required); + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::binary: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + if (use_type && !j.m_value.binary->empty()) + { + JSON_ASSERT(use_count); + oa->write_character(to_char_type('$')); + oa->write_character('U'); + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true); + } + + if (use_type) + { + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + j.m_value.binary->size()); + } + else + { + for (size_t i = 0; i < j.m_value.binary->size(); ++i) + { + oa->write_character(to_char_type('U')); + oa->write_character(j.m_value.binary->data()[i]); + } + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::object: + { + if (add_prefix) + { + oa->write_character(to_char_type('{')); + } + + bool prefix_required = true; + if (use_type && !j.m_value.object->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin(), j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.object->size(), true); + } + + for (const auto& el : *j.m_value.object) + { + write_number_with_ubjson_prefix(el.first.size(), true); + oa->write_characters( + reinterpret_cast(el.first.c_str()), + el.first.size()); + write_ubjson(el.second, use_count, use_type, prefix_required); + } + + if (!use_count) + { + oa->write_character(to_char_type('}')); + } + + break; + } + + case value_t::discarded: + default: + break; + } + } + + private: + ////////// + // BSON // + ////////// + + /*! + @return The size of a BSON document entry header, including the id marker + and the entry name size (and its null-terminator). + */ + static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j) + { + const auto it = name.find(static_cast(0)); + if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) + { + JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j)); + static_cast(j); + } + + return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; + } + + /*! + @brief Writes the given @a element_type and @a name to the output adapter + */ + void write_bson_entry_header(const string_t& name, + const std::uint8_t element_type) + { + oa->write_character(to_char_type(element_type)); // boolean + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + } + + /*! + @brief Writes a BSON element with key @a name and boolean value @a value + */ + void write_bson_boolean(const string_t& name, + const bool value) + { + write_bson_entry_header(name, 0x08); + oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and double value @a value + */ + void write_bson_double(const string_t& name, + const double value) + { + write_bson_entry_header(name, 0x01); + write_number(value); + } + + /*! + @return The size of the BSON-encoded string in @a value + */ + static std::size_t calc_bson_string_size(const string_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and string value @a value + */ + void write_bson_string(const string_t& name, + const string_t& value) + { + write_bson_entry_header(name, 0x02); + + write_number(static_cast(value.size() + 1ul)); + oa->write_characters( + reinterpret_cast(value.c_str()), + value.size() + 1); + } + + /*! + @brief Writes a BSON element with key @a name and null value + */ + void write_bson_null(const string_t& name) + { + write_bson_entry_header(name, 0x0A); + } + + /*! + @return The size of the BSON-encoded integer @a value + */ + static std::size_t calc_bson_integer_size(const std::int64_t value) + { + return (std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)() + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and integer @a value + */ + void write_bson_integer(const string_t& name, + const std::int64_t value) + { + if ((std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)()) + { + write_bson_entry_header(name, 0x10); // int32 + write_number(static_cast(value)); + } + else + { + write_bson_entry_header(name, 0x12); // int64 + write_number(static_cast(value)); + } + } + + /*! + @return The size of the BSON-encoded unsigned integer in @a j + */ + static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept + { + return (value <= static_cast((std::numeric_limits::max)())) + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and unsigned @a value + */ + void write_bson_unsigned(const string_t& name, + const BasicJsonType& j) + { + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x10 /* int32 */); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x12 /* int64 */); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j)); + } + } + + /*! + @brief Writes a BSON element with key @a name and object @a value + */ + void write_bson_object_entry(const string_t& name, + const typename BasicJsonType::object_t& value) + { + write_bson_entry_header(name, 0x03); // object + write_bson_object(value); + } + + /*! + @return The size of the BSON-encoded array @a value + */ + static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) + { + std::size_t array_index = 0ul; + + const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el) + { + return result + calc_bson_element_size(std::to_string(array_index++), el); + }); + + return sizeof(std::int32_t) + embedded_document_size + 1ul; + } + + /*! + @return The size of the BSON-encoded binary array @a value + */ + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and array @a value + */ + void write_bson_array(const string_t& name, + const typename BasicJsonType::array_t& value) + { + write_bson_entry_header(name, 0x04); // array + write_number(static_cast(calc_bson_array_size(value))); + + std::size_t array_index = 0ul; + + for (const auto& el : value) + { + write_bson_element(std::to_string(array_index++), el); + } + + oa->write_character(to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and binary value @a value + */ + void write_bson_binary(const string_t& name, + const binary_t& value) + { + write_bson_entry_header(name, 0x05); + + write_number(static_cast(value.size())); + write_number(value.has_subtype() ? static_cast(value.subtype()) : static_cast(0x00)); + + oa->write_characters(reinterpret_cast(value.data()), value.size()); + } + + /*! + @brief Calculates the size necessary to serialize the JSON value @a j with its @a name + @return The calculated size for the BSON document entry for @a j with the given @a name. + */ + static std::size_t calc_bson_element_size(const string_t& name, + const BasicJsonType& j) + { + const auto header_size = calc_bson_entry_header_size(name, j); + switch (j.type()) + { + case value_t::object: + return header_size + calc_bson_object_size(*j.m_value.object); + + case value_t::array: + return header_size + calc_bson_array_size(*j.m_value.array); + + case value_t::binary: + return header_size + calc_bson_binary_size(*j.m_value.binary); + + case value_t::boolean: + return header_size + 1ul; + + case value_t::number_float: + return header_size + 8ul; + + case value_t::number_integer: + return header_size + calc_bson_integer_size(j.m_value.number_integer); + + case value_t::number_unsigned: + return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned); + + case value_t::string: + return header_size + calc_bson_string_size(*j.m_value.string); + + case value_t::null: + return header_size + 0ul; + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return 0ul; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Serializes the JSON value @a j to BSON and associates it with the + key @a name. + @param name The name to associate with the JSON entity @a j within the + current BSON document + */ + void write_bson_element(const string_t& name, + const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + return write_bson_object_entry(name, *j.m_value.object); + + case value_t::array: + return write_bson_array(name, *j.m_value.array); + + case value_t::binary: + return write_bson_binary(name, *j.m_value.binary); + + case value_t::boolean: + return write_bson_boolean(name, j.m_value.boolean); + + case value_t::number_float: + return write_bson_double(name, j.m_value.number_float); + + case value_t::number_integer: + return write_bson_integer(name, j.m_value.number_integer); + + case value_t::number_unsigned: + return write_bson_unsigned(name, j); + + case value_t::string: + return write_bson_string(name, *j.m_value.string); + + case value_t::null: + return write_bson_null(name); + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Calculates the size of the BSON serialization of the given + JSON-object @a j. + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) + { + std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast(0), + [](size_t result, const typename BasicJsonType::object_t::value_type & el) + { + return result += calc_bson_element_size(el.first, el.second); + }); + + return sizeof(std::int32_t) + document_size + 1ul; + } + + /*! + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + void write_bson_object(const typename BasicJsonType::object_t& value) + { + write_number(static_cast(calc_bson_object_size(value))); + + for (const auto& el : value) + { + write_bson_element(el.first, el.second); + } + + oa->write_character(to_char_type(0x00)); + } + + ////////// + // CBOR // + ////////// + + static constexpr CharType get_cbor_float_prefix(float /*unused*/) + { + return to_char_type(0xFA); // Single-Precision Float + } + + static constexpr CharType get_cbor_float_prefix(double /*unused*/) + { + return to_char_type(0xFB); // Double-Precision Float + } + + ///////////// + // MsgPack // + ///////////// + + static constexpr CharType get_msgpack_float_prefix(float /*unused*/) + { + return to_char_type(0xCA); // float 32 + } + + static constexpr CharType get_msgpack_float_prefix(double /*unused*/) + { + return to_char_type(0xCB); // float 64 + } + + //////////// + // UBJSON // + //////////// + + // UBJSON: write number (floating point) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (add_prefix) + { + oa->write_character(get_ubjson_float_prefix(n)); + } + write_number(n); + } + + // UBJSON: write number (unsigned integer) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + } + + // UBJSON: write number (signed integer) + template < typename NumberType, typename std::enable_if < + std::is_signed::value&& + !std::is_floating_point::value, int >::type = 0 > + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + // LCOV_EXCL_START + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + // LCOV_EXCL_STOP + } + + /*! + @brief determine the type prefix of container values + */ + CharType ubjson_prefix(const BasicJsonType& j) const noexcept + { + switch (j.type()) + { + case value_t::null: + return 'Z'; + + case value_t::boolean: + return j.m_value.boolean ? 'T' : 'F'; + + case value_t::number_integer: + { + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'i'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'U'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'I'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'l'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'i'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'U'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'I'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'l'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_float: + return get_ubjson_float_prefix(j.m_value.number_float); + + case value_t::string: + return 'S'; + + case value_t::array: // fallthrough + case value_t::binary: + return '['; + + case value_t::object: + return '{'; + + case value_t::discarded: + default: // discarded values + return 'N'; + } + } + + static constexpr CharType get_ubjson_float_prefix(float /*unused*/) + { + return 'd'; // float 32 + } + + static constexpr CharType get_ubjson_float_prefix(double /*unused*/) + { + return 'D'; // float 64 + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /* + @brief write a number to output input + @param[in] n number of type @a NumberType + @tparam NumberType the type of the number + @tparam OutputIsLittleEndian Set to true if output data is + required to be little endian + + @note This function needs to respect the system's endianness, because bytes + in CBOR, MessagePack, and UBJSON are stored in network order (big + endian) and therefore need reordering on little endian systems. + */ + template + void write_number(const NumberType n) + { + // step 1: write number to array of length NumberType + std::array vec{}; + std::memcpy(vec.data(), &n, sizeof(NumberType)); + + // step 2: write array to output (with possible reordering) + if (is_little_endian != OutputIsLittleEndian) + { + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); + } + + oa->write_characters(vec.data(), sizeof(NumberType)); + } + + void write_compact_float(const number_float_t n, detail::input_format_t format) + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) && + static_cast(n) <= static_cast((std::numeric_limits::max)()) && + static_cast(static_cast(n)) == static_cast(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast(n)) + : get_msgpack_float_prefix(static_cast(n))); + write_number(static_cast(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + public: + // The following to_char_type functions are implement the conversion + // between uint8_t and CharType. In case CharType is not unsigned, + // such a conversion is required to allow values greater than 128. + // See for a discussion. + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_signed::value > * = nullptr > + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return *reinterpret_cast(&x); + } + + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_unsigned::value > * = nullptr > + static CharType to_char_type(std::uint8_t x) noexcept + { + static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); + static_assert(std::is_trivial::value, "CharType must be trivial"); + CharType result; + std::memcpy(&result, &x, sizeof(x)); + return result; + } + + template::value>* = nullptr> + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return x; + } + + template < typename InputCharType, typename C = CharType, + enable_if_t < + std::is_signed::value && + std::is_signed::value && + std::is_same::type>::value + > * = nullptr > + static constexpr CharType to_char_type(InputCharType x) noexcept + { + return x; + } + + private: + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); + + /// the output + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // reverse, remove, fill, find, none_of +#include // array +#include // localeconv, lconv +#include // labs, isfinite, isnan, signbit +#include // size_t, ptrdiff_t +#include // uint8_t +#include // snprintf +#include // numeric_limits +#include // string, char_traits +#include // setfill, setw +#include // is_same +#include // move + +// #include + + +#include // array +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove +#include // numeric_limits +#include // conditional + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + JSON_ASSERT(x.e == y.e); + JSON_ASSERT(x.f >= y.f); + + return {x.f - y.f, x.e}; + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; + + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; + + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; + + std::uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + return {h, x.e + y.e + 64}; + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + JSON_ASSERT(x.f != 0); + + while ((x.f >> 63u) == 0) + { + x.f <<= 1u; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + JSON_ASSERT(delta >= 0); + JSON_ASSERT(((x.f << delta) >> delta) == x.f); + + return {x.f << delta, target_exponent}; + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional::type; + + const auto bits = static_cast(reinterpret_bits(value)); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = F == 0 && E > 1; + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr std::array kCachedPowers = + { + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + JSON_ASSERT(e >= -1500); + JSON_ASSERT(e <= 1500); + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + JSON_ASSERT(index >= 0); + JSON_ASSERT(static_cast(index) < kCachedPowers.size()); + + const cached_power cached = kCachedPowers[static_cast(index)]; + JSON_ASSERT(kAlpha <= cached.e + e + 64); + JSON_ASSERT(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + if (n >= 100000) + { + pow10 = 100000; + return 6; + } + if (n >= 10000) + { + pow10 = 10000; + return 5; + } + if (n >= 1000) + { + pow10 = 1000; + return 4; + } + if (n >= 100) + { + pow10 = 100; + return 3; + } + if (n >= 10) + { + pow10 = 10; + return 2; + } + + pow10 = 1; + return 1; +} + +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) +{ + JSON_ASSERT(len >= 1); + JSON_ASSERT(dist <= delta); + JSON_ASSERT(rest <= delta); + JSON_ASSERT(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + && delta - rest >= ten_k + && (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) + { + JSON_ASSERT(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + JSON_ASSERT(M_plus.e >= kAlpha); + JSON_ASSERT(M_plus.e <= kGamma); + + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); + + auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + JSON_ASSERT(p1 > 0); + + std::uint32_t pow10{}; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + JSON_ASSERT(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + JSON_ASSERT(p2 <= (std::numeric_limits::max)() / 10); + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +JSON_HEDLEY_NON_NULL(1) +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + JSON_ASSERT(m_plus.e == m_minus.e); + JSON_ASSERT(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +JSON_HEDLEY_NON_NULL(1) +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes floats to doubles, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* append_exponent(char* buf, int e) +{ + JSON_ASSERT(e > -1000); + JSON_ASSERT(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + auto k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + JSON_ASSERT(min_exp < 0); + JSON_ASSERT(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n && n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n) - static_cast(k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (static_cast(n) + 2); + } + + if (0 < n && n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + JSON_ASSERT(k > n); + + std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); + buf[n] = '.'; + return buf + (static_cast(k) + 1U); + } + + if (min_exp < n && n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + static_cast(-n)), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2U + static_cast(-n) + static_cast(k)); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k) - 1); + buf[1] = '.'; + buf += 1 + static_cast(k); + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + JSON_ASSERT(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + JSON_ASSERT(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + JSON_ASSERT(last - first >= kMaxExp + 2); + JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// serialization // +/////////////////// + +/// how to treat decoding errors +enum class error_handler_t +{ + strict, ///< throw a type_error exception in case of invalid UTF-8 + replace, ///< replace invalid UTF-8 sequences with U+FFFD + ignore ///< ignore invalid UTF-8 sequences +}; + +template +class serializer +{ + using string_t = typename BasicJsonType::string_t; + using number_float_t = typename BasicJsonType::number_float_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; + static constexpr std::uint8_t UTF8_ACCEPT = 0; + static constexpr std::uint8_t UTF8_REJECT = 1; + + public: + /*! + @param[in] s output stream to serialize to + @param[in] ichar indentation character to use + @param[in] error_handler_ how to react on decoding errors + */ + serializer(output_adapter_t s, const char ichar, + error_handler_t error_handler_ = error_handler_t::strict) + : o(std::move(s)) + , loc(std::localeconv()) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) + , indent_char(ichar) + , indent_string(512, indent_char) + , error_handler(error_handler_) + {} + + // delete because of pointer members + serializer(const serializer&) = delete; + serializer& operator=(const serializer&) = delete; + serializer(serializer&&) = delete; + serializer& operator=(serializer&&) = delete; + ~serializer() = default; + + /*! + @brief internal implementation of the serialization function + + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. + + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + - binary values are serialized as objects containing the subtype and the + byte array + + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(const BasicJsonType& val, + const bool pretty_print, + const bool ensure_ascii, + const unsigned int indent_step, + const unsigned int current_indent = 0) + { + switch (val.m_type) + { + case value_t::object: + { + if (val.m_value.object->empty()) + { + o->write_characters("{}", 2); + return; + } + + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_character('{'); + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + + o->write_character('}'); + } + + return; + } + + case value_t::array: + { + if (val.m_value.array->empty()) + { + o->write_characters("[]", 2); + return; + } + + if (pretty_print) + { + o->write_characters("[\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + } + else + { + o->write_character('['); + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + dump(*i, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); + + o->write_character(']'); + } + + return; + } + + case value_t::string: + { + o->write_character('\"'); + dump_escaped(*val.m_value.string, ensure_ascii); + o->write_character('\"'); + return; + } + + case value_t::binary: + { + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_characters("{\"bytes\":[", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } + } + return; + } + + case value_t::boolean: + { + if (val.m_value.boolean) + { + o->write_characters("true", 4); + } + else + { + o->write_characters("false", 5); + } + return; + } + + case value_t::number_integer: + { + dump_integer(val.m_value.number_integer); + return; + } + + case value_t::number_unsigned: + { + dump_integer(val.m_value.number_unsigned); + return; + } + + case value_t::number_float: + { + dump_float(val.m_value.number_float); + return; + } + + case value_t::discarded: + { + o->write_characters("", 11); + return; + } + + case value_t::null: + { + o->write_characters("null", 4); + return; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief dump escaped string + + Escape a string by replacing certain special characters by a sequence of an + escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex + representation. The escaped string is written to output stream @a o. + + @param[in] s the string to escape + @param[in] ensure_ascii whether to escape non-ASCII characters with + \uXXXX sequences + + @complexity Linear in the length of string @a s. + */ + void dump_escaped(const string_t& s, const bool ensure_ascii) + { + std::uint32_t codepoint{}; + std::uint8_t state = UTF8_ACCEPT; + std::size_t bytes = 0; // number of bytes written to string_buffer + + // number of bytes written at the point of the last valid byte + std::size_t bytes_after_last_accept = 0; + std::size_t undumped_chars = 0; + + for (std::size_t i = 0; i < s.size(); ++i) + { + const auto byte = static_cast(s[i]); + + switch (decode(state, codepoint, byte)) + { + case UTF8_ACCEPT: // decode found a new code point + { + switch (codepoint) + { + case 0x08: // backspace + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'b'; + break; + } + + case 0x09: // horizontal tab + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 't'; + break; + } + + case 0x0A: // newline + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'n'; + break; + } + + case 0x0C: // formfeed + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'f'; + break; + } + + case 0x0D: // carriage return + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'r'; + break; + } + + case 0x22: // quotation mark + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\"'; + break; + } + + case 0x5C: // reverse solidus + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\\'; + break; + } + + default: + { + // escape control characters (0x00..0x1F) or, if + // ensure_ascii parameter is used, non-ASCII characters + if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F))) + { + if (codepoint <= 0xFFFF) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + static_cast((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x", + static_cast(codepoint))); + bytes += 6; + } + else + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + static_cast((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", + static_cast(0xD7C0u + (codepoint >> 10u)), + static_cast(0xDC00u + (codepoint & 0x3FFu)))); + bytes += 12; + } + } + else + { + // copy byte to buffer (all previous bytes + // been copied have in default case above) + string_buffer[bytes++] = s[i]; + } + break; + } + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + // remember the byte position of this accept + bytes_after_last_accept = bytes; + undumped_chars = 0; + break; + } + + case UTF8_REJECT: // decode found invalid UTF-8 byte + { + switch (error_handler) + { + case error_handler_t::strict: + { + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + hex_bytes(byte | 0), BasicJsonType())); + } + + case error_handler_t::ignore: + case error_handler_t::replace: + { + // in case we saw this character the first time, we + // would like to read it again, because the byte + // may be OK for itself, but just not OK for the + // previous sequence + if (undumped_chars > 0) + { + --i; + } + + // reset length buffer to the last accepted index; + // thus removing/ignoring the invalid characters + bytes = bytes_after_last_accept; + + if (error_handler == error_handler_t::replace) + { + // add a replacement character + if (ensure_ascii) + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'u'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'd'; + } + else + { + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xEF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBD'); + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + bytes_after_last_accept = bytes; + } + + undumped_chars = 0; + + // continue processing the string + state = UTF8_ACCEPT; + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + break; + } + + default: // decode found yet incomplete multi-byte code point + { + if (!ensure_ascii) + { + // code point will not be escaped - copy byte to buffer + string_buffer[bytes++] = s[i]; + } + ++undumped_chars; + break; + } + } + } + + // we finished processing the string + if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT)) + { + // write buffer + if (bytes > 0) + { + o->write_characters(string_buffer.data(), bytes); + } + } + else + { + // we finish reading, but do not accept: string was incomplete + switch (error_handler) + { + case error_handler_t::strict: + { + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + hex_bytes(static_cast(s.back() | 0)), BasicJsonType())); + } + + case error_handler_t::ignore: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + break; + } + + case error_handler_t::replace: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + // add a replacement character + if (ensure_ascii) + { + o->write_characters("\\ufffd", 6); + } + else + { + o->write_characters("\xEF\xBF\xBD", 3); + } + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + } + + private: + /*! + @brief count digits + + Count the number of decimal (base 10) digits for an input unsigned integer. + + @param[in] x unsigned integer number to count its digits + @return number of decimal digits + */ + inline unsigned int count_digits(number_unsigned_t x) noexcept + { + unsigned int n_digits = 1; + for (;;) + { + if (x < 10) + { + return n_digits; + } + if (x < 100) + { + return n_digits + 1; + } + if (x < 1000) + { + return n_digits + 2; + } + if (x < 10000) + { + return n_digits + 3; + } + x = x / 10000u; + n_digits += 4; + } + } + + /*! + * @brief convert a byte to a uppercase hex representation + * @param[in] byte byte to represent + * @return representation ("00".."FF") + */ + static std::string hex_bytes(std::uint8_t byte) + { + std::string result = "FF"; + constexpr const char* nibble_to_hex = "0123456789ABCDEF"; + result[0] = nibble_to_hex[byte / 16]; + result[1] = nibble_to_hex[byte % 16]; + return result; + } + + // templates to avoid warnings about useless casts + template ::value, int> = 0> + bool is_negative_number(NumberType x) + { + return x < 0; + } + + template < typename NumberType, enable_if_t ::value, int > = 0 > + bool is_negative_number(NumberType /*unused*/) + { + return false; + } + + /*! + @brief dump an integer + + Dump a given integer to output stream @a o. Works internally with + @a number_buffer. + + @param[in] x integer number (signed or unsigned) to dump + @tparam NumberType either @a number_integer_t or @a number_unsigned_t + */ + template < typename NumberType, detail::enable_if_t < + std::is_integral::value || + std::is_same::value || + std::is_same::value || + std::is_same::value, + int > = 0 > + void dump_integer(NumberType x) + { + static constexpr std::array, 100> digits_to_99 + { + { + {{'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'}}, + } + }; + + // special case for "0" + if (x == 0) + { + o->write_character('0'); + return; + } + + // use a pointer to fill the buffer + auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg) + + number_unsigned_t abs_value; + + unsigned int n_chars{}; + + if (is_negative_number(x)) + { + *buffer_ptr = '-'; + abs_value = remove_sign(static_cast(x)); + + // account one more byte for the minus sign + n_chars = 1 + count_digits(abs_value); + } + else + { + abs_value = static_cast(x); + n_chars = count_digits(abs_value); + } + + // spare 1 byte for '\0' + JSON_ASSERT(n_chars < number_buffer.size() - 1); + + // jump to the end to generate the string from backward, + // so we later avoid reversing the result + buffer_ptr += n_chars; + + // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu + // See: https://www.youtube.com/watch?v=o4-CwDo2zpg + while (abs_value >= 100) + { + const auto digits_index = static_cast((abs_value % 100)); + abs_value /= 100; + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + + if (abs_value >= 10) + { + const auto digits_index = static_cast(abs_value); + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + else + { + *(--buffer_ptr) = static_cast('0' + abs_value); + } + + o->write_characters(number_buffer.data(), n_chars); + } + + /*! + @brief dump a floating-point number + + Dump a given floating-point number to output stream @a o. Works internally + with @a number_buffer. + + @param[in] x floating-point number to dump + */ + void dump_float(number_float_t x) + { + // NaN / inf + if (!std::isfinite(x)) + { + o->write_characters("null", 4); + return; + } + + // If number_float_t is an IEEE-754 single or double precision number, + // use the Grisu2 algorithm to produce short numbers which are + // guaranteed to round-trip, using strtof and strtod, resp. + // + // NB: The test below works if == . + static constexpr bool is_ieee_single_or_double + = (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 24 && std::numeric_limits::max_exponent == 128) || + (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 53 && std::numeric_limits::max_exponent == 1024); + + dump_float(x, std::integral_constant()); + } + + void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) + { + auto* begin = number_buffer.data(); + auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); + + o->write_characters(begin, static_cast(end - begin)); + } + + void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) + { + // get number of digits for a float -> text -> float round-trip + static constexpr auto d = std::numeric_limits::max_digits10; + + // the actual conversion + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); + + // negative value indicates an error + JSON_ASSERT(len > 0); + // check if buffer was large enough + JSON_ASSERT(static_cast(len) < number_buffer.size()); + + // erase thousands separator + if (thousands_sep != '\0') + { + // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081 + const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep); + std::fill(end, number_buffer.end(), '\0'); + JSON_ASSERT((end - number_buffer.begin()) <= len); + len = (end - number_buffer.begin()); + } + + // convert decimal point to '.' + if (decimal_point != '\0' && decimal_point != '.') + { + // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081 + const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); + if (dec_pos != number_buffer.end()) + { + *dec_pos = '.'; + } + } + + o->write_characters(number_buffer.data(), static_cast(len)); + + // determine if we need to append ".0" + const bool value_is_int_like = + std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, + [](char c) + { + return c == '.' || c == 'e'; + }); + + if (value_is_int_like) + { + o->write_characters(".0", 2); + } + } + + /*! + @brief check whether a string is UTF-8 encoded + + The function checks each byte of a string whether it is UTF-8 encoded. The + result of the check is stored in the @a state parameter. The function must + be called initially with state 0 (accept). State 1 means the string must + be rejected, because the current byte is not allowed. If the string is + completely processed, but the state is non-zero, the string ended + prematurely; that is, the last byte indicated more bytes should have + followed. + + @param[in,out] state the state of the decoding + @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) + @param[in] byte next byte to decode + @return new state + + @note The function has been edited: a std::array is used. + + @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann + @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + */ + static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept + { + static const std::array utf8d = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF + 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, // C0..DF + 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF + 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 + } + }; + + JSON_ASSERT(byte < utf8d.size()); + const std::uint8_t type = utf8d[byte]; + + codep = (state != UTF8_ACCEPT) + ? (byte & 0x3fu) | (codep << 6u) + : (0xFFu >> type) & (byte); + + std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); + JSON_ASSERT(index < 400); + state = utf8d[index]; + return state; + } + + /* + * Overload to make the compiler happy while it is instantiating + * dump_integer for number_unsigned_t. + * Must never be called. + */ + number_unsigned_t remove_sign(number_unsigned_t x) + { + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return x; // LCOV_EXCL_LINE + } + + /* + * Helper function for dump_integer + * + * This function takes a negative signed integer and returns its absolute + * value as unsigned integer. The plus/minus shuffling is necessary as we can + * not directly remove the sign of an arbitrary signed integer as the + * absolute values of INT_MIN and INT_MAX are usually not the same. See + * #1708 for details. + */ + inline number_unsigned_t remove_sign(number_integer_t x) noexcept + { + JSON_ASSERT(x < 0 && x < (std::numeric_limits::max)()); // NOLINT(misc-redundant-expression) + return static_cast(-(x + 1)) + 1; + } + + private: + /// the output of the serializer + output_adapter_t o = nullptr; + + /// a (hopefully) large enough character buffer + std::array number_buffer{{}}; + + /// the locale + const std::lconv* loc = nullptr; + /// the locale's thousand separator character + const char thousands_sep = '\0'; + /// the locale's decimal point character + const char decimal_point = '\0'; + + /// string buffer + std::array string_buffer{{}}; + + /// the indentation character + const char indent_char; + /// the indentation string + string_t indent_string; + + /// error_handler how to react on decoding errors + const error_handler_t error_handler; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // less +#include // initializer_list +#include // input_iterator_tag, iterator_traits +#include // allocator +#include // for out_of_range +#include // enable_if, is_convertible +#include // pair +#include // vector + +// #include + + +namespace nlohmann +{ + +/// ordered_map: a minimal map-like container that preserves insertion order +/// for use within nlohmann::basic_json +template , + class Allocator = std::allocator>> + struct ordered_map : std::vector, Allocator> +{ + using key_type = Key; + using mapped_type = T; + using Container = std::vector, Allocator>; + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using size_type = typename Container::size_type; + using value_type = typename Container::value_type; + + // Explicit constructors instead of `using Container::Container` + // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) + ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {} + template + ordered_map(It first, It last, const Allocator& alloc = Allocator()) + : Container{first, last, alloc} {} + ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) + : Container{init, alloc} {} + + std::pair emplace(const key_type& key, T&& t) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return {it, false}; + } + } + Container::emplace_back(key, t); + return {--this->end(), true}; + } + + T& operator[](const Key& key) + { + return emplace(key, T{}).first->second; + } + + const T& operator[](const Key& key) const + { + return at(key); + } + + T& at(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + const T& at(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + size_type erase(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return 1; + } + } + return 0; + } + + iterator erase(iterator pos) + { + return erase(pos, std::next(pos)); + } + + iterator erase(iterator first, iterator last) + { + const auto elements_affected = std::distance(first, last); + const auto offset = std::distance(Container::begin(), first); + + // This is the start situation. We need to delete elements_affected + // elements (3 in this example: e, f, g), and need to return an + // iterator past the last deleted element (h in this example). + // Note that offset is the distance from the start of the vector + // to first. We will need this later. + + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // first last + + // Since we cannot move const Keys, we re-construct them in place. + // We start at first and re-construct (viz. copy) the elements from + // the back of the vector. Example for first iteration: + + // ,--------. + // v | destroy e and re-construct with h + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // it it + elements_affected + + for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it) + { + it->~value_type(); // destroy but keep allocation + new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it + } + + // [ a, b, c, d, h, i, j, h, i, j ] + // ^ ^ + // first last + + // remove the unneeded elements at the end of the vector + Container::resize(this->size() - static_cast(elements_affected)); + + // [ a, b, c, d, h, i, j ] + // ^ ^ + // first last + + // first is now pointing past the last deleted element, but we cannot + // use this iterator, because it may have been invalidated by the + // resize call. Instead, we can return begin() + offset. + return Container::begin() + offset; + } + + size_type count(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return 1; + } + } + return 0; + } + + iterator find(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + const_iterator find(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + std::pair insert( value_type&& value ) + { + return emplace(value.first, std::move(value.second)); + } + + std::pair insert( const value_type& value ) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == value.first) + { + return {it, false}; + } + } + Container::push_back(value); + return {--this->end(), true}; + } + + template + using require_input_iter = typename std::enable_if::iterator_category, + std::input_iterator_tag>::value>::type; + + template> + void insert(InputIt first, InputIt last) + { + for (auto it = first; it != last; ++it) + { + insert(*it); + } + } +}; + +} // namespace nlohmann + + +#if defined(JSON_HAS_CPP_17) + #include +#endif + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ + +/*! +@brief a class to store JSON values + +@internal +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + +@note ObjectType trick from https://stackoverflow.com/a/9860911 +@endinternal + +@since version 1.0.0 + +@nosubgrouping +*/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) +{ + private: + template friend struct detail::external_constructor; + friend ::nlohmann::json_pointer; + + template + friend class ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; + template + friend class ::nlohmann::detail::iter_impl; + template + friend class ::nlohmann::detail::binary_writer; + template + friend class ::nlohmann::detail::binary_reader; + template + friend class ::nlohmann::detail::json_sax_dom_parser; + template + friend class ::nlohmann::detail::json_sax_dom_callback_parser; + friend class ::nlohmann::detail::exception; + + /// workaround type for MSVC + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + + JSON_PRIVATE_UNLESS_TESTED: + // convenience aliases for types residing in namespace detail; + using lexer = ::nlohmann::detail::lexer_base; + + template + static ::nlohmann::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_tcb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false + ) + { + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); + } + + private: + using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; + template + using internal_iterator = ::nlohmann::detail::internal_iterator; + template + using iter_impl = ::nlohmann::detail::iter_impl; + template + using iteration_proxy = ::nlohmann::detail::iteration_proxy; + template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; + + template + using binary_reader = ::nlohmann::detail::binary_reader; + template using binary_writer = ::nlohmann::detail::binary_writer; + + JSON_PRIVATE_UNLESS_TESTED: + using serializer = ::nlohmann::detail::serializer; + + public: + using value_t = detail::value_t; + /// JSON Pointer, see @ref nlohmann::json_pointer + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; + /// how to treat decoding errors + using error_handler_t = detail::error_handler_t; + /// how to treat CBOR tags + using cbor_tag_handler_t = detail::cbor_tag_handler_t; + /// helper type for initializer lists of basic_json values + using initializer_list_t = std::initializer_list>; + + using input_format_t = detail::input_format_t; + /// SAX interface type, see @ref nlohmann::json_sax + using json_sax_t = json_sax; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + using exception = detail::exception; + using parse_error = detail::parse_error; + using invalid_iterator = detail::invalid_iterator; + using type_error = detail::type_error; + using out_of_range = detail::out_of_range; + using other_error = detail::other_error; + + /// @} + + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + + /// @brief returns the allocator associated with the container + /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/ + static allocator_type get_allocator() + { + return allocator_type(); + } + + /// @brief returns version information on the library + /// @sa https://json.nlohmann.me/api/basic_json/meta/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json meta() + { + basic_json result; + + result["copyright"] = "(C) 2013-2022 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"]["string"] = + std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_PATCH); + result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; + result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; + result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; + +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif + +#if defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif + +#ifdef __cplusplus + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } + + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + + /// @brief object key comparator type + /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/ +#if defined(JSON_HAS_CPP_14) + // Use transparent comparator if possible, combined with perfect forwarding + // on find() and count() calls prevents unnecessary string construction. + using object_comparator_t = std::less<>; +#else + using object_comparator_t = std::less; +#endif + + /// @brief a type for an object + /// @sa https://json.nlohmann.me/api/basic_json/object_t/ + using object_t = ObjectType>>; + + /// @brief a type for an array + /// @sa https://json.nlohmann.me/api/basic_json/array_t/ + using array_t = ArrayType>; + + /// @brief a type for a string + /// @sa https://json.nlohmann.me/api/basic_json/string_t/ + using string_t = StringType; + + /// @brief a type for a boolean + /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/ + using boolean_t = BooleanType; + + /// @brief a type for a number (integer) + /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/ + using number_integer_t = NumberIntegerType; + + /// @brief a type for a number (unsigned) + /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/ + using number_unsigned_t = NumberUnsignedType; + + /// @brief a type for a number (floating-point) + /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/ + using number_float_t = NumberFloatType; + + /// @brief a type for a packed binary type + /// @sa https://json.nlohmann.me/api/basic_json/binary_t/ + using binary_t = nlohmann::byte_container_with_subtype; + + /// @} + + private: + + /// helper for exception-safe object creation + template + JSON_HEDLEY_RETURNS_NON_NULL + static T* create(Args&& ... args) + { + AllocatorType alloc; + using AllocatorTraits = std::allocator_traits>; + + auto deleter = [&](T * obj) + { + AllocatorTraits::deallocate(alloc, obj, 1); + }; + std::unique_ptr obj(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, obj.get(), std::forward(args)...); + JSON_ASSERT(obj != nullptr); + return obj.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + binary | binary | pointer to @ref binary_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// binary (stored with pointer to save storage) + binary_t* binary; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } + + case value_t::array: + { + array = create(); + break; + } + + case value_t::string: + { + string = create(""); + break; + } + + case value_t::binary: + { + binary = create(); + break; + } + + case value_t::boolean: + { + boolean = static_cast(false); + break; + } + + case value_t::number_integer: + { + number_integer = static_cast(0); + break; + } + + case value_t::number_unsigned: + { + number_unsigned = static_cast(0); + break; + } + + case value_t::number_float: + { + number_float = static_cast(0.0); + break; + } + + case value_t::null: + { + object = nullptr; // silence warning, see #821 + break; + } + + case value_t::discarded: + default: + { + object = nullptr; // silence warning, see #821 + if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) + { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.5", basic_json())); // LCOV_EXCL_LINE + } + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) : string(create(value)) {} + + /// constructor for rvalue strings + json_value(string_t&& value) : string(create(std::move(value))) {} + + /// constructor for objects + json_value(const object_t& value) : object(create(value)) {} + + /// constructor for rvalue objects + json_value(object_t&& value) : object(create(std::move(value))) {} + + /// constructor for arrays + json_value(const array_t& value) : array(create(value)) {} + + /// constructor for rvalue arrays + json_value(array_t&& value) : array(create(std::move(value))) {} + + /// constructor for binary arrays + json_value(const typename binary_t::container_type& value) : binary(create(value)) {} + + /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) : binary(create(std::move(value))) {} + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) : binary(create(value)) {} + + /// constructor for rvalue binary arrays (internal type) + json_value(binary_t&& value) : binary(create(std::move(value))) {} + + void destroy(value_t t) + { + if (t == value_t::array || t == value_t::object) + { + // flatten the current json_value to a heap-allocated stack + std::vector stack; + + // move the top-level items to stack + if (t == value_t::array) + { + stack.reserve(array->size()); + std::move(array->begin(), array->end(), std::back_inserter(stack)); + } + else + { + stack.reserve(object->size()); + for (auto&& it : *object) + { + stack.push_back(std::move(it.second)); + } + } + + while (!stack.empty()) + { + // move the last item to local variable to be processed + basic_json current_item(std::move(stack.back())); + stack.pop_back(); + + // if current_item is array/object, move + // its children to the stack to be processed later + if (current_item.is_array()) + { + std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack)); + + current_item.m_value.array->clear(); + } + else if (current_item.is_object()) + { + for (auto&& it : *current_item.m_value.object) + { + stack.push_back(std::move(it.second)); + } + + current_item.m_value.object->clear(); + } + + // it's now safe that current_item get destructed + // since it doesn't have any children + } + } + + switch (t) + { + case value_t::object: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, string); + std::allocator_traits::deallocate(alloc, string, 1); + break; + } + + case value_t::binary: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, binary); + std::allocator_traits::deallocate(alloc, binary, 1); + break; + } + + case value_t::null: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::discarded: + default: + { + break; + } + } + } + }; + + private: + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + + Furthermore, the parent relation is checked for arrays and objects: If + @a check_parents true and the value is an array or object, then the + container's elements must have the current value as parent. + + @param[in] check_parents whether the parent relation should be checked. + The value is true by default and should only be set to false + during destruction of objects when the invariant does not + need to hold. + */ + void assert_invariant(bool check_parents = true) const noexcept + { + JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); + JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); + JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); + +#if JSON_DIAGNOSTICS + JSON_TRY + { + // cppcheck-suppress assertWithSideEffect + JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j) + { + return j.m_parent == this; + })); + } + JSON_CATCH(...) {} // LCOV_EXCL_LINE +#endif + static_cast(check_parents); + } + + void set_parents() + { +#if JSON_DIAGNOSTICS + switch (m_type) + { + case value_t::array: + { + for (auto& element : *m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + break; + } +#endif + } + + iterator set_parents(iterator it, typename iterator::difference_type count_set_parents) + { +#if JSON_DIAGNOSTICS + for (typename iterator::difference_type i = 0; i < count_set_parents; ++i) + { + (it + i)->m_parent = this; + } +#else + static_cast(count_set_parents); +#endif + return it; + } + + reference set_parent(reference j, std::size_t old_capacity = static_cast(-1)) + { +#if JSON_DIAGNOSTICS + if (old_capacity != static_cast(-1)) + { + // see https://github.com/nlohmann/json/issues/2838 + JSON_ASSERT(type() == value_t::array); + if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + return j; + } + } + + // ordered_json uses a vector internally, so pointers could have + // been invalidated; see https://github.com/nlohmann/json/issues/2962 +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning(push ) +#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr +#endif + if (detail::is_ordered_map::value) + { + set_parents(); + return j; + } +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning( pop ) +#endif + + j.m_parent = this; +#else + static_cast(j); + static_cast(old_capacity); +#endif + return j; + } + + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /// @brief parser event types + /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/ + using parse_event_t = detail::parse_event_t; + + /// @brief per-element parser callback type + /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/ + using parser_callback_t = detail::parser_callback_t; + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /// @brief create an empty value with a given type + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(const value_t v) + : m_type(v), m_value(v) + { + assert_invariant(); + } + + /// @brief create a null object + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) + { + assert_invariant(); + } + + /// @brief create a JSON value from compatible types + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < typename CompatibleType, + typename U = detail::uncvref_t, + detail::enable_if_t < + !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 > + basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape) + JSONSerializer::to_json(std::declval(), + std::forward(val)))) + { + JSONSerializer::to_json(*this, std::forward(val)); + set_parents(); + assert_invariant(); + } + + /// @brief create a JSON value from an existing one + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value&& !std::is_same::value, int > = 0 > + basic_json(const BasicJsonType& val) + { + using other_boolean_t = typename BasicJsonType::boolean_t; + using other_number_float_t = typename BasicJsonType::number_float_t; + using other_number_integer_t = typename BasicJsonType::number_integer_t; + using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using other_string_t = typename BasicJsonType::string_t; + using other_object_t = typename BasicJsonType::object_t; + using other_array_t = typename BasicJsonType::array_t; + using other_binary_t = typename BasicJsonType::binary_t; + + switch (val.type()) + { + case value_t::boolean: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::binary: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::null: + *this = nullptr; + break; + case value_t::discarded: + m_type = value_t::discarded; + break; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + set_parents(); + assert_invariant(); + } + + /// @brief create a container (array or object) from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(initializer_list_t init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const detail::json_ref& element_ref) + { + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string(); + }); + + // adjust type if type deduction is not wanted + if (!type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) + { + JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json())); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; + + for (auto& element_ref : init) + { + auto element = element_ref.moved_or_copied(); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); + } + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init.begin(), init.end()); + } + + set_parents(); + assert_invariant(); + } + + /// @brief explicitly create a binary array (without subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = init; + return res; + } + + /// @brief explicitly create a binary array (with subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } + + /// @brief explicitly create a binary array + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = std::move(init); + return res; + } + + /// @brief explicitly create a binary array (with subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(std::move(init), subtype); + return res; + } + + /// @brief explicitly create an array from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/array/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json array(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::array); + } + + /// @brief explicitly create an object from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/object/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json object(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::object); + } + + /// @brief construct an array with count copies of given value + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) + { + m_value.array = create(cnt, val); + set_parents(); + assert_invariant(); + } + + /// @brief construct a JSON container given an iterator range + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < class InputIT, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type = 0 > + basic_json(InputIT first, InputIT last) + { + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); + + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json())); + } + + // copy type from first iterator + m_type = first.m_object->m_type; + + // check if iterator range is complete for primitive values + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object)); + } + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::binary: + case value_t::discarded: + default: + break; + } + + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } + + case value_t::object: + { + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::binary: + { + m_value = *first.m_object->m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object)); + } + + set_parents(); + assert_invariant(); + } + + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + template, + std::is_same>::value, int> = 0 > + basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} + + /// @brief copy constructor + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(const basic_json& other) + : m_type(other.m_type) + { + // check of passed value is valid + other.assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + m_value = *other.m_value.object; + break; + } + + case value_t::array: + { + m_value = *other.m_value.array; + break; + } + + case value_t::string: + { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } + + case value_t::binary: + { + m_value = *other.m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + + set_parents(); + assert_invariant(); + } + + /// @brief move constructor + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) + { + // check that passed value is valid + other.assert_invariant(false); + + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; + + set_parents(); + assert_invariant(); + } + + /// @brief copy assignment + /// @sa https://json.nlohmann.me/api/basic_json/operator=/ + basic_json& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + set_parents(); + assert_invariant(); + return *this; + } + + /// @brief destructor + /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/ + ~basic_json() noexcept + { + assert_invariant(false); + m_value.destroy(m_type); + } + + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /// @brief serialization + /// @sa https://json.nlohmann.me/api/basic_json/dump/ + string_t dump(const int indent = -1, + const char indent_char = ' ', + const bool ensure_ascii = false, + const error_handler_t error_handler = error_handler_t::strict) const + { + string_t result; + serializer s(detail::output_adapter(result), indent_char, error_handler); + + if (indent >= 0) + { + s.dump(*this, true, ensure_ascii, static_cast(indent)); + } + else + { + s.dump(*this, false, ensure_ascii, 0); + } + + return result; + } + + /// @brief return the type of the JSON value (explicit) + /// @sa https://json.nlohmann.me/api/basic_json/type/ + constexpr value_t type() const noexcept + { + return m_type; + } + + /// @brief return whether type is primitive + /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/ + constexpr bool is_primitive() const noexcept + { + return is_null() || is_string() || is_boolean() || is_number() || is_binary(); + } + + /// @brief return whether type is structured + /// @sa https://json.nlohmann.me/api/basic_json/is_structured/ + constexpr bool is_structured() const noexcept + { + return is_array() || is_object(); + } + + /// @brief return whether value is null + /// @sa https://json.nlohmann.me/api/basic_json/is_null/ + constexpr bool is_null() const noexcept + { + return m_type == value_t::null; + } + + /// @brief return whether value is a boolean + /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/ + constexpr bool is_boolean() const noexcept + { + return m_type == value_t::boolean; + } + + /// @brief return whether value is a number + /// @sa https://json.nlohmann.me/api/basic_json/is_number/ + constexpr bool is_number() const noexcept + { + return is_number_integer() || is_number_float(); + } + + /// @brief return whether value is an integer number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/ + constexpr bool is_number_integer() const noexcept + { + return m_type == value_t::number_integer || m_type == value_t::number_unsigned; + } + + /// @brief return whether value is an unsigned integer number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/ + constexpr bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; + } + + /// @brief return whether value is a floating-point number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/ + constexpr bool is_number_float() const noexcept + { + return m_type == value_t::number_float; + } + + /// @brief return whether value is an object + /// @sa https://json.nlohmann.me/api/basic_json/is_object/ + constexpr bool is_object() const noexcept + { + return m_type == value_t::object; + } + + /// @brief return whether value is an array + /// @sa https://json.nlohmann.me/api/basic_json/is_array/ + constexpr bool is_array() const noexcept + { + return m_type == value_t::array; + } + + /// @brief return whether value is a string + /// @sa https://json.nlohmann.me/api/basic_json/is_string/ + constexpr bool is_string() const noexcept + { + return m_type == value_t::string; + } + + /// @brief return whether value is a binary array + /// @sa https://json.nlohmann.me/api/basic_json/is_binary/ + constexpr bool is_binary() const noexcept + { + return m_type == value_t::binary; + } + + /// @brief return whether value is discarded + /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/ + constexpr bool is_discarded() const noexcept + { + return m_type == value_t::discarded; + } + + /// @brief return the type of the JSON value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/ + constexpr operator value_t() const noexcept + { + return m_type; + } + + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (JSON_HEDLEY_LIKELY(is_boolean())) + { + return m_value.boolean; + } + + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this)); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (binary) + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /// get a pointer to the value (binary) + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This function helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // delegate the call to get_ptr<>() + auto* ptr = obj.template get_ptr::type>(); + + if (JSON_HEDLEY_LIKELY(ptr != nullptr)) + { + return *ptr; + } + + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj)); + } + + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /// @brief get a pointer value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/ + template::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /// @brief get a pointer value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/ + template < typename PointerType, typename std::enable_if < + std::is_pointer::value&& + std::is_const::type>::value, int >::type = 0 > + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + private: + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::is_default_constructible::value&& + detail::has_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) + { + auto ret = ValueType(); + JSONSerializer::from_json(*this, ret); + return ret; + } + + /*! + @brief get a value (explicit); special case + + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` + + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) + { + return JSONSerializer::from_json(*this); + } + + /*! + @brief get special-case overload + + This overloads converts the current @ref basic_json in a different + @ref basic_json type + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this, converted into @a BasicJsonType + + @complexity Depending on the implementation of the called `from_json()` + method. + + @since version 3.2.0 + */ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value, + int > = 0 > + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const + { + return *this; + } + + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::value, + int> = 0> + basic_json get_impl(detail::priority_tag<3> /*unused*/) const + { + return *this; + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, + int> = 0> + constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept + -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + public: + /*! + @brief get a (pointer) value (explicit) + + Performs explicit type conversion between the JSON value and a compatible value if required. + + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. + + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. + + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @tparam ValueType if necessary + + @throw what @ref json_serializer `from_json()` method throws if conversion is required + + @since version 2.1.0 + */ + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> +#if defined(JSON_HAS_CPP_14) + constexpr +#endif + auto get() const noexcept( + noexcept(std::declval().template get_impl(detail::priority_tag<4> {}))) + -> decltype(std::declval().template get_impl(detail::priority_tag<4> {})) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return get_impl(detail::priority_tag<4> {}); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa see @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get() noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /// @brief get a value (explicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_to/ + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow calling get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + /// @brief get a reference value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ref/ + template::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /// @brief get a reference value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ref/ + template < typename ReferenceType, typename std::enable_if < + std::is_reference::value&& + std::is_const::type>::value, int >::type = 0 > + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template < typename ValueType, typename std::enable_if < + detail::conjunction < + detail::negation>, + detail::negation>>, + detail::negation>, + detail::negation>, + detail::negation>>, + +#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) + detail::negation>, +#endif + detail::is_detected_lazy + >::value, int >::type = 0 > + JSON_EXPLICIT operator ValueType() const + { + // delegate the call to get<>() const + return get(); + } + + /// @brief get a binary value + /// @sa https://json.nlohmann.me/api/basic_json/get_binary/ + binary_t& get_binary() + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + } + + return *get_ptr(); + } + + /// @brief get a binary value + /// @sa https://json.nlohmann.me/api/basic_json/get_binary/ + const binary_t& get_binary() const + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + } + + return *get_ptr(); + } + + /// @} + + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /// @brief access specified array element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(size_type idx) + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return set_parent(m_value.array->at(idx)); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /// @brief access specified array element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(size_type idx) const + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return set_parent(m_value.object->at(key)); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /// @brief access specified array element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](size_type idx) + { + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { +#if JSON_DIAGNOSTICS + // remember array size & capacity before resizing + const auto old_size = m_value.array->size(); + const auto old_capacity = m_value.array->capacity(); +#endif + m_value.array->resize(idx + 1); + +#if JSON_DIAGNOSTICS + if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + } + else + { + // set parent for values added above + set_parents(begin() + static_cast(old_size), static_cast(idx + 1 - old_size)); + } +#endif + assert_invariant(); + } + + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified array element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](const typename object_t::key_type& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return set_parent(m_value.object->operator[](key)); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + template + JSON_HEDLEY_NON_NULL(2) + reference operator[](T* key) + { + // implicitly convert null to object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return set_parent(m_value.object->operator[](key)); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + template + JSON_HEDLEY_NON_NULL(2) + const_reference operator[](T* key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + /// using std::is_convertible in a std::enable_if will fail when using explicit conversions + template < class ValueType, typename std::enable_if < + detail::is_getable::value + && !std::is_same::value, int >::type = 0 > + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + /// overload for a default value of type const char* + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template::value, int>::type = 0> + ValueType value(const json_pointer& ptr, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return default_value; + } + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + /// overload for a default value of type const char* + JSON_HEDLEY_NON_NULL(3) + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + + /// @brief access the first element + /// @sa https://json.nlohmann.me/api/basic_json/front/ + reference front() + { + return *begin(); + } + + /// @brief access the first element + /// @sa https://json.nlohmann.me/api/basic_json/front/ + const_reference front() const + { + return *cbegin(); + } + + /// @brief access the last element + /// @sa https://json.nlohmann.me/api/basic_json/back/ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /// @brief access the last element + /// @sa https://json.nlohmann.me/api/basic_json/back/ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /// @brief remove element given an iterator + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType pos) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) + { + JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + return result; + } + + /// @brief remove elements given an iterator range + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType first, IteratorType last) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) + { + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this)); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + return result; + } + + /// @brief remove element from a JSON object given a key + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + size_type erase(const typename object_t::key_type& key) + { + // this erase only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->erase(key); + } + + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + /// @brief remove element from a JSON array given an index + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + void erase(const size_type idx) + { + // this erase only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + if (JSON_HEDLEY_UNLIKELY(idx >= size())) + { + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + + m_value.array->erase(m_value.array->begin() + static_cast(idx)); + } + else + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + } + + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + template + iterator find(KeyT&& key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + template + const_iterator find(KeyT&& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /// @brief returns the number of occurrences of a key in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/count/ + template + size_type count(KeyT&& key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(std::forward(key)) : 0; + } + + /// @brief check the existence of an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + template < typename KeyT, typename std::enable_if < + !std::is_same::type, json_pointer>::value, int >::type = 0 > + bool contains(KeyT && key) const + { + return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); + } + + /// @brief check the existence of an element in a JSON object given a JSON pointer + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + bool contains(const json_pointer& ptr) const + { + return ptr.contains(this); + } + + /// @} + + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /// @brief returns an iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/begin/ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /// @brief returns an iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/begin/ + const_iterator begin() const noexcept + { + return cbegin(); + } + + /// @brief returns a const iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/cbegin/ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/end/ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/end/ + const_iterator end() const noexcept + { + return cend(); + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/cend/ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /// @brief returns an iterator to the reverse-beginning + /// @sa https://json.nlohmann.me/api/basic_json/rbegin/ + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /// @brief returns an iterator to the reverse-beginning + /// @sa https://json.nlohmann.me/api/basic_json/rbegin/ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } + + /// @brief returns an iterator to the reverse-end + /// @sa https://json.nlohmann.me/api/basic_json/rend/ + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /// @brief returns an iterator to the reverse-end + /// @sa https://json.nlohmann.me/api/basic_json/rend/ + const_reverse_iterator rend() const noexcept + { + return crend(); + } + + /// @brief returns a const reverse iterator to the last element + /// @sa https://json.nlohmann.me/api/basic_json/crbegin/ + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /// @brief returns a const reverse iterator to one before the first + /// @sa https://json.nlohmann.me/api/basic_json/crend/ + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + + public: + /// @brief wrapper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + /// @deprecated This function is deprecated since 3.1.0 and will be removed in + /// version 4.0.0 of the library. Please use @ref items() instead; + /// that is, replace `json::iterator_wrapper(j)` with `j.items()`. + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(reference ref) noexcept + { + return ref.items(); + } + + /// @brief wrapper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + /// @deprecated This function is deprecated since 3.1.0 and will be removed in + /// version 4.0.0 of the library. Please use @ref items() instead; + /// that is, replace `json::iterator_wrapper(j)` with `j.items()`. + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(const_reference ref) noexcept + { + return ref.items(); + } + + /// @brief helper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + iteration_proxy items() noexcept + { + return iteration_proxy(*this); + } + + /// @brief helper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + iteration_proxy items() const noexcept + { + return iteration_proxy(*this); + } + + /// @} + + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /// @brief checks whether the container is empty. + /// @sa https://json.nlohmann.me/api/basic_json/empty/ + bool empty() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return true; + } + + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } + + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types are nonempty + return false; + } + } + } + + /// @brief returns the number of elements + /// @sa https://json.nlohmann.me/api/basic_json/size/ + size_type size() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } + + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } + + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have size 1 + return 1; + } + } + } + + /// @brief returns the maximum possible number of elements + /// @sa https://json.nlohmann.me/api/basic_json/max_size/ + size_type max_size() const noexcept + { + switch (m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } + + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /// @brief clears the contents + /// @sa https://json.nlohmann.me/api/basic_json/clear/ + void clear() noexcept + { + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } + + case value_t::boolean: + { + m_value.boolean = false; + break; + } + + case value_t::string: + { + m_value.string->clear(); + break; + } + + case value_t::binary: + { + m_value.binary->clear(); + break; + } + + case value_t::array: + { + m_value.array->clear(); + break; + } + + case value_t::object: + { + m_value.object->clear(); + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (move semantics) + const auto old_capacity = m_value.array->capacity(); + m_value.array->push_back(std::move(val)); + set_parent(m_value.array->back(), old_capacity); + // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array + const auto old_capacity = m_value.array->capacity(); + m_value.array->push_back(val); + set_parent(m_value.array->back(), old_capacity); + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to object + auto res = m_value.object->insert(val); + set_parent(res.first->second); + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(initializer_list_t init) + { + if (is_object() && init.size() == 2 && (*init.begin())->is_string()) + { + basic_json&& key = init.begin()->moved_or_copied(); + push_back(typename object_t::value_type( + std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); + } + else + { + push_back(basic_json(init)); + } + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(initializer_list_t init) + { + push_back(init); + return *this; + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/ + template + reference emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) + const auto old_capacity = m_value.array->capacity(); + m_value.array->emplace_back(std::forward(args)...); + return set_parent(m_value.array->back(), old_capacity); + } + + /// @brief add an object to an object if key does not exist + /// @sa https://json.nlohmann.me/api/basic_json/emplace/ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this)); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + set_parent(res.first->second); + + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + + /// Helper for insertion of an iterator + /// @note: This uses std::distance to support GCC 4.8, + /// see https://github.com/nlohmann/json/pull/1257 + template + iterator insert_iterator(const_iterator pos, Args&& ... args) + { + iterator result(this); + JSON_ASSERT(m_value.array != nullptr); + + auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); + m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); + result.m_it.array_iterator = m_value.array->begin() + insert_pos; + + // This could have been written as: + // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + // but the return value of insert is missing in GCC 4.8, so it is written this way instead. + + set_parents(); + return result; + } + + /// @brief inserts element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + /// @brief inserts element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } + + /// @brief inserts copies of element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, cnt, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + /// @brief inserts range of elements into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) + { + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + } + + /// @brief inserts elements from initializer list into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, initializer_list_t ilist) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, ilist.begin(), ilist.end()); + } + + /// @brief inserts range of elements into object + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + void insert(const_iterator first, const_iterator last) + { + // insert only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); + } + + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + } + + /// @brief updates a JSON object from another object, overwriting existing keys + /// @sa https://json.nlohmann.me/api/basic_json/update/ + void update(const_reference j, bool merge_objects = false) + { + update(j.begin(), j.end(), merge_objects); + } + + /// @brief updates a JSON object from another object, overwriting existing keys + /// @sa https://json.nlohmann.me/api/basic_json/update/ + void update(const_iterator first, const_iterator last, bool merge_objects = false) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(first.m_object->type_name()), *first.m_object)); + } + + for (auto it = first; it != last; ++it) + { + if (merge_objects && it.value().is_object()) + { + auto it2 = m_value.object->find(it.key()); + if (it2 != m_value.object->end()) + { + it2->second.update(it.value(), true); + continue; + } + } + m_value.object->operator[](it.key()) = it.value(); +#if JSON_DIAGNOSTICS + m_value.object->operator[](it.key()).m_parent = this; +#endif + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + + set_parents(); + other.set_parents(); + assert_invariant(); + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + left.swap(right); + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(array_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + std::swap(*(m_value.array), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(object_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + std::swap(*(m_value.object), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(string_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_string())) + { + std::swap(*(m_value.string), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(binary_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @} + + public: + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + return *lhs.m_value.array == *rhs.m_value.array; + + case value_t::object: + return *lhs.m_value.object == *rhs.m_value.object; + + case value_t::null: + return true; + + case value_t::string: + return *lhs.m_value.string == *rhs.m_value.string; + + case value_t::boolean: + return lhs.m_value.boolean == rhs.m_value.boolean; + + case value_t::number_integer: + return lhs.m_value.number_integer == rhs.m_value.number_integer; + + case value_t::number_unsigned: + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + + case value_t::number_float: + return lhs.m_value.number_float == rhs.m_value.number_float; + + case value_t::binary: + return *lhs.m_value.binary == *rhs.m_value.binary; + + case value_t::discarded: + default: + return false; + } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); + } + + return false; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, ScalarType rhs) noexcept + { + return lhs == basic_json(rhs); + } + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + template::value, int>::type = 0> + friend bool operator==(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) == rhs; + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs == rhs); + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs != basic_json(rhs); + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + template::value, int>::type = 0> + friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) != rhs; + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + // note parentheses are necessary, see + // https://github.com/nlohmann/json/issues/1530 + return (*lhs.m_value.array) < (*rhs.m_value.array); + + case value_t::object: + return (*lhs.m_value.object) < (*rhs.m_value.object); + + case value_t::null: + return false; + + case value_t::string: + return (*lhs.m_value.string) < (*rhs.m_value.string); + + case value_t::boolean: + return (lhs.m_value.boolean) < (rhs.m_value.boolean); + + case value_t::number_integer: + return (lhs.m_value.number_integer) < (rhs.m_value.number_integer); + + case value_t::number_unsigned: + return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned); + + case value_t::number_float: + return (lhs.m_value.number_float) < (rhs.m_value.number_float); + + case value_t::binary: + return (*lhs.m_value.binary) < (*rhs.m_value.binary); + + case value_t::discarded: + default: + return false; + } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } + + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, ScalarType rhs) noexcept + { + return lhs < basic_json(rhs); + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + template::value, int>::type = 0> + friend bool operator<(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) < rhs; + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + return !(rhs < lhs); + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs <= basic_json(rhs); + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + template::value, int>::type = 0> + friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) <= rhs; + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs <= rhs); + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, ScalarType rhs) noexcept + { + return lhs > basic_json(rhs); + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + template::value, int>::type = 0> + friend bool operator>(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) > rhs; + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs < rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs >= basic_json(rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + template::value, int>::type = 0> + friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) >= rhs; + } + + /// @} + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ +#ifndef JSON_NO_IO + /// @brief serialize to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = o.width() > 0; + const auto indentation = pretty_print ? o.width() : 0; + + // reset width to 0 for subsequent calls to this stream + o.width(0); + + // do the actual serialization + serializer s(detail::output_adapter(o), o.fill()); + s.dump(j, pretty_print, false, static_cast(indentation)); + return o; + } + + /// @brief serialize to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ + /// @deprecated This function is deprecated since 3.0.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// operator<<(std::ostream&, const basic_json&) instead; that is, + /// replace calls like `j >> o;` with `o << j;`. + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } +#endif // JSON_NO_IO + /// @} + + + ///////////////////// + // deserialization // + ///////////////////// + + /// @name deserialization + /// @{ + + /// @brief deserialize from a compatible input + /// @sa https://json.nlohmann.me/api/basic_json/parse/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(InputType&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /// @brief deserialize from a pair of character iterators + /// @sa https://json.nlohmann.me/api/basic_json/parse/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(IteratorType first, + IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) + static basic_json parse(detail::span_input_adapter&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /// @brief check if the input is valid JSON + /// @sa https://json.nlohmann.me/api/basic_json/accept/ + template + static bool accept(InputType&& i, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); + } + + /// @brief check if the input is valid JSON + /// @sa https://json.nlohmann.me/api/basic_json/accept/ + template + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) + { + return parser(i.get(), nullptr, false, ignore_comments).accept(true); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + template + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(InputType&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::forward(i)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + template + JSON_HEDLEY_NON_NULL(3) + static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::move(first), std::move(last)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + /// @deprecated This function is deprecated since 3.8.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// sax_parse(ptr, ptr + len) instead. + template + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = i.get(); + return format == input_format_t::json + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } +#ifndef JSON_NO_IO + /// @brief deserialize from stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/ + /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// operator>>(std::istream&, basic_json&) instead; that is, + /// replace calls like `j << i;` with `i >> j;`. + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + return operator>>(i, j); + } + + /// @brief deserialize from stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + parser(detail::input_adapter(i)).parse(false, j); + return i; + } +#endif // JSON_NO_IO + /// @} + + /////////////////////////// + // convenience functions // + /////////////////////////// + + /// @brief return the type as string + /// @sa https://json.nlohmann.me/api/basic_json/type_name/ + JSON_HEDLEY_RETURNS_NON_NULL + const char* type_name() const noexcept + { + switch (m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::binary: + return "binary"; + case value_t::discarded: + return "discarded"; + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + default: + return "number"; + } + } + + + JSON_PRIVATE_UNLESS_TESTED: + ////////////////////// + // member variables // + ////////////////////// + + /// the type of the current element + value_t m_type = value_t::null; + + /// the value of the current element + json_value m_value = {}; + +#if JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + public: + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + to_cbor(j, result); + return result; + } + + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack(j, result); + return result; + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static std::vector to_ubjson(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_ubjson(j, result, use_size, use_type); + return result; + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static std::vector to_bson(const basic_json& j) + { + std::vector result; + to_bson(j, result); + return result; + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /// @brief create a JSON value from an input in CBOR format + /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in CBOR format + /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); + } + + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in MessagePack format + /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in MessagePack format + /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in UBJSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in UBJSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + /// @} + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](const json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(const json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + /// @brief return flattened JSON value + /// @sa https://json.nlohmann.me/api/basic_json/flatten/ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /// @brief unflatten a previously flattened JSON value + /// @sa https://json.nlohmann.me/api/basic_json/unflatten/ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /// @brief applies a JSON patch + /// @sa https://json.nlohmann.me/api/basic_json/patch/ + basic_json patch(const basic_json& json_patch) const + { + // make a working copy to apply the patch to + basic_json result = *this; + + // the valid JSON Patch operations + enum class patch_operations {add, remove, replace, move, copy, test, invalid}; + + const auto get_op = [](const std::string & op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer & ptr, basic_json val) + { + // adding to the root of the target document means replacing it + if (ptr.empty()) + { + result = val; + return; + } + + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) + { + case value_t::null: + case value_t::object: + { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: + { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = json_pointer::array_index(last_path); + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) + { + // avoid undefined behavior + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent)); + } + + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + break; + } + + // if there exists a parent it cannot be primitive + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [this, &result](json_pointer & ptr) + { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (JSON_HEDLEY_LIKELY(it != parent.end())) + { + parent.erase(it); + } + else + { + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this)); + } + } + else if (parent.is_array()) + { + // note erase performs range check + parent.erase(json_pointer::array_index(last_path)); + } + }; + + // type check: top level value must be an array + if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch)); + } + + // iterate and apply the operations + for (const auto& val : json_patch) + { + // wrapper to get a value for an operation + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json & + { + // find value + auto it = val.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + + // check if desired value is present + if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val)); + } + + // check if result is of type string + if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val)); + } + + // no error: return value + return it->second; + }; + + // type check: every element of the array must be an object + if (JSON_HEDLEY_UNLIKELY(!val.is_object())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val)); + } + + // collect mandatory members + const auto op = get_value("op", "op", true).template get(); + const auto path = get_value(op, "path", true).template get(); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: + { + operation_remove(ptr); + break; + } + + case patch_operations::replace: + { + // the "path" location must exist - use at() + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: + { + const auto from_path = get_value("move", "from", true).template get(); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: + { + const auto from_path = get_value("copy", "from", true).template get(); + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The copy is functionally identical to an "add" + // operation at the target location using the value + // specified in the "from" member. + operation_add(ptr, v); + break; + } + + case patch_operations::test: + { + bool success = false; + JSON_TRY + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (JSON_HEDLEY_UNLIKELY(!success)) + { + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val)); + } + + break; + } + + case patch_operations::invalid: + default: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val)); + } + } + } + + return result; + } + + /// @brief creates a diff as a JSON patch + /// @sa https://json.nlohmann.me/api/basic_json/diff/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json diff(const basic_json& source, const basic_json& target, + const std::string& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + return result; + } + + switch (source.type()) + { + case value_t::array: + { + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() && i < target.size()) + { + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // We now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", path + "/" + std::to_string(i)} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", path + "/-"}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.cbegin(); it != source.cend(); ++it) + { + // escape the key name to be used in a JSON patch + const auto path_key = path + "/" + detail::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path_key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, {"path", path_key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.cbegin(); it != target.cend(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto path_key = path + "/" + detail::escape(it.key()); + result.push_back( + { + {"op", "add"}, {"path", path_key}, + {"value", it.value()} + }); + } + } + + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + break; + } + } + + return result; + } + + /// @} + + //////////////////////////////// + // JSON Merge Patch functions // + //////////////////////////////// + + /// @name JSON Merge Patch functions + /// @{ + + /// @brief applies a JSON Merge Patch + /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/ + void merge_patch(const basic_json& apply_patch) + { + if (apply_patch.is_object()) + { + if (!is_object()) + { + *this = object(); + } + for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) + { + if (it.value().is_null()) + { + erase(it.key()); + } + else + { + operator[](it.key()).merge_patch(it.value()); + } + } + } + else + { + *this = apply_patch; + } + } + + /// @} +}; + +/// @brief user-defined to_string function for JSON values +/// @sa https://json.nlohmann.me/api/basic_json/to_string/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j) +{ + return j.dump(); +} + +} // namespace nlohmann + +/////////////////////// +// nonmember support // +/////////////////////// + +namespace std // NOLINT(cert-dcl58-cpp) +{ + +/// @brief hash value for JSON objects +/// @sa https://json.nlohmann.me/api/basic_json/std_hash/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct hash +{ + std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const + { + return nlohmann::detail::hash(j); + } +}; + +// specialization for std::less +template<> +struct less< ::nlohmann::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679 +{ + /*! + @brief compare two value_t enum values + @since version 3.0.0 + */ + bool operator()(nlohmann::detail::value_t lhs, + nlohmann::detail::value_t rhs) const noexcept + { + return nlohmann::detail::operator<(lhs, rhs); + } +}; + +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + +/// @brief exchanges the values of two JSON objects +/// @sa https://json.nlohmann.me/api/basic_json/std_swap/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name) + is_nothrow_move_constructible::value&& // NOLINT(misc-redundant-expression) + is_nothrow_move_assignable::value) +{ + j1.swap(j2); +} + +#endif + +} // namespace std + +/// @brief user-defined string literal for JSON values +/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json operator "" _json(const char* s, std::size_t n) +{ + return nlohmann::json::parse(s, s + n); +} + +/// @brief user-defined string literal for JSON pointer +/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +// #include + + +// restore clang diagnostic settings +#if defined(__clang__) + #pragma clang diagnostic pop +#endif + +// clean up +#undef JSON_ASSERT +#undef JSON_INTERNAL_CATCH +#undef JSON_CATCH +#undef JSON_THROW +#undef JSON_TRY +#undef JSON_PRIVATE_UNLESS_TESTED +#undef JSON_HAS_CPP_11 +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef JSON_HAS_CPP_20 +#undef JSON_HAS_FILESYSTEM +#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL +#undef JSON_EXPLICIT +#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL + +// #include + + +#undef JSON_HEDLEY_ALWAYS_INLINE +#undef JSON_HEDLEY_ARM_VERSION +#undef JSON_HEDLEY_ARM_VERSION_CHECK +#undef JSON_HEDLEY_ARRAY_PARAM +#undef JSON_HEDLEY_ASSUME +#undef JSON_HEDLEY_BEGIN_C_DECLS +#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#undef JSON_HEDLEY_CLANG_HAS_FEATURE +#undef JSON_HEDLEY_CLANG_HAS_WARNING +#undef JSON_HEDLEY_COMPCERT_VERSION +#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#undef JSON_HEDLEY_CONCAT +#undef JSON_HEDLEY_CONCAT3 +#undef JSON_HEDLEY_CONCAT3_EX +#undef JSON_HEDLEY_CONCAT_EX +#undef JSON_HEDLEY_CONST +#undef JSON_HEDLEY_CONSTEXPR +#undef JSON_HEDLEY_CONST_CAST +#undef JSON_HEDLEY_CPP_CAST +#undef JSON_HEDLEY_CRAY_VERSION +#undef JSON_HEDLEY_CRAY_VERSION_CHECK +#undef JSON_HEDLEY_C_DECL +#undef JSON_HEDLEY_DEPRECATED +#undef JSON_HEDLEY_DEPRECATED_FOR +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#undef JSON_HEDLEY_DIAGNOSTIC_POP +#undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#undef JSON_HEDLEY_DMC_VERSION +#undef JSON_HEDLEY_DMC_VERSION_CHECK +#undef JSON_HEDLEY_EMPTY_BASES +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#undef JSON_HEDLEY_END_C_DECLS +#undef JSON_HEDLEY_FLAGS +#undef JSON_HEDLEY_FLAGS_CAST +#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_BUILTIN +#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_EXTENSION +#undef JSON_HEDLEY_GCC_HAS_FEATURE +#undef JSON_HEDLEY_GCC_HAS_WARNING +#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#undef JSON_HEDLEY_GCC_VERSION +#undef JSON_HEDLEY_GCC_VERSION_CHECK +#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#undef JSON_HEDLEY_GNUC_HAS_FEATURE +#undef JSON_HEDLEY_GNUC_HAS_WARNING +#undef JSON_HEDLEY_GNUC_VERSION +#undef JSON_HEDLEY_GNUC_VERSION_CHECK +#undef JSON_HEDLEY_HAS_ATTRIBUTE +#undef JSON_HEDLEY_HAS_BUILTIN +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_HAS_EXTENSION +#undef JSON_HEDLEY_HAS_FEATURE +#undef JSON_HEDLEY_HAS_WARNING +#undef JSON_HEDLEY_IAR_VERSION +#undef JSON_HEDLEY_IAR_VERSION_CHECK +#undef JSON_HEDLEY_IBM_VERSION +#undef JSON_HEDLEY_IBM_VERSION_CHECK +#undef JSON_HEDLEY_IMPORT +#undef JSON_HEDLEY_INLINE +#undef JSON_HEDLEY_INTEL_CL_VERSION +#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#undef JSON_HEDLEY_INTEL_VERSION +#undef JSON_HEDLEY_INTEL_VERSION_CHECK +#undef JSON_HEDLEY_IS_CONSTANT +#undef JSON_HEDLEY_IS_CONSTEXPR_ +#undef JSON_HEDLEY_LIKELY +#undef JSON_HEDLEY_MALLOC +#undef JSON_HEDLEY_MCST_LCC_VERSION +#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#undef JSON_HEDLEY_MESSAGE +#undef JSON_HEDLEY_MSVC_VERSION +#undef JSON_HEDLEY_MSVC_VERSION_CHECK +#undef JSON_HEDLEY_NEVER_INLINE +#undef JSON_HEDLEY_NON_NULL +#undef JSON_HEDLEY_NO_ESCAPE +#undef JSON_HEDLEY_NO_RETURN +#undef JSON_HEDLEY_NO_THROW +#undef JSON_HEDLEY_NULL +#undef JSON_HEDLEY_PELLES_VERSION +#undef JSON_HEDLEY_PELLES_VERSION_CHECK +#undef JSON_HEDLEY_PGI_VERSION +#undef JSON_HEDLEY_PGI_VERSION_CHECK +#undef JSON_HEDLEY_PREDICT +#undef JSON_HEDLEY_PRINTF_FORMAT +#undef JSON_HEDLEY_PRIVATE +#undef JSON_HEDLEY_PUBLIC +#undef JSON_HEDLEY_PURE +#undef JSON_HEDLEY_REINTERPRET_CAST +#undef JSON_HEDLEY_REQUIRE +#undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#undef JSON_HEDLEY_REQUIRE_MSG +#undef JSON_HEDLEY_RESTRICT +#undef JSON_HEDLEY_RETURNS_NON_NULL +#undef JSON_HEDLEY_SENTINEL +#undef JSON_HEDLEY_STATIC_ASSERT +#undef JSON_HEDLEY_STATIC_CAST +#undef JSON_HEDLEY_STRINGIFY +#undef JSON_HEDLEY_STRINGIFY_EX +#undef JSON_HEDLEY_SUNPRO_VERSION +#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#undef JSON_HEDLEY_TINYC_VERSION +#undef JSON_HEDLEY_TINYC_VERSION_CHECK +#undef JSON_HEDLEY_TI_ARMCL_VERSION +#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL2000_VERSION +#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL430_VERSION +#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL6X_VERSION +#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL7X_VERSION +#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CLPRU_VERSION +#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#undef JSON_HEDLEY_TI_VERSION +#undef JSON_HEDLEY_TI_VERSION_CHECK +#undef JSON_HEDLEY_UNAVAILABLE +#undef JSON_HEDLEY_UNLIKELY +#undef JSON_HEDLEY_UNPREDICTABLE +#undef JSON_HEDLEY_UNREACHABLE +#undef JSON_HEDLEY_UNREACHABLE_RETURN +#undef JSON_HEDLEY_VERSION +#undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#undef JSON_HEDLEY_VERSION_DECODE_MINOR +#undef JSON_HEDLEY_VERSION_DECODE_REVISION +#undef JSON_HEDLEY_VERSION_ENCODE +#undef JSON_HEDLEY_WARNING +#undef JSON_HEDLEY_WARN_UNUSED_RESULT +#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#undef JSON_HEDLEY_FALL_THROUGH + + + +#endif // INCLUDE_NLOHMANN_JSON_HPP_ \ No newline at end of file diff --git a/3rd_party/jsonrpcpp-1.1.1/.gitignore b/3rd_party/jsonrpcpp-1.1.1/.gitignore new file mode 100644 index 00000000..028ce9b5 --- /dev/null +++ b/3rd_party/jsonrpcpp-1.1.1/.gitignore @@ -0,0 +1,33 @@ +# 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 + +jsonrpctest +.vscode +build diff --git a/3rd_party/jsonrpcpp-1.1.1/.travis.yml b/3rd_party/jsonrpcpp-1.1.1/.travis.yml new file mode 100644 index 00000000..36b696ac --- /dev/null +++ b/3rd_party/jsonrpcpp-1.1.1/.travis.yml @@ -0,0 +1,29 @@ +language: cpp +dist: trusty +sudo: required +group: edge + +compiler: + - gcc +matrix: + include: + # build on ubuntu + - os: linux + compiler: gcc + env: + - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-4.9 + # build on osx + - os: osx + osx_image: xcode9.1 +before_install: + - eval "${MATRIX_EVAL}" +script: + - mkdir build + - cd build + - cmake .. && make diff --git a/3rd_party/jsonrpcpp-1.1.1/CMakeLists.txt b/3rd_party/jsonrpcpp-1.1.1/CMakeLists.txt new file mode 100644 index 00000000..b01441a2 --- /dev/null +++ b/3rd_party/jsonrpcpp-1.1.1/CMakeLists.txt @@ -0,0 +1,65 @@ +cmake_minimum_required(VERSION 3.0.0) + +project(jsonrpcpp VERSION 1.1.1 LANGUAGES CXX) +set(PROJECT_DESCRIPTION "C++ JSON-RPC 2.0 library") +set(PROJECT_URL "https://github.com/badaix/jsonrpcpp") + +option(BUILD_SHARED_LIBS "Build jsonrpcpp as a shared library" ON) +option(BUILD_STATIC_LIBS "Build jsonrpcpp as a static library" ON) +option(BUILD_TESTS "Build tests (run tests with make test)" ON) + +if (NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS) + message(FATAL_ERROR "One or both of BUILD_SHARED_LIBS or BUILD_STATIC_LIBS" + "must be set to ON to build") +endif() + +if(NOT DEFINED CMAKE_INSTALL_LIBDIR) + SET(CMAKE_INSTALL_LIBDIR lib CACHE PATH "Output directory for libraries") +endif() + +if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR) + SET(CMAKE_INSTALL_INCLUDEDIR include CACHE + PATH "Output directory for header files") +endif() + +include_directories(lib) +set(JSONRPCPP_SOURCES lib/jsonrp.cpp) + +if (BUILD_SHARED_LIBS) + add_library(jsonrpcpp SHARED "${JSONRPCPP_SOURCES}") + target_compile_features(jsonrpcpp PUBLIC cxx_std_11) + if(WIN32) + install(TARGETS jsonrpcpp RUNTIME DESTINATION "${CMAKE_INSTALL_LIBDIR}") + else() + install(TARGETS jsonrpcpp LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") + endif() + +endif (BUILD_SHARED_LIBS) + +if (BUILD_STATIC_LIBS) + add_library(jsonrpcpp-static STATIC "${JSONRPCPP_SOURCES}") + set_target_properties(jsonrpcpp-static PROPERTIES OUTPUT_NAME jsonrpcpp) + target_compile_features(jsonrpcpp-static PUBLIC cxx_std_11) + install(TARGETS jsonrpcpp-static ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") +endif (BUILD_STATIC_LIBS) + +if (BUILD_TESTS) + if (NOT BUILD_STATIC_LIBS) + message(FATAL_ERROR "Tests can only be built against static libraries " + "(set BUILD_STATIC_LIBS=ON)") + endif (NOT BUILD_STATIC_LIBS) + add_executable(jsonrpctest jsonrpctest.cpp) + target_link_libraries(jsonrpctest jsonrpcpp-static) + target_compile_features(jsonrpctest PUBLIC cxx_std_11) +endif (BUILD_TESTS) + +install(FILES lib/jsonrp.hpp lib/json.hpp + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/jsonrpcpp") + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/jsonrpcpp.pc.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/jsonrpcpp.pc" + @ONLY) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/jsonrpcpp.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") diff --git a/3rd_party/jsonrpcpp-1.1.1/LICENSE b/3rd_party/jsonrpcpp-1.1.1/LICENSE new file mode 100644 index 00000000..ce4771ad --- /dev/null +++ b/3rd_party/jsonrpcpp-1.1.1/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Johannes Pohl + +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. diff --git a/3rd_party/jsonrpcpp-1.1.1/Makefile b/3rd_party/jsonrpcpp-1.1.1/Makefile new file mode 100644 index 00000000..8c9fad0d --- /dev/null +++ b/3rd_party/jsonrpcpp-1.1.1/Makefile @@ -0,0 +1,19 @@ +BIN = jsonrpctest + +CXX = clang++ +STRIP = strip +CXXFLAGS = -std=c++0x -Wall -O3 -Ilib -isystem lib/externals + +OBJ = jsonrpctest.o lib/jsonrp.o + + +all: $(OBJ) + $(CXX) $(CXXFLAGS) -o $(BIN) $(OBJ) $(LDFLAGS) + $(STRIP) $(BIN) + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $< -o $@ + +clean: + rm -rf $(BIN) $(OBJ) *~ + diff --git a/3rd_party/jsonrpcpp-1.1.1/README.md b/3rd_party/jsonrpcpp-1.1.1/README.md new file mode 100644 index 00000000..d67d66d7 --- /dev/null +++ b/3rd_party/jsonrpcpp-1.1.1/README.md @@ -0,0 +1,75 @@ +# jsonrpc++ + +C++ [JSON-RPC 2.0](http://www.jsonrpc.org/specification) library + +[![Build Status](https://travis-ci.org/badaix/jsonrpcpp.svg?branch=master)](https://travis-ci.org/badaix/jsonrpcpp) +[![Github Releases](https://img.shields.io/github/release/badaix/jsonrpcpp.svg)](https://github.com/badaix/jsonrpcpp/releases) + +When grown up, this will be a leightweight JSON-RPC 2.0 C++ library. + +### What it is +jsonrpc++ parses and constructs JSON RPC 2.0 objects, like +* [Request](http://www.jsonrpc.org/specification#request_object) + * [Notification](http://www.jsonrpc.org/specification#notification) + * [Parameter](http://www.jsonrpc.org/specification#parameter_structures) +* [Response](http://www.jsonrpc.org/specification#response_object) + * [Error](http://www.jsonrpc.org/specification#error_object) +* [Batch](http://www.jsonrpc.org/specification#batch) + + +#### Example: Parsing a request +````c++ +jsonrpcpp::entity_ptr entity = jsonrpcpp::Parser::parse(R"({"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3})"); +if (entity->is_request()) +{ + jsonrpcpp::request_ptr request = dynamic_pointer_cast(entity); + if (request->method == "subtract") + { + int result = request->params.get("minuend") - request->params.get("subtrahend"); + jsonrpcpp::Response response(*request, result); + cout << " Response: " << response.to_json().dump() << "\n"; + //will print: {"jsonrpc": "2.0", "result": 19, "id": 3} + } + else + throw jsonrpcpp::MethodNotFoundException(*request); +} +```` + +### What it not is +jsonrpc++ is completely transport agnostic, i.e. it doesn't care about transportation of the messages and there are no TCP client or server components shipped with this lib. + +As JSON backbone [JSON for Modern C++](https://nlohmann.github.io/json/) is used. + + +## Some code +````c++ +jsonrpcpp::entity_ptr entity = jsonrpcpp::Parser::parse(R"({"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3})"); +if (entity && entity->is_request()) +{ + jsonrpcpp::request_ptr request = dynamic_pointer_cast(entity); + cout << " Request: " << request->method << ", id: " << request->id << ", has params: " << !request->params.is_null() << "\n"; + if (request->method == "subtract") + { + int result; + if (request->params.is_array()) + result = request->params.get(0) - request->params.get(1); + else + result = request->params.get("minuend") - request->params.get("subtrahend"); + + jsonrpcpp::Response response(*request, result); + cout << " Response: " << response.to_json().dump() << "\n"; + } + else if (request->method == "sum") + { + int result = 0; + for (const auto& summand: request->params.param_array) + result += summand.get(); + jsonrpcpp::Response response(*request, result); + cout << " Response: " << response.to_json().dump() << "\n"; + } + else + { + throw jsonrpcpp::MethodNotFoundException(*request); + } +} + ```` diff --git a/3rd_party/jsonrpcpp-1.1.1/cmake/jsonrpcpp.pc.cmake b/3rd_party/jsonrpcpp-1.1.1/cmake/jsonrpcpp.pc.cmake new file mode 100644 index 00000000..32c10818 --- /dev/null +++ b/3rd_party/jsonrpcpp-1.1.1/cmake/jsonrpcpp.pc.cmake @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: @PROJECT_NAME@ +Description: @PROJECT_DESCRIPTION@ +URL: @PROJECT_URL@ +Version: @PROJECT_VERSION@ + +Libs: -L${libdir} -ljsonrpcpp +Cflags: -I${includedir} diff --git a/3rd_party/jsonrpcpp-1.1.1/jsonrpctest.cpp b/3rd_party/jsonrpcpp-1.1.1/jsonrpctest.cpp new file mode 100644 index 00000000..f4855dc4 --- /dev/null +++ b/3rd_party/jsonrpcpp-1.1.1/jsonrpctest.cpp @@ -0,0 +1,244 @@ +/*** + This file is part of jsonrpc++ + Copyright (C) 2017 Johannes Pohl + + This software may be modified and distributed under the terms + of the MIT license. See the LICENSE file for details. +***/ + + +#include +#include "jsonrp.hpp" + +using namespace std; + + +jsonrpcpp::Parser parser; + + +jsonrpcpp::Response getRespone(jsonrpcpp::request_ptr request) +{ + //cout << " Request: " << request->method << ", id: " << request->id << ", has params: " << !request->params.is_null() << "\n"; + if (request->method == "subtract") + { + if (request->params) + { + int result; + if (request->params.is_array()) + result = request->params.get(0) - request->params.get(1); + else + result = request->params.get("minuend") - request->params.get("subtrahend"); + + return jsonrpcpp::Response(*request, result); + } + else + throw jsonrpcpp::InvalidParamsException(*request); + } + else if (request->method == "sum") + { + int result = 0; + for (const auto& summand: request->params.param_array) + result += summand.get(); + return jsonrpcpp::Response(*request, result); + } + else if (request->method == "get_data") + { + return jsonrpcpp::Response(*request, Json({"hello", 5})); + } + else + { + throw jsonrpcpp::MethodNotFoundException(*request); + } +} + + + +void test(const std::string& json_str) +{ + try + { + cout << "--> " << json_str << "\n"; + jsonrpcpp::entity_ptr entity = parser.parse(json_str); + if (entity) + { + //cout << " Json: " << entity->to_json().dump() << "\n"; + if (entity->is_response()) + { + cout << "<-- " << entity->to_json().dump() << "\n"; + } + if (entity->is_request()) + { + jsonrpcpp::Response response = getRespone(dynamic_pointer_cast(entity)); + cout << "<-- " << response.to_json().dump() << "\n"; + } + else if (entity->is_notification()) + { + jsonrpcpp::notification_ptr notification = dynamic_pointer_cast(entity); + cout << "Notification: " << notification->method << ", has params: " << !notification->params.is_null() << "\n"; + } + else if (entity->is_batch()) + { + jsonrpcpp::batch_ptr batch = dynamic_pointer_cast(entity); + jsonrpcpp::Batch responseBatch; + //cout << " Batch\n"; + for (const auto& batch_entity: batch->entities) + { + //cout << batch_entity->type_str() << ": \t" << batch_entity->to_json() << "\n"; + if (batch_entity->is_request()) + { + try + { + jsonrpcpp::Response response = getRespone(dynamic_pointer_cast(batch_entity)); + responseBatch.add(response); // + } + catch(const jsonrpcpp::RequestException& e) + { + responseBatch.add(e); // + } + } + else if (batch_entity->is_exception()) + { + responseBatch.add_ptr(batch_entity); + } + else if (batch_entity->is_error()) + { + jsonrpcpp::error_ptr error = dynamic_pointer_cast(batch_entity); + responseBatch.add(jsonrpcpp::RequestException(*error)); + } + } + if (!responseBatch.entities.empty()) + cout << "<-- " << responseBatch.to_json().dump() << "\n"; + } + } + } + catch(const jsonrpcpp::RequestException& e) + { + cout << "<-- " << e.to_json().dump() << "\n"; + //cout << " Response: " << jsonrpcpp::Response(e).to_json().dump() << "\n"; + //cerr << "RequestException: " << e.what() << "\n"; + } + catch(const jsonrpcpp::ParseErrorException& e) + { + cout << "<-- " << e.to_json().dump() << "\n"; + } + catch(const jsonrpcpp::RpcException& e) + { + cerr << "RpcException: " << e.what() << "\n"; + cout << "<-- " << jsonrpcpp::ParseErrorException(e.what()).to_json().dump() << "\n"; + } + catch(const std::exception& e) + { + cerr << "Exception: " << e.what() << "\n"; + } + cout << "\n"; +} + + +void test(const jsonrpcpp::Entity& entity) +{ + test(entity.to_json().dump()); +} + + +void update(const jsonrpcpp::Parameter& params) +{ + cout << "Notification callback: update, has params: " << !params.is_null() << "\n"; +} + +/* +void foobar(const jsonrpcpp::Notification& notification, const jsonrpcpp::Parameter& params) +{ + cout << "Notification callback: " << notification.method << ", has params: " << !notification.params.is_null() << "\n"; +} +*/ + +void foobar(const jsonrpcpp::Parameter& params) +{ + cout << "Notification callback: foobar, has params: " << !params.is_null() << "\n"; +} + + +jsonrpcpp::response_ptr sum(const jsonrpcpp::Id& id, const jsonrpcpp::Parameter& params) +{ + int result = 0; + for (const auto& summand: params.param_array) + result += summand.get(); + cout << "Request callback: sum, result: " << result << "\n"; + return make_shared(id, result); +} + + +//examples taken from: http://www.jsonrpc.org/specification#examples +int main(int argc, char* argv[]) +{ + parser.register_notification_callback("update", update); + parser.register_notification_callback("foobar", foobar); + parser.register_request_callback("sum", sum); + + cout << "rpc call with positional parameters:\n\n"; + test(R"({"jsonrpc": "2.0", "method": "sum", "params": [1, 2, 3, 4, 5], "id": 1})"); + test(jsonrpcpp::Request(1, "sum", Json({1, 2, 3, 4, 5}))); + + test(R"({"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1})"); + test(jsonrpcpp::Request(1, "subtract", Json({42, 23}))); + test(R"({"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2})"); + test(jsonrpcpp::Request(2, "subtract", Json({23, 42}))); + + cout << "\n\nrpc call with named parameters:\n\n"; + test(R"({"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3})"); + test(jsonrpcpp::Request(3, "subtract", Json({{"subtrahend", 23}, {"minuend", 42}}))); + test(R"({"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4})"); + test(jsonrpcpp::Request(4, "subtract", Json({{"minuend", 42}, {"subtrahend", 23}}))); + + cout << "\n\na Notification:\n\n"; + test(R"({"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]})"); + test(jsonrpcpp::Notification("update", Json({1, 2, 3, 4, 5}))); + test(R"({"jsonrpc": "2.0", "method": "foobar"})"); + test(jsonrpcpp::Notification("foobar")); + + cout << "\n\nrpc call of non-existent method:\n\n"; + test(R"({"jsonrpc": "2.0", "method": "foobar", "id": "1"})"); + test(jsonrpcpp::Request("1", "foobar")); + + cout << "\n\nrpc call with invalid JSON:\n\n"; + test(R"({"jsonrpc": "2.0", "method": "foobar, "params": "bar", "baz])"); + + cout << "\n\nrpc call with invalid Request object:\n\n"; + test(R"({"jsonrpc": "2.0", "method": 1, "params": "bar"})"); + + cout << "\n\nrpc call Batch, invalid JSON:\n\n"; + test(R"( [ + {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}, + {"jsonrpc": "2.0", "method" + ])"); + + cout << "\n\nrpc call with an empty Array:\n\n"; + test(R"([])"); + + cout << "\n\nrpc call with an invalid Batch (but not empty):\n\n"; + test(R"([1])"); + + cout << "\n\nrpc call with invalid Batch:\n\n"; + test(R"([1,2,3])"); + + cout << "\n\nrpc call Batch:\n\n"; + test(R"( [ + {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}, + {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]}, + {"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"}, + {"foo": "boo"}, + {"jsonrpc": "2.0", "method": 1, "params": "bar"}, + {"jsonrpc": "2.0", "method": 1, "params": "bar", "id": 4}, + {"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"}, + {"jsonrpc": "2.0", "method": "get_data", "id": "9"} + ])"); + + cout << "\n\nrpc call Batch (all notifications):\n\n"; + test(R"( [ + {"jsonrpc": "2.0", "method": "notify_sum", "params": [1,2,4]}, + {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]} + ])"); + +} + + diff --git a/3rd_party/jsonrpcpp-1.1.1/lib/jsonrp.cpp b/3rd_party/jsonrpcpp-1.1.1/lib/jsonrp.cpp new file mode 100644 index 00000000..29b6938c --- /dev/null +++ b/3rd_party/jsonrpcpp-1.1.1/lib/jsonrp.cpp @@ -0,0 +1,1017 @@ +/*** + This file is part of jsonrpc++ + Copyright (C) 2017 Johannes Pohl + + This software may be modified and distributed under the terms + of the MIT license. See the LICENSE file for details. +***/ + +#include "jsonrp.hpp" + + +using namespace std; + +namespace jsonrpcpp +{ + + +/////////////////////////// Entity implementation ///////////////////////////// + +Entity::Entity(entity_t type) : entity(type) +{ +} + + +Entity::~Entity() +{ +} + + +bool Entity::is_exception() +{ + return (entity == entity_t::exception); +} + + +bool Entity::is_id() +{ + return (entity == entity_t::id); +} + + +bool Entity::is_error() +{ + return (entity == entity_t::error); +} + + +bool Entity::is_response() +{ + return (entity == entity_t::response); +} + + +bool Entity::is_request() +{ + return (entity == entity_t::request); +} + + +bool Entity::is_notification() +{ + return (entity == entity_t::notification); +} + + +bool Entity::is_batch() +{ + return (entity == entity_t::batch); +} + + +void Entity::parse(const char* json_str) +{ + // http://www.jsonrpc.org/specification + // code message meaning + // -32700 Parse error Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text. + // -32600 Invalid Request The JSON sent is not a valid Request object. + // -32601 Method not found The method does not exist / is not available. + // -32602 Invalid params Invalid method parameter(s). + // -32603 Internal error Internal JSON-RPC error. + // -32000 to -32099 Server error Reserved for implementation-defined server-errors. + try + { + parse_json(Json::parse(json_str)); + } + catch (const RpcException& e) + { + throw; + } + catch (const exception& e) + { + throw ParseErrorException(e.what()); + } +} + + +void Entity::parse(const std::string& json_str) +{ + parse(json_str.c_str()); +} + + +std::string Entity::type_str() const +{ + switch (entity) + { + case entity_t::unknown: + return "unknown"; + case entity_t::id: + return "id"; + case entity_t::exception: + return "exception"; + case entity_t::error: + return "error"; + case entity_t::response: + return "response"; + case entity_t::request: + return "request"; + case entity_t::notification: + return "notification"; + case entity_t::batch: + return "batch"; + default: + return "unknown"; + } +} + + + + + +/////////////////////////// NullableEntity implementation ///////////////////// + +NullableEntity::NullableEntity(entity_t type) : Entity(type), isNull(false) +{ +} + + +NullableEntity::NullableEntity(entity_t type, std::nullptr_t) : Entity(type), isNull(true) +{ +} + + +NullableEntity::~NullableEntity() +{ +}; + + + + + +/////////////////////////// Id implementation ///////////////////////////////// + +Id::Id() : Entity(entity_t::id), type(value_t::null), int_id(0), string_id("") +{ +} + + +Id::Id(int id) : Entity(entity_t::id), type(value_t::integer), int_id(id), string_id("") +{ +} + + +Id::Id(const char* id) : Entity(entity_t::id), type(value_t::string), int_id(0), string_id(id) +{ +} + + +Id::Id(const std::string& id) : Id(id.c_str()) +{ +} + + +Id::Id(const Json& json_id) : Entity(entity_t::id), type(value_t::null) +{ + parse_json(json_id); +} + + +void Id::parse_json(const Json& json) +{ + if (json.is_null()) + { + type = value_t::null; + } + else if (json.is_number_integer()) + { + int_id = json.get(); + type = value_t::integer; + } + else if (json.is_string()) + { + string_id = json.get(); + type = value_t::string; + } + else + throw std::invalid_argument("id must be integer, string or null"); +} + + +Json Id::to_json() const +{ + if (type == value_t::null) + return nullptr; + else if (type == value_t::string) + return string_id; + else if (type == value_t::integer) + return int_id; + + return nullptr; +} + + + + + +//////////////////////// Error implementation ///////////////////////////////// + +Parameter::Parameter(std::nullptr_t) : NullableEntity(entity_t::id, nullptr), type(value_t::null) +{ +} + + +Parameter::Parameter(const Json& json) : NullableEntity(entity_t::id), type(value_t::null) +{ + if (json != nullptr) + parse_json(json); +} + + +Parameter::Parameter(const std::string& key1, const Json& value1, + const std::string& key2, const Json& value2, + const std::string& key3, const Json& value3, + const std::string& key4, const Json& value4) : NullableEntity(entity_t::id), type(value_t::map) +{ + param_map[key1] = value1; + if (!key2.empty()) + param_map[key2] = value2; + if (!key3.empty()) + param_map[key3] = value3; + if (!key4.empty()) + param_map[key4] = value4; +} + + +void Parameter::parse_json(const Json& json) +{ + if (json.is_array()) + { + param_array = json.get>(); + param_map.clear(); + type = value_t::array; + } + else + { + param_map = json.get>(); + param_array.clear(); + type = value_t::map; + } +} + + +Json Parameter::to_json() const +{ + if (type == value_t::array) + return param_array; + else if (type == value_t::map) + return param_map; + else + return nullptr; +} + + +bool Parameter::is_array() const +{ + return type == value_t::array; +} + + +bool Parameter::is_map() const +{ + return type == value_t::map; +} + + +bool Parameter::is_null() const +{ + return isNull; +} + + +bool Parameter::has(const std::string& key) const +{ + if (type != value_t::map) + return false; + return (param_map.find(key) != param_map.end()); +} + + +Json Parameter::get(const std::string& key) const +{ + return param_map.at(key); +} + + +bool Parameter::has(size_t idx) const +{ + if (type != value_t::array) + return false; + return (param_array.size() > idx); +} + + +Json Parameter::get(size_t idx) const +{ + return param_array.at(idx); +} + + + + + +//////////////////////// Error implementation ///////////////////////////////// + +Error::Error(const Json& json) : Error("Internal error", -32603, nullptr) +{ + if (json != nullptr) + parse_json(json); +} + + +Error::Error(std::nullptr_t) : NullableEntity(entity_t::error, nullptr), code(0), message(""), data(nullptr) +{ +} + + +Error::Error(const std::string& message, int code, const Json& data) : NullableEntity(entity_t::error), code(code), message(message), data(data) +{ +} + + +void Error::parse_json(const Json& json) +{ + try + { + if (json.count("code") == 0) + throw RpcException("code is missing"); + code = json["code"]; + if (json.count("message") == 0) + throw RpcException("message is missing"); + message = json["message"]; + if (json.count("data")) + data = json["data"]; + else + data = nullptr; + } + catch (const RpcException& e) + { + throw; + } + catch (const exception& e) + { + throw RpcException(e.what()); + } +} + + +Json Error::to_json() const +{ + Json j = { + {"code", code}, + {"message", message}, + }; + + if (!data.is_null()) + j["data"] = data; + return j; +} + + + + + +////////////////////// Request implementation ///////////////////////////////// + +Request::Request(const Json& json) : Entity(entity_t::request), method(""), id() +{ + if (json != nullptr) + parse_json(json); +} + + +Request::Request(const Id& id, const std::string& method, const Parameter& params) : Entity(entity_t::request), method(method), params(params), id(id) +{ +} + + +void Request::parse_json(const Json& json) +{ + try + { + if (json.count("id") == 0) + throw InvalidRequestException("id is missing"); + + try + { + id = Id(json["id"]); + } + catch(const std::exception& e) + { + throw InvalidRequestException(e.what()); + } + + if (json.count("jsonrpc") == 0) + throw InvalidRequestException("jsonrpc is missing", id); + string jsonrpc = json["jsonrpc"].get(); + if (jsonrpc != "2.0") + throw InvalidRequestException("invalid jsonrpc value: " + jsonrpc, id); + + if (json.count("method") == 0) + throw InvalidRequestException("method is missing", id); + if (!json["method"].is_string()) + throw InvalidRequestException("method must be a string value", id); + method = json["method"]; + if (method.empty()) + throw InvalidRequestException("method must not be empty", id); + + if (json.count("params")) + params.parse_json(json["params"]); + else + params = nullptr; + } + catch (const RequestException& e) + { + throw; + } + catch (const exception& e) + { + throw InternalErrorException(e.what(), id); + } +} + + +Json Request::to_json() const +{ + Json json = { + {"jsonrpc", "2.0"}, + {"method", method}, + {"id", id.to_json()} + }; + + if (params) + json["params"] = params.to_json(); + + return json; +} + + + +RpcException::RpcException(const char* text) +{ + text_ = new char[std::strlen(text) + 1]; + std::strcpy(text_, text); +} + +RpcException::RpcException(const std::string& text) : RpcException(text.c_str()) +{ +} + +RpcException::RpcException(const RpcException& e) : RpcException(e.what()) +{ +} + +RpcException::~RpcException() throw() +{ + delete[] text_; +} + +const char* RpcException::what() const noexcept +{ + return text_; +} + + + + +ParseErrorException::ParseErrorException(const Error& error) : RpcException(error.message), Entity(entity_t::exception), error(error) +{ +} + +ParseErrorException::ParseErrorException(const ParseErrorException& e) : RpcException(e.what()), Entity(entity_t::exception), error(e.error) +{ +} + +ParseErrorException::ParseErrorException(const std::string& data) : ParseErrorException(Error("Parse error", -32700, data)) +{ +} + +Json ParseErrorException::to_json() const +{ + Json response = { + {"jsonrpc", "2.0"}, + {"error", error.to_json()}, + {"id", nullptr} + }; + + return response; +} + +void ParseErrorException::parse_json(const Json& json) +{ +} + + + + +RequestException::RequestException(const Error& error, const Id& requestId) : RpcException(error.message), Entity(entity_t::exception), error(error), id(requestId) +{ +} + +RequestException::RequestException(const RequestException& e) : RpcException(e.what()), Entity(entity_t::exception), error(e.error), id(e.id) +{ +} + +Json RequestException::to_json() const +{ + Json response = { + {"jsonrpc", "2.0"}, + {"error", error.to_json()}, + {"id", id.to_json()} + }; + + return response; +} + +void RequestException::parse_json(const Json& json) +{ +} + + + + + +InvalidRequestException::InvalidRequestException(const Id& requestId) : RequestException(Error("Invalid request", -32600), requestId) +{ +} + +InvalidRequestException::InvalidRequestException(const Request& request) : InvalidRequestException(request.id) +{ +} + +InvalidRequestException::InvalidRequestException(const char* data, const Id& requestId) : RequestException(Error("Invalid request", -32600, data), requestId) +{ +} + +InvalidRequestException::InvalidRequestException(const std::string& data, const Id& requestId) : InvalidRequestException(data.c_str(), requestId) +{ +} + + + +MethodNotFoundException::MethodNotFoundException(const Id& requestId) : RequestException(Error("Method not found", -32601), requestId) +{ +} + +MethodNotFoundException::MethodNotFoundException(const Request& request) : MethodNotFoundException(request.id) +{ +} + +MethodNotFoundException::MethodNotFoundException(const char* data, const Id& requestId) : RequestException(Error("Method not found", -32601, data), requestId) +{ +} + +MethodNotFoundException::MethodNotFoundException(const std::string& data, const Id& requestId) : MethodNotFoundException(data.c_str(), requestId) +{ +} + + + +InvalidParamsException::InvalidParamsException(const Id& requestId) : RequestException(Error("Invalid params", -32602), requestId) +{ +} + +InvalidParamsException::InvalidParamsException(const Request& request) : InvalidParamsException(request.id) +{ +} + +InvalidParamsException::InvalidParamsException(const char* data, const Id& requestId) : RequestException(Error("Invalid params", -32602, data), requestId) +{ +} + +InvalidParamsException::InvalidParamsException(const std::string& data, const Id& requestId) : InvalidParamsException(data.c_str(), requestId) +{ +} + + + +InternalErrorException::InternalErrorException(const Id& requestId) : RequestException(Error("Internal error", -32603), requestId) +{ +} + +InternalErrorException::InternalErrorException(const Request& request) : InternalErrorException(request.id) +{ +} + +InternalErrorException::InternalErrorException(const char* data, const Id& requestId) : RequestException(Error("Internal error", -32603, data), requestId) +{ +} + +InternalErrorException::InternalErrorException(const std::string& data, const Id& requestId) : InternalErrorException(data.c_str(), requestId) +{ +} + + + +///////////////////// Response implementation ///////////////////////////////// + +Response::Response(const Json& json) : Entity(entity_t::response) +{ + if (json != nullptr) + parse_json(json); +} + + +Response::Response(const Id& id, const Json& result) : Entity(entity_t::response), id(id), result(result), error(nullptr) +{ +} + + +Response::Response(const Id& id, const Error& error) : Entity(entity_t::response), id(id), result(), error(error) +{ +} + + +Response::Response(const Request& request, const Json& result) : Response(request.id, result) +{ +} + + +Response::Response(const Request& request, const Error& error) : Response(request.id, error) +{ +} + + +Response::Response(const RequestException& exception) : Response(exception.id, exception.error) +{ +} + + +void Response::parse_json(const Json& json) +{ + try + { + error = nullptr; + result = nullptr; + if (json.count("jsonrpc") == 0) + throw RpcException("jsonrpc is missing"); + string jsonrpc = json["jsonrpc"].get(); + if (jsonrpc != "2.0") + throw RpcException("invalid jsonrpc value: " + jsonrpc); + if (json.count("id") == 0) + throw RpcException("id is missing"); + id = Id(json["id"]); + if (json.count("result")) + result = json["result"]; + else if (json.count("error")) + error.parse_json(json["error"]); + else + throw RpcException("response must contain result or error"); + } + catch (const RpcException& e) + { + throw; + } + catch (const exception& e) + { + throw RpcException(e.what()); + } +} + + +Json Response::to_json() const +{ + Json j = { + {"jsonrpc", "2.0"}, + {"id", id.to_json()}, + }; + + if (error) + j["error"] = error.to_json(); + else + j["result"] = result; + + return j; +} + + + + + +///////////////// Notification implementation ///////////////////////////////// + +Notification::Notification(const Json& json) : Entity(entity_t::notification) +{ + if (json != nullptr) + parse_json(json); +} + + +Notification::Notification(const char* method, const Parameter& params) : Entity(entity_t::notification), method(method), params(params) +{ +} + + +Notification::Notification(const std::string& method, const Parameter& params) : Notification(method.c_str(), params) +{ +} + + +void Notification::parse_json(const Json& json) +{ + try + { + if (json.count("jsonrpc") == 0) + throw RpcException("jsonrpc is missing"); + string jsonrpc = json["jsonrpc"].get(); + if (jsonrpc != "2.0") + throw RpcException("invalid jsonrpc value: " + jsonrpc); + + if (json.count("method") == 0) + throw RpcException("method is missing"); + if (!json["method"].is_string()) + throw RpcException("method must be a string value"); + method = json["method"]; + if (method.empty()) + throw RpcException("method must not be empty"); + + if (json.count("params")) + params.parse_json(json["params"]); + else + params = nullptr; + } + catch (const RpcException& e) + { + throw; + } + catch (const exception& e) + { + throw RpcException(e.what()); + } +} + + +Json Notification::to_json() const +{ + Json json = { + {"jsonrpc", "2.0"}, + {"method", method}, + }; + + if (params) + json["params"] = params.to_json(); + + return json; +} + + + + + +//////////////////////// Batch implementation ///////////////////////////////// + +Batch::Batch(const Json& json) : Entity(entity_t::batch) +{ + if (json != nullptr) + parse_json(json); +} + + +void Batch::parse_json(const Json& json) +{ +// cout << "Batch::parse: " << json.dump() << "\n"; + entities.clear(); + for (auto it = json.begin(); it != json.end(); ++it) + { +// cout << "x: " << it->dump() << "\n"; + entity_ptr entity(nullptr); + try + { + entity = Parser::do_parse_json(*it); + if (!entity) + entity = make_shared("Invalid Request", -32600); + } + catch(const RequestException& e) + { + entity = make_shared(e); + } + catch(const std::exception& e) + { + entity = make_shared(e.what(), -32600); + } + entities.push_back(entity); + } + if (entities.empty()) + throw InvalidRequestException(); +} + + +Json Batch::to_json() const +{ + Json result; + for (const auto& j: entities) + result.push_back(j->to_json()); + return result; +} + + +/*void Batch::add(const entity_ptr entity) +{ + entities.push_back(entity); +} +*/ + + + + +//////////////////////// Parser implementation //////////////////////////////// + +Parser::Parser() +{ + +} + + +Parser::~Parser() +{ + +} + + +void Parser::register_notification_callback(const std::string& notification, notification_callback callback) +{ + if (callback) + notification_callbacks_[notification] = callback; +} + + +void Parser::register_request_callback(const std::string& request, request_callback callback) +{ + if (callback) + request_callbacks_[request] = callback; +} + + +entity_ptr Parser::parse(const std::string& json_str) +{ + //std::cout << "parse: " << json_str << "\n"; + entity_ptr entity = do_parse(json_str); + if (entity && entity->is_notification()) + { + notification_ptr notification = dynamic_pointer_cast(entity); + if (notification_callbacks_.find(notification->method) != notification_callbacks_.end()) + { + notification_callback callback = notification_callbacks_[notification->method]; + if (callback) + callback(notification->params); + } + } + else if (entity && entity->is_request()) + { + request_ptr request = dynamic_pointer_cast(entity); + if (request_callbacks_.find(request->method) != request_callbacks_.end()) + { + request_callback callback = request_callbacks_[request->method]; + if (callback) + { + jsonrpcpp::response_ptr response = callback(request->id, request->params); + if (response) + return response; + } + } + } + return entity; +} + + +entity_ptr Parser::parse_json(const Json& json) +{ + return do_parse_json(json); +} + + +entity_ptr Parser::do_parse(const std::string& json_str) +{ + try + { + return do_parse_json(Json::parse(json_str)); + } + catch (const RpcException& e) + { + throw; + } + catch (const exception& e) + { + throw ParseErrorException(e.what()); + } + + return nullptr; +} + + +entity_ptr Parser::do_parse_json(const Json& json) +{ + try + { + if (is_request(json)) + return make_shared(json); + else if (is_notification(json)) + return make_shared(json); + else if (is_response(json)) + return make_shared(json); + else if (is_batch(json)) + return make_shared(json); + } + catch (const RpcException& e) + { + throw; + } + catch (const exception& e) + { + throw RpcException(e.what()); + } + + return nullptr; +} + + +bool Parser::is_request(const std::string& json_str) +{ + try + { + return is_request(Json::parse(json_str)); + } + catch (const exception& e) + { + return false; + } +} + + +bool Parser::is_request(const Json& json) +{ + return (json.count("method") && json.count("id")); +} + + +bool Parser::is_notification(const std::string& json_str) +{ + try + { + return is_notification(Json::parse(json_str)); + } + catch (const exception& e) + { + return false; + } +} + + +bool Parser:: is_notification(const Json& json) +{ + return (json.count("method") && (json.count("id") == 0)); +} + + +bool Parser::is_response(const std::string& json_str) +{ + try + { + return is_response(Json::parse(json_str)); + } + catch (const exception& e) + { + return false; + } +} + + +bool Parser::is_response(const Json& json) +{ + return (json.count("result") && json.count("id")); +} + + +bool Parser::is_batch(const std::string& json_str) +{ + try + { + return is_batch(Json::parse(json_str)); + } + catch (const exception& e) + { + return false; + } +} + + +bool Parser::is_batch(const Json& json) +{ + return (json.is_array()); +} + + + +} + + diff --git a/3rd_party/jsonrpcpp-1.1.1/lib/jsonrp.hpp b/3rd_party/jsonrpcpp-1.1.1/lib/jsonrp.hpp new file mode 100644 index 00000000..cdda1d3c --- /dev/null +++ b/3rd_party/jsonrpcpp-1.1.1/lib/jsonrp.hpp @@ -0,0 +1,461 @@ +/*** + __ ____ __ __ _ ____ ____ ___ _ _ + _( )/ ___) / \ ( ( \( _ \( _ \ / __)( ) ( ) + / \) \\___ \( O )/ / ) / ) __/( (__(_ _)(_ _) + \____/(____/ \__/ \_)__)(__\_)(__) \___)(_) (_) + version 1.1.1 + https://github.com/badaix/jsonrpcpp + + This file is part of jsonrpc++ + Copyright (C) 2017 Johannes Pohl + + This software may be modified and distributed under the terms + of the MIT license. See the LICENSE file for details. +***/ + +/// http://patorjk.com/software/taag/#p=display&f=Graceful&t=JSONRPC%2B%2B + +#ifndef JSON_RPC_H +#define JSON_RPC_H + +#include +#include +#include +#include +#include "json.hpp" + + +using Json = nlohmann::json; + +namespace jsonrpcpp +{ + + +class Entity; +class Request; +class Notification; +class Parameter; +class Response; +class Error; +class Batch; + + +typedef std::shared_ptr entity_ptr; +typedef std::shared_ptr request_ptr; +typedef std::shared_ptr notification_ptr; +typedef std::shared_ptr parameter_ptr; +typedef std::shared_ptr response_ptr; +typedef std::shared_ptr error_ptr; +typedef std::shared_ptr batch_ptr; + + + +class Entity +{ +public: + enum class entity_t : uint8_t + { + unknown, + exception, + id, + error, + response, + request, + notification, + batch + }; + + Entity(entity_t type); + virtual ~Entity(); + + bool is_exception(); + bool is_id(); + bool is_error(); + bool is_response(); + bool is_request(); + bool is_notification(); + bool is_batch(); + + virtual std::string type_str() const; + + virtual Json to_json() const = 0; + virtual void parse_json(const Json& json) = 0; + + virtual void parse(const std::string& json_str); + virtual void parse(const char* json_str); + +protected: + entity_t entity; +}; + + + + + +class NullableEntity : public Entity +{ +public: + NullableEntity(entity_t type); + NullableEntity(entity_t type, std::nullptr_t); + virtual ~NullableEntity(); +#ifdef _MSC_VER + virtual operator bool() const +#else + virtual explicit operator bool() const +#endif + { + return !isNull; + } + +protected: + bool isNull; +}; + + + + + +class Id : public Entity +{ +public: + enum class value_t : uint8_t + { + null, + string, + integer + }; + + Id(); + Id(int id); + Id(const char* id); + Id(const std::string& id); + Id(const Json& json_id); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); + + friend std::ostream& operator<< (std::ostream &out, const Id &id) + { + out << id.to_json(); + return out; + } + + value_t type; + int int_id; + std::string string_id; +}; + + + + + +class Parameter : public NullableEntity +{ +public: + enum class value_t : uint8_t + { + null, + array, + map + }; + + Parameter(std::nullptr_t); + Parameter(const Json& json = nullptr); + Parameter(const std::string& key1, const Json& value1, + const std::string& key2 = "", const Json& value2 = nullptr, + const std::string& key3 = "", const Json& value3 = nullptr, + const std::string& key4 = "", const Json& value4 = nullptr); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); + + bool is_array() const; + bool is_map() const; + bool is_null() const; + + Json get(const std::string& key) const; + Json get(size_t idx) const; + bool has(const std::string& key) const; + bool has(size_t idx) const; + + template + T get(const std::string& key) const + { + return get(key).get(); + } + + template + T get(size_t idx) const + { + return get(idx).get(); + } + + template + T get(const std::string& key, const T& default_value) const + { + if (!has(key)) + return default_value; + else + return get(key); + } + + template + T get(size_t idx, const T& default_value) const + { + if (!has(idx)) + return default_value; + else + return get(idx); + } + + value_t type; + std::vector param_array; + std::map param_map; +}; + + + + + +class Error : public NullableEntity +{ +public: + Error(const Json& json = nullptr); + Error(std::nullptr_t); + Error(const std::string& message, int code, const Json& data = nullptr); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); + + int code; + std::string message; + Json data; +}; + + + +/// JSON-RPC 2.0 request +/** + * Simple jsonrpc 2.0 parser with getters + * Currently no named parameters are supported, but only array parameters + */ +class Request : public Entity +{ +public: + Request(const Json& json = nullptr); + Request(const Id& id, const std::string& method, const Parameter& params = nullptr); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); + + std::string method; + Parameter params; + Id id; +}; + + + + + +class RpcException : public std::exception +{ + char* text_; +public: + RpcException(const char* text); + RpcException(const std::string& text); + RpcException(const RpcException& e); + + virtual ~RpcException() throw(); + virtual const char* what() const noexcept; +}; + + + + + +class ParseErrorException : public RpcException, public Entity +{ +public: + Error error; + + ParseErrorException(const Error& error); + ParseErrorException(const ParseErrorException& e); + ParseErrorException(const std::string& data); + virtual Json to_json() const; + +protected: + virtual void parse_json(const Json& json); +}; + + + +// -32600 Invalid Request The JSON sent is not a valid Request object. +// -32601 Method not found The method does not exist / is not available. +// -32602 Invalid params Invalid method parameter(s). +// -32603 Internal error Internal JSON-RPC error. + +class RequestException : public RpcException, public Entity +{ +public: + Error error; + Id id; + + RequestException(const Error& error, const Id& requestId = Id()); + RequestException(const RequestException& e); + virtual Json to_json() const; + +protected: + virtual void parse_json(const Json& json); +}; + + + +class InvalidRequestException : public RequestException +{ +public: + InvalidRequestException(const Id& requestId = Id()); + InvalidRequestException(const Request& request); + InvalidRequestException(const char* data, const Id& requestId = Id()); + InvalidRequestException(const std::string& data, const Id& requestId = Id()); +}; + + + +class MethodNotFoundException : public RequestException +{ +public: + MethodNotFoundException(const Id& requestId = Id()); + MethodNotFoundException(const Request& request); + MethodNotFoundException(const char* data, const Id& requestId = Id()); + MethodNotFoundException(const std::string& data, const Id& requestId = Id()); +}; + + + +class InvalidParamsException : public RequestException +{ +public: + InvalidParamsException(const Id& requestId = Id()); + InvalidParamsException(const Request& request); + InvalidParamsException(const char* data, const Id& requestId = Id()); + InvalidParamsException(const std::string& data, const Id& requestId = Id()); +}; + + + +class InternalErrorException : public RequestException +{ +public: + InternalErrorException(const Id& requestId = Id()); + InternalErrorException(const Request& request); + InternalErrorException(const char* data, const Id& requestId = Id()); + InternalErrorException(const std::string& data, const Id& requestId = Id()); +}; + + + + + +class Response : public Entity +{ +public: + Id id; + Json result; + Error error; + + Response(const Json& json = nullptr); + Response(const Id& id, const Json& result); + Response(const Id& id, const Error& error); + Response(const Request& request, const Json& result); + Response(const Request& request, const Error& error); + Response(const RequestException& exception); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); +}; + + + + + +class Notification : public Entity +{ +public: + std::string method; + Parameter params; + Notification(const Json& json = nullptr); + Notification(const char* method, const Parameter& params = nullptr); + Notification(const std::string& method, const Parameter& params); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); +}; + + + + +typedef std::function notification_callback; +typedef std::function request_callback; + + +class Parser +{ +public: + Parser(); + virtual ~Parser(); + + entity_ptr parse(const std::string& json_str); + entity_ptr parse_json(const Json& json); + + void register_notification_callback(const std::string& notification, notification_callback callback); + void register_request_callback(const std::string& request, request_callback callback); + + static entity_ptr do_parse(const std::string& json_str); + static entity_ptr do_parse_json(const Json& json); + static bool is_request(const std::string& json_str); + static bool is_request(const Json& json); + static bool is_notification(const std::string& json_str); + static bool is_notification(const Json& json); + static bool is_response(const std::string& json_str); + static bool is_response(const Json& json); + static bool is_batch(const std::string& json_str); + static bool is_batch(const Json& json); + +private: + std::map notification_callbacks_; + std::map request_callbacks_; +}; + + + + + +class Batch : public Entity +{ +public: + std::vector entities; + + Batch(const Json& json = nullptr); + + virtual Json to_json() const; + virtual void parse_json(const Json& json); + + template + void add(const T& entity) + { + entities.push_back(std::make_shared(entity)); + } + + void add_ptr(const entity_ptr& entity) + { + entities.push_back(entity); + } +}; + + + +} //namespace jsonrpc + + + +#endif diff --git a/3rd_party/libhttpserver-0.18.2/.github/ISSUE_TEMPLATE/bug_report.md b/3rd_party/libhttpserver-0.18.2/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..c121c157 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,56 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG] Title" +labels: bug +assignees: etr + +--- + + + +### Prerequisites + +* [ ] Put an X between the brackets on this line if you have checked that your issue isn't already filed: https://github.com/search?l=&q=repo%3Aetr%2Flibhttpserver&type=Issues + +### Description + +[Description of the issue] + +### Steps to Reproduce + +1. [First Step] +2. [Second Step] +3. [and so on...] + +**Expected behavior:** [What you expect to happen] + +**Actual behavior:** [What actually happens] + +**Reproduces how often:** [What percentage of the time does it reproduce?] + +### Versions + +* OS version (if on linux, the output of "uname -a") +* libhttpserver version (please specify whether compiled or packaged) +* libmicrohttpd version (please specify whether compiled or packaged) + +If you have problems during build: +* Compiler version +* autotools version + +### Additional Information + +Any additional information, configuration (especially build configuration flags if you compiled the libraries) or data that might be necessary to reproduce the issue. + +If you have problems during build, please attach your config.log and the full scope of your error from make. + +If you have problems at execution, please: +* attach the stacktrace in case of crash (a coredump would be even better). +* provide a main that reproduces the error. diff --git a/3rd_party/libhttpserver-0.18.2/.github/ISSUE_TEMPLATE/feature_request.md b/3rd_party/libhttpserver-0.18.2/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..a9833cf0 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: feature-request +assignees: etr + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe why the feature or enhancement you are proposing fits the library.** +A clear and concise explanation of the reason it fits into the library's mission. + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/3rd_party/libhttpserver-0.18.2/.github/PULL_REQUEST_TEMPLATE/bug_fix.md b/3rd_party/libhttpserver-0.18.2/.github/PULL_REQUEST_TEMPLATE/bug_fix.md new file mode 100644 index 00000000..edcbbff7 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/.github/PULL_REQUEST_TEMPLATE/bug_fix.md @@ -0,0 +1,59 @@ +### Requirements for Contributing a Bug Fix + +* Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. +* The pull request must only fix an existing bug. To contribute other changes, you must use a different template. You can see all templates at https://github.com/etr/libhttpserver/tree/master/.github/PULL_REQUEST_TEMPLATE. +* The pull request must update the test suite to demonstrate the changed functionality. +* After you create the pull request, all status checks must be pass before a maintainer reviews your contribution. For more details, please see https://github.com/etr/libhttpserver/tree/master/CONTRIBUTING.md#pull-requests. + +### Identify the Bug + + + +### Description of the Change + + + +### Alternate Designs + + + +### Possible Drawbacks + + + +### Verification Process + + + +### Release Notes + + diff --git a/3rd_party/libhttpserver-0.18.2/.github/PULL_REQUEST_TEMPLATE/documentation.md b/3rd_party/libhttpserver-0.18.2/.github/PULL_REQUEST_TEMPLATE/documentation.md new file mode 100644 index 00000000..2fc5a29e --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/.github/PULL_REQUEST_TEMPLATE/documentation.md @@ -0,0 +1,30 @@ +### Requirements for Contributing Documentation + +* Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. +* The pull request must only contribute documentation (for example, markdown files or API docs). To contribute other changes, you must use a different template. You can see all templates at https://github.com/etr/libhttpserver/tree/master/.github/PULL_REQUEST_TEMPLATE. + +### Description of the Change + + + +### Release Notes + + diff --git a/3rd_party/libhttpserver-0.18.2/.github/PULL_REQUEST_TEMPLATE/feature_change.md b/3rd_party/libhttpserver-0.18.2/.github/PULL_REQUEST_TEMPLATE/feature_change.md new file mode 100644 index 00000000..826d72cc --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/.github/PULL_REQUEST_TEMPLATE/feature_change.md @@ -0,0 +1,62 @@ +### Requirements for Adding, Changing, or Removing a Feature + +* Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. +* The pull request must contribute a change that has been endorsed by the maintainer team. See details in the template below. +* The pull request must update the test suite to exercise the updated functionality. +* After you create the pull request, all status checks must be pass before a maintainer reviews your contribution. For more details, please see https://github.com/etr/libhttpserver/tree/master/CONTRIBUTING.md#pull-requests. + +### Issue or RFC Endorsed by Maintainers + + + +### Description of the Change + + + +### Alternate Designs + + + +### Possible Drawbacks + + + +### Verification Process + + + +### Release Notes + + diff --git a/3rd_party/libhttpserver-0.18.2/.github/PULL_REQUEST_TEMPLATE/performance_improvement.md b/3rd_party/libhttpserver-0.18.2/.github/PULL_REQUEST_TEMPLATE/performance_improvement.md new file mode 100644 index 00000000..82ea175f --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/.github/PULL_REQUEST_TEMPLATE/performance_improvement.md @@ -0,0 +1,55 @@ +### Requirements for Contributing a Performance Improvement + +* Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. +* The pull request must only affect performance of existing functionality. To contribute other changes, you must use a different template. You can see all templates at https://github.com/etr/libhttpserver/tree/master/.github/PULL_REQUEST_TEMPLATE. +* After you create the pull request, all status checks must be pass before a maintainer reviews your contribution. For more details, please see https://github.com/etr/libhttpserver/tree/master/CONTRIBUTING.md#pull-requests. + +### Description of the Change + + + +### Quantitative Performance Benefits + + + +### Possible Drawbacks + + + +### Verification Process + + + +### Applicable Issues + + + +### Release Notes + + diff --git a/3rd_party/libhttpserver-0.18.2/.github/workflows/codeql-analysis.yml b/3rd_party/libhttpserver-0.18.2/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..fb1041db --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/.github/workflows/codeql-analysis.yml @@ -0,0 +1,73 @@ +name: "CodeQL" + +on: + push: + branches: [master] + pull_request: + # The branches below must be a subset of the branches above + branches: [master] + schedule: + - cron: '0 4 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + language: ['cpp'] + # Learn more... + # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + #- name: Autobuild + # uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + - name: Install libmicrohttpd dependency + run: | + curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz ; + tar -xzf libmicrohttpd-0.9.59.tar.gz ; + cd libmicrohttpd-0.9.59 ; + ./configure --disable-examples ; + make ; + sudo make install ; + + - name: Manual steps to build the library + run: | + ./bootstrap ; + ./configure --enable-same-directory-build; + make ; + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/3rd_party/libhttpserver-0.18.2/.gitignore b/3rd_party/libhttpserver-0.18.2/.gitignore new file mode 100644 index 00000000..72c98a64 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/.gitignore @@ -0,0 +1,59 @@ +*.sw* +*.*~ +*.in +*.php +*.pm +*.py +*_wrap.* +*.gcov +*.gcno +*.gcda +*.o +*.lo +*.la +.idea +libhttpserver.iml +build/* +aclocal.m4 +autom4te.cache/ +config.guess +config.sub +configure +depcomp +install-sh +ltmain.sh +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 +missing +src/core +src/http_request_builder.cpp +src/httpserver/core +src/httpserver/http_request_builder.hpp +src/.deps/ +src/.libs/ +test/Test +test/core +test/err +test/test.txt +Makefile +src/Makefile +stamp-h1 +test-driver +test/.deps/ +test/Makefile +compile +config.h +config.log +config.status +debian/changelog +debian/control +debian/copyright +debian/libhttpserver-dev.install +debian/libhttpserver.install +debian/rules +redhat/libhttpserver.SPEC +libhttpserver.pc +libtool diff --git a/3rd_party/libhttpserver-0.18.2/.gitmodules b/3rd_party/libhttpserver-0.18.2/.gitmodules new file mode 100644 index 00000000..e69de29b diff --git a/3rd_party/libhttpserver-0.18.2/.travis.yml b/3rd_party/libhttpserver-0.18.2/.travis.yml new file mode 100644 index 00000000..5aa66c2e --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/.travis.yml @@ -0,0 +1,358 @@ +language: cpp +os: + - linux + - osx +compiler: + - gcc + - clang +env: + - DEBUG="debug" COVERAGE="coverage" + - DEBUG="nodebug" COVERAGE="nocoverage" + - LINKING="static" +before_install: + - eval "${MATRIX_EVAL}" + # Installing iwyu manually because clang and iwyu paths won't match on Ubuntu otherwise. + - if [ "$IWYU" = "iwyu" ]; then + CLANG_VERSION=`clang --version | grep version | cut -f3 -d' ' | cut -f1 -d'-'` ; + CLANG_PKG_VERSION=`echo $CLANG_VERSION | cut -f1,2 -d'.'` + CLANG_PREFIX_PATH="/usr/local/clang-${CLANG_VERSION}/lib/clang/${CLANG_VERSION}" ; + CLANG_BIN_PATH="/usr/local/clang-${CLANG_VERSION}/bin" ; + git clone https://github.com/include-what-you-use/include-what-you-use.git ; + cd include-what-you-use ; + echo "$CLANG_PKG_VERSION" | grep '\.[0-9]$' ; + if [ $? -eq 0 ]; then + git checkout clang_${CLANG_PKG_VERSION} ; + else + git checkout clang_${CLANG_PKG_VERSION}.0 ; + fi; + cd .. ; + mkdir build_iwyu ; + cd build_iwyu ; + cmake -G "Unix Makefiles" -DCMAKE_PREFIX_PATH=$CLANG_PREFIX_PATH -DCMAKE_C_COMPILER=$CLANG_BIN_PATH/clang -DCMAKE_CXX_COMPILER=$CLANG_BIN_PATH/clang++ ../include-what-you-use ; + make ; + sudo make install ; + cd .. ; + fi + - export LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" + - export PATH=$PATH:/usr/local/lib + - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib + - export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/usr/local/lib + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install info install-info; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install codecov; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install cppcheck; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-mtune=generic'; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export IPV6_TESTS_ENABLED="true"; fi + - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz + - tar -xzf libmicrohttpd-0.9.59.tar.gz + - cd libmicrohttpd-0.9.59 + - ./configure --disable-examples + - make + - sudo make install + - cd .. + - if [ "$BUILD_TYPE" = "asan" ]; then export CFLAGS='-fsanitize=address'; export CXXLAGS='-fsanitize=address'; export LDFLAGS='-fsanitize=address'; fi + - if [ "$BUILD_TYPE" = "msan" ]; then export CFLAGS='-fsanitize=memory'; export CXXLAGS='-fsanitize=memory'; export LDFLAGS='-fsanitize=memory'; fi + - if [ "$BUILD_TYPE" = "lsan" ]; then export CFLAGS='-fsanitize=leak'; export CXXLAGS='-fsanitize=leak'; export LDFLAGS='-fsanitize=leak'; fi + - if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export CXXLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi + - if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export CXXLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi +install: + - ./bootstrap + - mkdir build + - cd build + - | + if [ "$LINKING" = "static" ]; then + ../configure --enable-static --disable-fastopen; + elif [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ]; then + ../configure --enable-debug --enable-coverage --disable-shared --disable-fastopen; + elif [ "$DEBUG" = "debug" ]; then + ../configure --enable-debug --disable-shared --disable-fastopen; + elif [ "$VALGRIND" = "valgrind" ]; then + ../configure --enable-debug --disable-fastopen --disable-valgrind-helgrind --disable-valgrind-drd --disable-valgrind-sgcheck; + elif [ "$IWYU" = "iwyu" ]; then + ../configure --disable-examples; + else + ../configure --disable-fastopen; + fi + # Make or run iwyu. If running iwyu, check for the result code to be 2 (IWYU always returns an error code, if it is 2, no corrections are necessary). + - | + if [ "$IWYU" = "iwyu" ]; then + make -k CXX='/usr/local/bin/include-what-you-use -Xiwyu --mapping_file=${top_builddir}/../custom_iwyu.imp' CXXFLAGS="-isystem ${CLANG_PREFIX_PATH}/include -std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $CXXFLAGS" ; + if [ $? -ne 2 ]; then + return 1; + fi + else + make; + fi +script: + - if [ "$IWYU" != "iwyu" ]; then make check; cat test/test-suite.log; fi + - if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi; + - if [ "$VALGRIND" = "valgrind" ]; then cat test/test-suite-memcheck.log; fi; + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi + - | + if [ "$PERFORMANCE" = "select" ]; then + cd examples + ./benchmark_select 8080 $(nproc) & + sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext + fi + - | + if [ "$PERFORMANCE" = "nodelay" ]; then + cd examples + ./benchmark_nodelay 8080 $(nproc) & + sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext + fi + - | + if [ "$PERFORMANCE" = "threads" ]; then + cd examples + ./benchmark_threads 8080 & + sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext + fi +after_success: + - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi +matrix: + exclude: + - compiler: clang + env: DEBUG="debug" COVERAGE="coverage" + include: + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + packages: + - clang-3.8 + env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + packages: + - clang-3.8 + env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + packages: + - clang-3.8 + env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + packages: + - clang-3.8 + env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + packages: + - clang-3.8 + env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-5 + env: + - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-6 + env: + - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-8 + env: + - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-9 + env: + - MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - valgrind + - valgrind-dbg + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && VALGRIND=valgrind" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - apache2-utils + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && PERFORMANCE=select" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - apache2-utils + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && PERFORMANCE=nodelay" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - apache2-utils + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && PERFORMANCE=threads" + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.6 + packages: + - clang-3.6 + env: + - MATRIX_EVAL="CC=clang-3.6 && CXX=clang++-3.6" + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.7 + packages: + - clang-3.7 + env: + - MATRIX_EVAL="CC=clang-3.7 && CXX=clang++-3.7" + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + packages: + - clang-3.8 + env: + - MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" + # works on Trusty + - os: linux + addons: + apt: + sources: + - llvm-toolchain-trusty-3.9 + packages: + - clang-3.9 + env: + - MATRIX_EVAL="CC=clang-3.9 && CXX=clang++-3.9" + # works on Trusty + - os: linux + addons: + apt: + packages: + - clang-4.0 + env: + - MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0" + # works on Trusty + - os: linux + addons: + apt: + packages: + - clang-5.0 + env: + - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0" + - os: linux + addons: + apt: + packages: + - clang-6.0 + env: + - MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0" + - os: linux + addons: + apt: + sources: + - llvm-toolchain-xenial-7 + - ubuntu-toolchain-r-test + packages: + - clang-7 + env: + - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" + - os: linux + addons: + apt: + sources: + - llvm-toolchain-xenial-8 + - ubuntu-toolchain-r-test + packages: + - clang-8 + env: + - MATRIX_EVAL="CC=clang-8 && CXX=clang++-8" + - os: linux + addons: + apt: + sources: + - llvm-toolchain-xenial-9 + - ubuntu-toolchain-r-test + - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + packages: + - clang-9 + env: + - MATRIX_EVAL="CC=clang-9 && CXX=clang++-9" + - os: linux + compiler: clang + addons: + apt: + sources: + - llvm-toolchain-xenial-7 + - ubuntu-toolchain-r-test + packages: + - iwyu + - cmake + - llvm-dev + - libclang-dev + env: + - MATRIX_EVAL="IWYU=iwyu" diff --git a/3rd_party/libhttpserver-0.18.2/AUTHORS b/3rd_party/libhttpserver-0.18.2/AUTHORS new file mode 100644 index 00000000..8f314bd1 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/AUTHORS @@ -0,0 +1,44 @@ +- Primary developer: +Sebastiano Merlino (maintainer) + +- Code contributions also came from: +Dario Mazza +Andrea Nicotra +Jeff Waller +Craig Minihan +Guo Xiao +Philipp Claßen +Vitaut Bajaryn +Felipe Zipitría +Steven 'Steve' Kendall +G. Mercat +Thomas Schätzlein + +- Support for building on MinGW/Cygwin systems +Shane Peelar +Dean M. Sands, III + +- Support for building on MaxOsX +Jan Klimke + +- Example of SSL usage and operator<< on http_request and http_response +Chris Love + +- Added proper error handling to tcp socket creation and binding +Marcel Pursche + +- Fixed error management and regex handling +Julian Picht + +- Fix string termination when loading files in memory +martamoreton (Github: https://github.com/martamoreton) + +- Memory leaks +rdiazmartin + +- Cleanup of multiple parts of the code +bcsgh (Github: https://github.com/bcsgh) + +- Management of large uploads +Walter Landry +Jagat diff --git a/3rd_party/libhttpserver-0.18.2/CODE_OF_CONDUCT.md b/3rd_party/libhttpserver-0.18.2/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..565a76c1 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at merlino.sebastiano@gmail.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/3rd_party/libhttpserver-0.18.2/CONTRIBUTING.md b/3rd_party/libhttpserver-0.18.2/CONTRIBUTING.md new file mode 100644 index 00000000..ee018af3 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/CONTRIBUTING.md @@ -0,0 +1,229 @@ +# Contributing to libhttpserver + +:+1::tada: First off, thanks for taking the time to contribute! :tada::+1: + +The following is a set of guidelines for contributing to libhttpserver. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. + +#### Table Of Contents + +[Code of Conduct](#code-of-conduct) + +[I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question) + +[How Can I Contribute?](#how-can-i-contribute) + * [Reporting Bugs](#reporting-bugs) + * [Suggesting Enhancements](#suggesting-enhancements) + * [Your First Code Contribution](#your-first-code-contribution) + * [Pull Requests](#pull-requests) + +[Styleguides](#styleguides) + * [Git Commit Messages](#git-commit-messages) + * [Documentation Styleguide](#documentation-styleguide) + +[Additional Notes](#additional-notes) + * [Issue and Pull Request Labels](#issue-and-pull-request-labels) + +## Code of Conduct + +This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [merlino.sebastiano@gmail.com](mailto:merlino.sebastiano@gmail.com). + +## I don't want to read this whole thing I just have a question!!! + +> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below. + +We have an official community board where the community chimes in with helpful advice if you have questions. + +* [libhttpserver on Gitter](https://gitter.im/libhttpserver/community) + +## How Can I Contribute? + +### Reporting Bugs + +This section guides you through submitting a bug report for libhttpserver. Following these guidelines helps maintainers and the community understand your report :pencil:, reproduce the behavior :computer: :computer:, and find related reports :mag_right:. + +Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](https://github.com/etr/libhttpserver/blob/master/.github/ISSUE_TEMPLATE/bug_report.md), the information it asks for helps us resolve issues faster. + +> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. + +#### Before Submitting A Bug Report + +* **Try to debug the problem** You might be able to find the cause of the problem and fix things yourself. Most importantly, check if you can reproduce the problem in the latest version of libhttpserver (head on github). +* **Perform a [cursory search](https://github.com/search?l=&q=repo%3Aetr%2Flibhttpserver&type=Issues)** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Bug Report? + +Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you followed the steps above, create an issue and provide the following information by filling in [the template](https://github.com/etr/libhttpserver/blob/master/.github/ISSUE_TEMPLATE/bug_report.md). + +Explain the problem and include additional details to help maintainers reproduce the problem: + +* **Use a clear and descriptive title** for the issue to identify the problem. +* **Describe the exact steps which reproduce the problem** in as many details as possible. When listing steps, **don't just say what you did, but explain how you did it**. +* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). +* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. +* **Explain which behavior you expected instead and why.** +* **If you're reporting a crash**, include a crash report with a stack trace from the operating system. Include these in the issue in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines), a [file attachment](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests/), or put it in a [gist](https://gist.github.com/) and provide link to that gist. +* **Consider attaching a simple snipped reproducing the problem. ** +* **If the problem is related to performance or memory**, include a CPU profile capture with your report. + +Provide more context by answering these questions: + +* **Did the problem start happening recently** (e.g. after updating to a new version of libhttpserver) or was this always a problem? +* If the problem started happening recently, **can you reproduce the problem in an older version of libhttpserver?** What's the most recent version in which the problem doesn't happen? You can download older versions of libhttpserver from [the releases page](https://github.com/etr/libhttpserver/releases). +* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. + +Include details about your configuration and environment: + +* **Which version of libhttpserver are you using?** +* **What's the name and version of the OS you're using (e.g. "uname -a" on linux) **? +* **What's the version of libmicrohttpd that you have installed? ** +* **Have you installed the libraries (both libhttpserver and libmicrohttpd) manually or through package manager? ** +* **Which options did you use when compiling? ** +* **What compiler version and version of autotools did you use? ** + +### Feature Requests and Enhancements + +This section guides you through submitting an enhancement suggestion for libhttpserver, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions. + +Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion). Fill in [the template](https://github.com/etr/libhttpserver/blob/master/.github/ISSUE_TEMPLATE/feature_request.md). + +#### Before Submitting An Enhancement Suggestion or a Feature Request + +* **Perform a [cursory search](https://github.com/search?l=&q=repo%3Aetr%2Flibhttpserver&type=Issues)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Feature Request / Enhancement Suggestion? + +Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). Create an issue on that repository and provide the following information: + +* **Use a clear and descriptive title** for the issue to identify the suggestion. +* **Provide a step-by-step description of the suggested enhancement** in as many details as possible. +* **Provide a specific example to demonstrate the new feature**. +* **Describe the current behavior** and **explain which behavior you expected instead** and why. +* **Describe which alternatives you have considered**. +* **Explain why this enhancement would be useful** to most users and **why it fits the mission of the library**. + +### Your First Code Contribution + +Unsure where to begin contributing to libhttpserver? You can start by looking through these `beginner` and `help-wanted` issues: + +* [Beginner issues][beginner] - issues which should only require a few lines of code, and a test or two. +* [Help wanted issues][help-wanted] - issues which should be a bit more involved than `beginner` issues. + +Both issue lists are sorted by total number of comments. While not perfect, number of comments is a reasonable proxy for impact a given change will have. + +### Pull Requests + +The process described here has several goals: + +- Maintain libhttpserver's quality +- Fix problems that are important to users +- Engage the community in working toward the best possible solution +- Enable a sustainable system for maintainers to review contributions + +Please follow these steps to have your contribution considered by the maintainers: + +1. Follow all instructions in [the template](https://github.com/etr/libhttpserver/blob/master/PULL_REQUEST_TEMPLATE.md) +2. Follow the [styleguides](#styleguides) +3. After you submit your pull request, verify that all [status checks](https://help.github.com/articles/about-status-checks/) are passing
What if the status checks are failing?If a status check is failing, and you believe that the failure is unrelated to your change, please leave a comment on the pull request explaining why you believe the failure is unrelated. A maintainer will re-run the status check for you. If we conclude that the failure was a false positive, then we will open an issue to track that problem with our status check suite.
+ +While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted. + +## Styleguides + +### Git Commit Messages + +* Limit the first line to 80 characters or less. +* Add a concise description of what your change does. +* Reference issues and pull requests liberally after the first line. + +### Documentation Styleguide + +* Use [Markdown](https://daringfireball.net/projects/markdown). + +## Additional Notes + +### Issue and Pull Request Labels + +This section lists the labels we use to help us track and manage issues and pull requests. + +[GitHub search](https://help.github.com/articles/searching-issues/) makes it easy to use labels for finding groups of issues or pull requests you're interested in. We encourage you to read about [other search filters](https://help.github.com/articles/searching-issues/) which will help you write more focused queries. + +The labels are loosely grouped by their purpose, but it's not required that every issue have a label from every group or that an issue can't have more than one label from the same group. + +Please open an issue on `etr/libhttpserver` if you have suggestions for new labels. + +#### Type of Issue and Issue State + +| Label name | `etr/libhttpserver` :mag_right: | Description | +| --- | --- | --- | +| `feature-request` | [search][search-libhttpserver-repo-label-feature-request] | Feature requests or enhancements. | +| `bug` | [search][search-libhttpserver-repo-label-bug] | Confirmed bugs or reports that are very likely to be bugs. | +| `question` | [search][search-libhttpserver-repo-label-question] | Questions more than bug reports or feature requests (e.g. how do I do X). | +| `feedback` | [search][search-libhttpserver-repo-label-feedback] | General feedback more than bug reports or feature requests. | +| `help-wanted` | [search][search-libhttpserver-repo-label-help-wanted] | The maintainer would appreciate help from the community in resolving these issues. | +| `beginner` | [search][search-libhttpserver-repo-label-beginner] | Less complex issues which would be good first issues to work on for users who want to contribute to libhttpserver. | +| `more-information-needed` | [search][search-libhttpserver-repo-label-more-information-needed] | More information needs to be collected about these problems or feature requests (e.g. steps to reproduce). | +| `needs-reproduction` | [search][search-libhttpserver-repo-label-needs-reproduction] | Likely bugs, but haven't been reliably reproduced. | +| `blocked` | [search][search-libhttpserver-repo-label-blocked] | Issues blocked on other issues. | +| `duplicate` | [search][search-libhttpserver-repo-label-duplicate] | Issues which are duplicates of other issues, i.e. they have been reported before. | +| `wontfix` | [search][search-libhttpserver-repo-label-wontfix] | The maintainers have decided not to fix these issues for now, either because they're working as intended or for some other reason. | +| `invalid` | [search][search-libhttpserver-repo-label-invalid] | Issues which aren't valid (e.g. user errors). | + +#### Topic Categories + +| Label name | `etr/libhttpserver` :mag_right: | Description | +| --- | --- | --- | +| `windows` | [search][search-libhttpserver-repo-label-windows] | Related to Windows. | +| `linux` | [search][search-libhttpserver-repo-label-linux] | Related to Linux. | +| `mac` | [search][search-libhttpserver-repo-label-mac] | Related to macOS. | +| `travis` | [search][search-libhttpserver-repo-label-travis] | Related to travis and CI in general. | +| `tests` | [search][search-libhttpserver-repo-label-tests] | Related to tests (add tests, fix tests, etc...). | +| `documentation` | [search][search-libhttpserver-repo-label-documentation] | Related to any type of documentation. | +| `performance` | [search][search-libhttpserver-repo-label-performance] | Related to performance. | +| `security` | [search][search-libhttpserver-repo-label-security] | Related to security. | +| `api` | [search][search-libhttpserver-repo-label-api] | Related to libhttpserver's public APIs. | +| `git` | [search][search-libhttpserver-repo-label-git] | Related to Git functionality (e.g. problems with gitignore files or with showing the correct file status). | + +#### Pull Request Labels + +| Label name | `etr/libhttpserver` :mag_right: | Description | +| --- | --- | --- | +| `work-in-progress` | [search][search-libhttpserver-repo-label-work-in-progress] | Pull requests which are still being worked on, more changes will follow. | +| `needs-review` | [search][search-libhttpserver-repo-label-needs-review] | Pull requests which need code review, and approval from maintainers. | +| `under-review` | [search][search-libhttpserver-repo-label-under-review] | Pull requests being reviewed by maintainers. | +| `requires-changes` | [search][search-libhttpserver-repo-label-requires-changes] | Pull requests which need to be updated based on review comments and then reviewed again. | +| `needs-testing` | [search][search-libhttpserver-repo-label-needs-testing] | Pull requests which need manual testing. | + +[search-libhttpserver-repo-label-feature-request]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Afeature-request +[search-libhttpserver-repo-label-bug]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Abug +[search-libhttpserver-repo-label-question]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Aquestion +[search-libhttpserver-repo-label-feedback]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Afeedback +[search-libhttpserver-repo-label-help-wanted]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Ahelp-wanted +[search-libhttpserver-repo-label-beginner]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Abeginner +[search-libhttpserver-repo-label-more-information-needed]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Amore-information-needed +[search-libhttpserver-repo-label-needs-reproduction]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Aneeds-reproduction +[search-libhttpserver-repo-label-triage-help-needed]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Atriage-help-needed +[search-libhttpserver-repo-label-windows]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Awindows +[search-libhttpserver-repo-label-linux]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Alinux +[search-libhttpserver-repo-label-mac]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Amac +[search-libhttpserver-repo-label-travis]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Atravis +[search-libhttpserver-repo-label-tests]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Atests +[search-libhttpserver-repo-label-documentation]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Adocumentation +[search-libhttpserver-repo-label-performance]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Aperformance +[search-libhttpserver-repo-label-security]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Asecurity +[search-libhttpserver-repo-label-api]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Aapi +[search-libhttpserver-repo-label-git]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Agit +[search-libhttpserver-repo-label-blocked]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Ablocked +[search-libhttpserver-repo-label-duplicate]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Aduplicate +[search-libhttpserver-repo-label-wontfix]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Awontfix +[search-libhttpserver-repo-label-invalid]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Ainvalid +[search-libhttpserver-repo-label-build-error]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Abuild-error +[search-libhttpserver-repo-label-installer]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Ainstaller +[search-libhttpserver-repo-label-deprecation-help]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Adeprecation-help +[search-libhttpserver-repo-label-work-in-progress]: https://github.com/search?q=is%3Aopen+is%3Apr+repo%3Aetr%23Flibhttpserver+label%3Awork-in-progress +[search-libhttpserver-repo-label-needs-review]: https://github.com/search?q=is%3Aopen+is%3Apr+repo%3Aetr%23Flibhttpserver+label%3Aneeds-review +[search-libhttpserver-repo-label-under-review]: https://github.com/search?q=is%3Aopen+is%3Apr+repo%3Aetr%23Flibhttpserver+label%3Aunder-review +[search-libhttpserver-repo-label-requires-changes]: https://github.com/search?q=is%3Aopen+is%3Apr+repo%3Aetr%23Flibhttpserver+label%3Arequires-changes +[search-libhttpserver-repo-label-needs-testing]: https://github.com/search?q=is%3Aopen+is%3Apr+repo%3Aetr%23Flibhttpserver+label%3Aneeds-testing + +[beginner]:https://github.com/search?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3Abeginner+label%3Ahelp-wanted+user%3Aetr+sort%3Acomments-desc +[help-wanted]:https://github.com/search?q=is%3Aopen+is%3Aissue+label%3Ahelp-wanted+user%3Aetr+sort%3Acomments-desc+-label%3Abeginner diff --git a/3rd_party/libhttpserver-0.18.2/COPYING.LESSER b/3rd_party/libhttpserver-0.18.2/COPYING.LESSER new file mode 100644 index 00000000..5ab7695a --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/COPYING.LESSER @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/3rd_party/libhttpserver-0.18.2/ChangeLog b/3rd_party/libhttpserver-0.18.2/ChangeLog new file mode 100644 index 00000000..72bbe973 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/ChangeLog @@ -0,0 +1,290 @@ +Sat Jun 6 10:21:05 2020 -0800 + Prevent use of regex in http_endpoint outside of registration which could + allow DOS attacks. + +Sat May 16 07:20:00 2020 -0800 + General performance improvements (reduced use of regex, lazy-building of + post-processor) + General code cleanup + General fixes to the documentation + Fixed support on FreeBSD (added missing headers) + Fixed support for Cygwin + Removed depedency on C regex - now using C++11 regex + +Sat Aug 10 18:34:07 2019 -0800 + Added support for TCP-NODELAY + Changed set_path on http_request to have lazy behavior + +Tue Aug 06 22:22:14 2019 -0800 + Added support for body parsing in DELETE requests. + Added support for PATCH method + +Sat Jan 27 21:59:11 2019 -0800 + libhttpserver now includes set of examples to demonstrate the main capabilities of the library + "examples" are now optionally disabled. + Adds valgrind memcheck to the build system on travis + Travis now tests performance with apache benchmark + Reduced the CPU time spent in normalizing URLs (thus saving ~15% on average per request). + All classes now implement move constructor and move assignment operator + The library now avoids collecting connection properties (headers, arguments, footers, cookies, etc...) unless explicitly asked by the client code. + +Sat Jan 12 00:51:00 2019 -0800 + Removed the support for integrated COMET logic. + Removed the support for caching logic. + Added integ tests. + Changed http_resource interface to use shared_ptr. + Improved interface of the http_response object. + Deprecated http_response_builder object. + +Thu Dec 26 10:00:30 2018 -0800 + Fixed IPV6 parsing logic. + Added tests to support IP parsing, URL parsing and utilities + +Thu Nov 22 20:58:00 2018 -0800 + Solved problem with the server not being able to start on mac os + +Sun Nov 04 19:28:00 2018 -0800 + Moved http_endpoint as a sub-class of webserver. This avoids usage of friends. + +Wed Feb 26 21:31:00 2017 +0000 + Fixed problem with segfault when copying http_response object + +Wed Feb 12 13:14:01 2017 +0000 + Updated to libmicrohttpd 0.9.52 + +Wed Jul 13 02:23:11 2016 +0100 + Fixed problems with large payloads + Fixed memory leak in http_response_ptr + +Tue Dec 29 18:56:31 2015 +0100 + Removed support for event supplier (badly defined, complicated and almost useless) + Eliminated custom selection logic (simplified overall code in webserver.cpp) + Changed comet to use a lock-free implementation + +Sun Dec 27 19:39:01 2015 +0100 + Removed POLL start configuration (THREAD now defaults to POLL or EPOLL on Linux) + Use TCP_FASTOPEN on linux >= 3.6 + +Sat Dec 26 15:08:22 2015 +0100 + Changed http_resource to use classic C++ polymorphism using virtual instead of CRTP + +Fri Jul 17 21:38:54 2015 +0000 + Removed build dependency on pkg-config + +Wed Apr 15 01:40:11 2015 +0000 + Support build on MacOsX + Improved support for CI on travis + Solved bug on event_supplier registering + Solved bug on standardize_url to avoid removing root + Change cycle_callback_ptr so that buffer can be modified + Moved to version 0.9.0 + +Sun Jul 23 02:46:20 2014 +0100 + Support for building on MinGW/Cygwin systems + min libmicrohttpd version moved to 0.9.37 + Moved to version 0.8.0 + +Sat Mar 23 15:22:40 2014 +0100 + Continue the cleanup reducing webserver.cpp responsibilities + Deep work on documentation + Moved to version 0.7.2 + +Sat Jan 25 16:31:03 2014 +0100 + Cleaned-up webserver.cpp code to extract secondary classes + Enforced immutability of webserver class + Enabled library to compile on g++ 4.1.2 + +Wed Oct 31 17:59:40 2012 +0100 + Added parameter in http_response to specify if it needs to be deleted by + WS - Sebastiano Merlino + +Wed Oct 31 14:23:57 2012 +0100 + Changed dependency download method - Sebastiano Merlino + +Wed Oct 31 14:13:49 2012 +0100 + Added dependency to travis - Sebastiano Merlino + +Wed Oct 31 14:07:30 2012 +0100 + Changed travis build path - Sebastiano Merlino + +Wed Oct 31 14:02:59 2012 +0100 + Added travis conf to repo - Sebastiano Merlino + +Tue Oct 30 16:13:10 2012 +0100 + Changed the buggy debian changelog - Sebastiano Merlino + +Tue Oct 30 16:06:26 2012 +0100 + Changed version to v0.5.4 - Sebastiano Merlino + +Tue Oct 30 15:59:45 2012 +0100 + Adjusted debian build rules - Sebastiano Merlino + +Tue Oct 30 12:52:04 2012 +0100 + Changed version to 0.5.3 + Added grow method to http_request - Sebastiano Merlino + +Tue Oct 23 12:46:48 2012 +0200 + Changed version from 0.5.1 to 0.5.2 - Sebastiano Merlino + +Tue Oct 23 12:46:07 2012 +0200 + Changed default log behaviour to print nothing + Added getters and setters for dynamic components of WS - Sebastiano Merlino + +Mon Oct 22 12:13:11 2012 +0200 + Modified version number and changelog in order to prepare tag - Sebastiano Merlino + +Fri Oct 19 17:11:21 2012 +0200 + Added response constructor with byte - Sebastiano Merlino + +Mon Oct 15 11:16:22 2012 +0200 + Removed unuseful dependency from libuuid - Sebastiano Merlino + +Fri Oct 12 15:42:21 2012 +0200 + Solved a bug that made impossible to parse post data - Sebastiano Merlino + +Wed Oct 10 17:19:25 2012 +0200 + Moved to version 0.5.1 - Sebastiano Merlino + +Wed Oct 10 17:16:26 2012 +0200 + Added querystring to request attributes - Sebastiano Merlino + +Fri Oct 5 18:00:38 2012 +0200 + Merge branch 'master' of https://github.com/etr/libhttpserver + Conflicts: + src/webserver.cpp - Sebastiano Merlino + +Fri Oct 5 17:55:42 2012 +0200 + Added -D_REENTRANT to configuration. + Aligned debian changelog. + Added comet capabilities to the server. - Sebastiano Merlino + +Tue Sep 25 00:50:45 2012 +0200 + Solved a bug with print in debug mode - Sebastiano Merlino + +Mon Sep 24 15:29:28 2012 +0200 + Modified webserver in order to accept comet calls + Added ignored patters in gitignore - Sebastiano Merlino + +Sun Sep 23 19:10:28 2012 +0200 + Partially solved undefined symbol in wrappers - Sebastiano Merlino + +Sun Sep 23 19:09:54 2012 +0200 + Avoided the usage of the sole option MHD_USE_POLL - Sebastiano Merlino + +Thu Sep 20 08:47:24 2012 +0200 + Added forgotten modded_request.hpp file - Sebastiano Merlino + +Thu Sep 20 08:46:33 2012 +0200 + Added .gitignore file - Sebastiano Merlino + +Sat Sep 15 13:02:52 2012 +0200 + Moved http_endpoint to details namespace - Sebastiano Merlino + +Sat Sep 15 02:39:47 2012 -0700 + Merge pull request #35 from etr/cflags_for_swig_in_pcfile + add -I${includedir}/httpserver to CFLAGS - Sebastiano Merlino + +Tue Aug 28 16:33:45 2012 +0200 + add -I${includedir}/httpserver to CFLAGS + This make swig file generation easier because HTTPSERVER_CFLAGS can be + directly used in swig file generation. + This fix affect only clients that use swing on their code. - Dario Mazza + +Sun Aug 26 19:03:44 2012 +0200 + Changed version. + Aligned version and dependencies in pc and debian files + Updated debian changelog. - Sebastiano Merlino + +Sun Aug 26 18:55:05 2012 +0200 + Changed visibility of http_endpoint methods to avoid them to be called + by external applications. + Avoided explicit usage of MHD constants in classes interface. + Changed http_resource interface in order to avoid copy-constructor calls + and improve performances. + Changed answer_to_connection method in order to avoid multiple checking + on methods and thus improve performances. + Added a way to register personalized error pages. - Sebastiano Merlino + +Wed Aug 8 17:33:39 2012 +0200 + Removed code repetition in handle_request method - Sebastiano Merlino + +Wed Aug 8 12:31:44 2012 +0200 + Added capability to compile with gcov + Changed infinite loop in ws to use wait conditions + Removed a bug from GET-like method handling - Sebastiano Merlino + +Sun Aug 5 18:26:25 2012 +0200 + Modified in order to parse qs in POST/PUT cases - Sebastiano Merlino + +Fri Aug 3 23:36:14 2012 +0200 + Avoid inclusion of internal headers - Sebastiano Merlino + +Thu Aug 2 00:43:02 2012 +0200 + Changed in order to find libmicrohttpd in system - Sebastiano Merlino + +Thu Jul 26 14:08:47 2012 +0200 + Solved some performance and style issues - Sebastiano Merlino + +Wed Jul 25 18:42:48 2012 +0200 + Merge branch 'master' of github.com:etr/libhttpserver - Sebastiano Merlino + +Wed Jul 25 18:41:45 2012 +0200 + Added some comments to http_endpoint and http_request - Sebastiano Merlino + +Wed Jul 25 08:58:04 2012 -0700 + Merge pull request #29 from etr/libtool_version_number + using m4 to define major,minor and revision number in configure.ac - Sebastiano Merlino + +Wed Jul 25 17:50:05 2012 +0200 + using m4 to define major,minor and revision number in configure.ac and send version number to libtool and AC_INIT - Dario Mazza + +Wed Jul 25 17:10:49 2012 +0200 + Changed in order to solve some problems with deb package and rpm package - Sebastiano Merlino + +Tue Jul 24 16:55:51 2012 -0700 + Merge pull request #28 from etr/debpkg_patch_deps + added parameter used to ignore dependecies during debpkg creation - Sebastiano Merlino + +Wed Jul 25 01:51:52 2012 +0200 + added parameter used to ignore dependecies during debpkg creation - Dario Mazza + +Wed Jul 25 00:42:25 2012 +0200 + Adjusted errors in debian rules - Sebastiano Merlino + +Tue Jul 24 16:37:07 2012 +0200 + Modified rpm build in order to compile it + Lowered required version of libmicrohttpd to 0.9.7 - Sebastiano Merlino + +Tue Jul 24 13:28:38 2012 +0200 + Changed also build default directory for debs - Sebastiano Merlino + +Tue Jul 24 13:22:59 2012 +0200 + Changed rules.in in order to avoid relative paths in deb compile - Sebastiano Merlino + +Mon Jul 23 15:42:33 2012 +0200 + Solved a logical error in http_resource route + Added some debug prints - Sebastiano Merlino + +Sun Jul 22 00:24:04 2012 +0200 + Changed in order to add optional optimizations on ws - Sebastiano Merlino + +Sat Jul 21 17:46:03 2012 +0200 + Changed in order to enhance deb packages generation + Added rpm packages generation - Sebastiano Merlino + +Sat Jul 21 00:43:39 2012 +0200 + adjusted error in changelog - Sebastiano Merlino + +Sat Jul 21 00:41:43 2012 +0200 + Changed in order to include debian package creation to makefile - Sebastiano Merlino + +Fri Jul 20 12:11:30 2012 -0700 + Merge pull request #26 from etr/debpackage + project debianized - Sebastiano Merlino + +Fri Jul 20 21:03:43 2012 +0200 + Merge branch 'master' of github.com:etr/libhttpserver - Sebastiano Merlino + +Fri Jul 20 21:03:24 2012 +0200 + Changed version - Sebastiano Merlino + diff --git a/3rd_party/libhttpserver-0.18.2/INSTALL b/3rd_party/libhttpserver-0.18.2/INSTALL new file mode 100644 index 00000000..97a25409 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/INSTALL @@ -0,0 +1,367 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007, 2008, 2009 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `make -f Makefile.cvs` to create configure file. + + 2. `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 3. Type `make' to compile the package. + + 4. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 5. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 6. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 7. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 8. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 9. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/3rd_party/libhttpserver-0.18.2/LICENSE b/3rd_party/libhttpserver-0.18.2/LICENSE new file mode 100644 index 00000000..5ab7695a --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/3rd_party/libhttpserver-0.18.2/Makefile.am b/3rd_party/libhttpserver-0.18.2/Makefile.am new file mode 100644 index 00000000..a46882fd --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/Makefile.am @@ -0,0 +1,61 @@ +# +# This file is part of libhttpserver +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# not a GNU package. You can remove this line, if +# have all needed files, that a GNU package needs + +LIBTOOL_DEPS = @LIBTOOL_DEPS@ + +AUTOMAKE_OPTIONS = foreign 1.4 +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = src +DIST_SUBDIRS = src + +if !COND_CROSS_COMPILE +SUBDIRS += test +DIST_SUBDIRS += test + +if BUILD_EXAMPLES +SUBDIRS += examples +DIST_SUBDIRS += examples +endif + +endif + +EXTRA_DIST = libhttpserver.pc.in $(DX_CONFIG) + +MOSTLYCLEANFILES = $(DX_CLEANFILES) *.gcda *.gcno *.gcov +DISTCLEANFILES = DIST_REVISION + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libhttpserver.pc + +cmakemoduledir = $(datadir)/cmake/Modules +cmakemodule_DATA = cmakemodule/FindLibHttpServer.cmake + +include $(top_srcdir)/aminclude.am + +# Update libtool, if needed. +libtool: $(LIBTOOL_DEPS) + $(SHELL) ./config.status --recheck + +dist-hook: + date >DIST_REVISION + git branch -vv >>DIST_REVISION + cp DIST_REVISION $(distdir)/ diff --git a/3rd_party/libhttpserver-0.18.2/Makefile.cvs b/3rd_party/libhttpserver-0.18.2/Makefile.cvs new file mode 100644 index 00000000..e655e6d8 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/Makefile.cvs @@ -0,0 +1,27 @@ +# +# This file is part of libhttpserver +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +default: all + +all: + aclocal -I m4 + autoheader + libtoolize --automake + automake --add-missing + autoconf + diff --git a/3rd_party/libhttpserver-0.18.2/NEWS b/3rd_party/libhttpserver-0.18.2/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/3rd_party/libhttpserver-0.18.2/PULL_REQUEST_TEMPLATE.md b/3rd_party/libhttpserver-0.18.2/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..a4d74934 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,10 @@ +Hello there! Welcome. Please follow the steps below to tell us about your contribution. + +1. Copy the correct template for your contribution + - Are you fixing a bug? Copy the template from https://raw.githubusercontent.com/etr/libhttpserver/master/.github/PULL_REQUEST_TEMPLATE/bug_fix.md + - Are you improving performance? Copy the template https://raw.githubusercontent.com/etr/libhttpserver/master/.github/PULL_REQUEST_TEMPLATE/performance_improvement.md + - Are you updating documentation? Copy the template from https://raw.githubusercontent.com/etr/libhttpserver/master/.github/PULL_REQUEST_TEMPLATE/documentation.md + - Are you changing functionality? Copy the template from https://raw.githubusercontent.com/etr/libhttpserver/master/.github/PULL_REQUEST_TEMPLATE/feature_change.md +2. Replace this text with the contents of the template +3. Fill in all sections of the template +4. Click "Create pull request" diff --git a/3rd_party/libhttpserver-0.18.2/README.CentOS-7 b/3rd_party/libhttpserver-0.18.2/README.CentOS-7 new file mode 100644 index 00000000..1dfaaa70 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/README.CentOS-7 @@ -0,0 +1,7 @@ +## Cent OS 7 / RHEL 7 + +CentOS 7 has a lower version of gcc (4.8.7) that is barely C++11 capable and this library +needs a better compiler. We recommend at least gcc 5+ + +We recommend installing devtoolset-8 +https://www.softwarecollections.org/en/scls/rhscl/devtoolset-8/ diff --git a/3rd_party/libhttpserver-0.18.2/README.FreeBSD b/3rd_party/libhttpserver-0.18.2/README.FreeBSD new file mode 100644 index 00000000..853c5589 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/README.FreeBSD @@ -0,0 +1,9 @@ +## Building on FreeBSD (Tested on 12.0) + +# Due to the differences in the directory structures on BSD systems some minor tweaks need to occur +# Also, FreeBSD and AIX "make" command is not compatible with gmake, like Linux and Mingw are + +export MAKE=gmake +bootstrap +configure CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib +gmake diff --git a/3rd_party/libhttpserver-0.18.2/README.md b/3rd_party/libhttpserver-0.18.2/README.md new file mode 100644 index 00000000..f3bdf4c1 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/README.md @@ -0,0 +1,1945 @@ + + +# The libhttpserver reference manual + +[![Build Status](https://api.travis-ci.com/etr/libhttpserver.svg?branch=master)](https://travis-ci.com/etr/libhttpserver) +[![Build status](https://ci.appveyor.com/api/projects/status/ktoy6ewkrf0q1hw6/branch/master?svg=true)](https://ci.appveyor.com/project/etr/libhttpserver/branch/master) +[![codecov](https://codecov.io/gh/etr/libhttpserver/branch/master/graph/badge.svg)](https://codecov.io/gh/etr/libhttpserver) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/5fa4bdc3815b4c10977f3badefedecd6)](https://www.codacy.com/app/etr/libhttpserver?utm_source=github.com&utm_medium=referral&utm_content=etr/libhttpserver&utm_campaign=Badge_Grade) +[![Gitter chat](https://badges.gitter.im/etr/libhttpserver.png)](https://gitter.im/libhttpserver/community) + +[![ko-fi](https://www.ko-fi.com/img/donate_sm.png)](https://ko-fi.com/F1F5HY8B) + +## Tl;dr +libhttpserver is a C++ library for building high performance RESTful web servers. +libhttpserver is built upon [libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) to provide a simple API for developers to create HTTP services in C++. + +**Features:** +- HTTP 1.1 compatible request parser +- RESTful oriented interface +- Flexible handler API +- Cross-platform compatible +- Implementation is HTTP 1.1 compliant +- Multiple threading models +- Support for IPv6 +- Support for SHOUTcast +- Support for incremental processing of POST data (optional) +- Support for basic and digest authentication (optional) +- Support for TLS (requires libgnutls, optional) + +## Table of Contents +* [Introduction](#introduction) +* [Requirements](#requirements) +* [Building](#building) +* [Getting Started](#getting-started) +* [Structures and classes type definition](#structures-and-classes-type-definition) +* [Create and work with a webserver](#create-and-work-with-a-webserver) +* [The resource object](#the-resource-object) +* [Registering resources](#registering-resources) +* [Parsing requests](#parsing-requests) +* [Building responses to requests](#building-responses-to-requests) +* [IP Blacklisting and Whitelisting](#ip-blacklisting-and-whitelisting) +* [Authentication](#authentication) +* [HTTP Utils](#http-utils) +* [Other Examples](#other-examples) + +#### Community +* [Code of Conduct (on a separate page)](https://github.com/etr/libhttpserver/blob/master/CODE_OF_CONDUCT.md) +* [Contributing (on a separate page)](https://github.com/etr/libhttpserver/blob/master/CONTRIBUTING.md) + +#### Appendices +* [Copying statement](#copying) +* [GNU-LGPL](#GNU-lesser-general-public-license): The GNU Lesser General Public License says how you can copy and share almost all of libhttpserver. +* [GNU-FDL](#GNU-free-documentation-license): The GNU Free Documentation License says how you can copy and share the documentation of libhttpserver. + +## Introduction +libhttpserver is meant to constitute an easy system to build HTTP servers with REST fashion. +libhttpserver is based on [libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) and, like this, it is a daemon library (parts of this documentation are, in fact, matching those of the wrapped library). +The mission of this library is to support all possible HTTP features directly and with a simple semantic allowing then the user to concentrate only on his application and not on HTTP request handling details. + +The library is supposed to work transparently for the client Implementing the business logic and using the library itself to realize an interface. +If the user wants it must be able to change every behavior of the library itself through the registration of callbacks. + +libhttpserver is able to decode certain body format a and automatically format them in object oriented fashion. This is true for query arguments and for *POST* and *PUT* requests bodies if *application/x-www-form-urlencoded* or *multipart/form-data* header are passed. + +All functions are guaranteed to be completely reentrant and thread-safe (unless differently specified). +Additionally, clients can specify resource limits on the overall number of connections, number of connections per IP address and memory used per connection to avoid resource exhaustion. + +[Back to TOC](#table-of-contents) + +## Requirements +libhttpserver can be used without any dependencies aside from libmicrohttpd. + +The minimum versions required are: +* g++ >= 5.5.0 or clang-3.6 +* libmicrohttpd >= 0.9.52 +* [Optionally]: for TLS (HTTPS) support, you'll need [libgnutls](http://www.gnutls.org/). +* [Optionally]: to compile the code-reference, you'll need [doxygen](http://www.doxygen.nl/). + +Additionally, for MinGW on windows you will need: +* libwinpthread (For MinGW-w64, if you use thread model posix then you have this) + +For versions before 0.18.0, on MinGW, you will need: +* libgnurx >= 2.5.1 + +Furthermore, the testcases use [libcurl](http://curl.haxx.se/libcurl/) but you don't need it to compile the library. + +Please refer to the readme file for your particular distribution if there is one for important notes. + +[Back to TOC](#table-of-contents) + +## Building +libhttpserver uses the standard system where the usual build process involves running +> ./bootstrap +> mkdir build +> cd build +> \.\./configure +> make +> make install # (optionally to install on the system) + +[Back to TOC](#table-of-contents) + +### Optional parameters to configure script +A complete list of parameters can be obtained running 'configure --help'. +Here are listed the libhttpserver specific options (the canonical configure options are also supported). + +* _\-\-enable-same-directory-build:_ enable to compile in the same directory. This is heavily discouraged. (def=no) +* _\-\-enable-debug:_ enable debug data generation. (def=no) +* _\-\-disable-doxygen-doc:_ don't generate any doxygen documentation. Doxygen is automatically invoked if present on the system. Automatically disabled otherwise. +* _\-\-enable-fastopen:_ enable use of TCP_FASTOPEN (def=yes) +* _\-\-enable-poll[=ARG]:_ enable poll support. Internal behavior of the `INTERNAL_SELECT` (yes, no, auto) [auto] +* _\-\-enable-epoll[=ARG]:_ enable epoll support. Internal behavior of the `INTERNAL_SELECT` (yes, no, auto) [auto] +* _\-\-enable-static:_ enable use static linking (def=yes) + +[Back to TOC](#table-of-contents) + +## Getting Started +The most basic example of creating a server and handling a requests for the path `/hello`: + + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you could run the following command from a terminal: + + curl -XGET -v http://localhost:8080/hello + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_hello_world.cpp). + +[Back to TOC](#table-of-contents) + +## Structures and classes type definition +* _webserver:_ Represents the daemon listening on a socket for HTTP traffic. + * _create_webserver:_ Builder class to support the creation of a webserver. +* _http_resource:_ Represents the resource associated with a specific http endpoint. +* _http_request:_ Represents the request received by the resource that process it. +* _http_response:_ Represents the response sent by the server once the resource finished its work. + * _string_response:_ A simple string response. + * _file_response:_ A response getting content from a fail. + * _basic_auth_fail_response:_ A failure in basic authentication. + * _digest_auth_fail_response:_ A failure in digest authentication. + * _deferred_response:_ A response getting content from a callback. + +[Back to TOC](#table-of-contents) + +## Create and work with a webserver +As you can see from the example above, creating a webserver with standard configuration is quite simple: + + webserver ws = create_webserver(8080); + +The `create_webserver` class is a supporting _builder_ class that eases the building of a webserver through chained syntax. + +### Basic Startup Options + +In this section we will explore other basic options that you can use when configuring your server. More advanced options (custom callbacks, https support, etc...) will be discussed separately. + +* _.port(**int** port):_ The port at which the server will listen. This can also be passed to the consturctor of `create_webserver`. E.g. `create_webserver(8080)`. +* _.max_connections(**int** max_conns):_ Maximum number of concurrent connections to accept. The default is `FD_SETSIZE - 4` (the maximum number of file descriptors supported by `select` minus four for `stdin`, `stdout`, `stderr` and the server socket). In other words, the default is as large as possible. Note that if you set a low connection limit, you can easily get into trouble with browsers doing request pipelining. +For example, if your connection limit is “1”, a browser may open a first connection to access your “index.html” file, keep it open but use a second connection to retrieve CSS files, images and the like. In fact, modern browsers are typically by default configured for up to 15 parallel connections to a single server. If this happens, the library will refuse to even accept the second connection until the first connection is closed — which does not happen until timeout. As a result, the browser will fail to render the page and seem to hang. If you expect your server to operate close to the connection limit, you should first consider using a lower timeout value and also possibly add a “Connection: close” header to your response to ensure that request pipelining is not used and connections are closed immediately after the request has completed. +* _.content_size_limit(**size_t** size_limit):_ Sets the maximum size of the content that a client can send over in a single block. The default is `-1 = unlimited`. +* _.connection_timeout(**int** timeout):_ Determines after how many seconds of inactivity a connection should be timed out automatically. The default timeout is `180 seconds`. +* _.memory_limit(**int** memory_limit):_ Maximum memory size per connection (followed by a `size_t`). The default is 32 kB (32*1024 bytes). Values above 128k are unlikely to result in much benefit, as half of the memory will be typically used for IO, and TCP buffers are unlikely to support window sizes above 64k on most systems. +* _.per_IP_connection_limit(**int** connection_limit):_ Limit on the number of (concurrent) connections made to the server from the same IP address. Can be used to prevent one IP from taking over all of the allowed connections. If the same IP tries to establish more than the specified number of connections, they will be immediately rejected. The default is `0`, which means no limit on the number of connections from the same IP address. +* _.bind_socket(**int** socket_fd):_ Listen socket to use. Pass a listen socket for the daemon to use (systemd-style). If this option is used, the daemon will not open its own listen socket(s). The argument passed must be of type "int" and refer to an existing socket that has been bound to a port and is listening. +* _.max_thread_stack_size(**int** stack_size):_ Maximum stack size for threads created by the library. Not specifying this option or using a value of zero means using the system default (which is likely to differ based on your platform). Default is `0 (system default)`. +* _.use_ipv6() and .no_ipv6():_ Enable or disable the IPv6 protocol support (by default, libhttpserver will just support IPv4). If you specify this and the local platform does not support it, starting up the server will throw an exception. `off` by default. +* _.use_dual_stack() and .no_dual_stack():_ Enable or disable the support for both IPv6 and IPv4 protocols at the same time (by default, libhttpserver will just support IPv4). If you specify this and the local platform does not support it, starting up the server will throw an exception. Note that this will mean that IPv4 addresses are returned in the IPv6-mapped format (the ’structsockaddrin6’ format will be used for IPv4 and IPv6). `off` by default. +* _.pedantic() and .no_pedantic():_ Enables pedantic checks about the protocol (as opposed to as tolerant as possible). Specifically, at the moment, this flag causes the library to reject HTTP 1.1 connections without a `Host` header. This is required by the standard, but of course in violation of the “be as liberal as possible in what you accept” norm. It is recommended to turn this **off** if you are testing clients against the library, and **on** in production. `off` by default. +* _.debug() and .no_debug():_ Enables debug messages from the library. `off` by default. +* _.regex_checking() and .no_regex_checking():_ Enables pattern matching for endpoints. Read more [here](#registering-resources). `on` by default. +* _.post_process() and .no_post_process():_ Enables/Disables the library to automatically parse the body of the http request as arguments if in querystring format. Read more [here](#parsing-requests). `on` by default. +* _.deferred()_ and _.no_deferred():_ Enables/Disables the ability for the server to suspend and resume connections. Simply put, it enables/disables the ability to use `deferred_response`. Read more [here](#building-responses-to-requests). `on` by default. +* _.single_resource() and .no_single_resource:_ Sets or unsets the server in single resource mode. This limits all endpoints to be served from a single resource. The resultant is that the webserver will process the request matching to the endpoint skipping any complex semantic. Because of this, the option is incompatible with `regex_checking` and requires the resource to be registered against an empty endpoint or the root endpoint (`"/"`). The resource will also have to be registered as family. (For more information on resource registration, read more [here](#registering-resources)). `off` by default. + +### Threading Models +* _.start_method(**const http::http_utils::start_method_T&** start_method):_ libhttpserver can operate with two different threading models that can be selected through this method. Default value is `INTERNAL_SELECT`. + * `http::http_utils::INTERNAL_SELECT`: In this mode, libhttpserver uses only a single thread to handle listening on the port and processing of requests. This mode is preferable if spawning a thread for each connection would be costly. If the HTTP server is able to quickly produce responses without much computational overhead for each connection, this mode can be a great choice. Note that libhttpserver will still start a single thread for itself -- this way, the main program can continue with its operations after calling the start method. Naturally, if the HTTP server needs to interact with shared state in the main application, synchronization will be required. If such synchronization in code providing a response results in blocking, all HTTP server operations on all connections will stall. This mode is a bad choice if response data cannot always be provided instantly. The reason is that the code generating responses should not block (since that would block all other connections) and on the other hand, if response data is not available immediately, libhttpserver will start to busy wait on it. If you need to scale along the number of concurrent connection and scale on multiple thread you can specify a value for `max_threads` (see below) thus enabling a thread pool - this is different from `THREAD_PER_CONNECTION` below where a new thread is spawned for each connection. + * `http::http_utils::THREAD_PER_CONNECTION`: In this mode, libhttpserver starts one thread to listen on the port for new connections and then spawns a new thread to handle each connection. This mode is great if the HTTP server has hardly any state that is shared between connections (no synchronization issues!) and may need to perform blocking operations (such as extensive IO or running of code) to handle an individual connection. +* _.max_threads(**int** max_threads):_ A thread pool can be combined with the `INTERNAL_SELECT` mode to benefit implementations that require scalability. As said before, by default this mode only uses a single thread. When combined with the thread pool option, it is possible to handle multiple connections with multiple threads. Any value greater than one for this option will activate the use of the thread pool. In contrast to the `THREAD_PER_CONNECTION` mode (where each thread handles one and only one connection), threads in the pool can handle a large number of concurrent connections. Using `INTERNAL_SELECT` in combination with a thread pool is typically the most scalable (but also hardest to debug) mode of operation for libhttpserver. Default value is `1`. This option is incompatible with `THREAD_PER_CONNECTION`. + +### Custom defaulted error messages +libhttpserver allows to override internal error retrieving functions to provide custom messages to the HTTP client. There are only 3 cases in which implementing logic (an http_resource) cannot be invoked: (1) a not found resource, where the library is not being able to match the URL requested by the client to any implementing http_resource object; (2) a not allowed method, when the HTTP client is requesting a method explicitly marked as not allowed (more info [here](#allowing-and-disallowing-methods-on-a-resource)) by the implementation; (3) an exception being thrown. +In all these 3 cases libhttpserver would provide a standard HTTP response to the client with the correct error code; respectively a `404`, a `405` and a `500`. The library allows its user to specify custom callbacks that will be called to replace the default behavior. +* _.not_found_resource(**const shared_ptr(*render_ptr)(const http_request&)** resource):_ Specifies a function to handle a request when no matching registered endpoint exist for the URL requested by the client. +* _.method_not_allowed_resource(**const shared_ptr(*render_ptr)(const http_request&)** resource):_ Specifies a function to handle a request that is asking for a method marked as not allowed on the matching http_resource. +* _.internal_error_resource(**const shared_ptr(*render_ptr)(const http_request&)** resource):_ Specifies a function to handle a request that is causing an uncaught exception during its execution. **REMEMBER:** is this callback is causing an exception itself, the standard default response from libhttpserver will be reported to the HTTP client. + +#### Example of custom errors: + #include + + using namespace httpserver; + + const std::shared_ptr not_found_custom(const http_request& req) { + return std::shared_ptr(new string_response("Not found custom", 404, "text/plain")); + } + + const std::shared_ptr not_allowed_custom(const http_request& req) { + return std::shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); + } + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .not_found_resource(not_found_custom) + .method_not_allowed_resource(not_allowed_custom); + + hello_world_resource hwr; + hwr.disallow_all(); + hwr.set_allowing("GET", true); + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v http://localhost:8080/hello + +If you try to run either of the two following commands, you'll see your custom errors: +* `curl -XGET -v http://localhost:8080/morning`: will return your custom `not found` error. +* `curl -XPOST -v http://localhost:8080/hello`: will return your custom `not allowed` error. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/custom_error.cpp). + +### Custom logging callbacks +* _.log_access(**void(*log_access_ptr)(const std::string&)** functor):_ Specifies a function used to log accesses (requests) to the server. +* _.log_error(**void(*log_error_ptr)(const std::string&)** functor):_ Specifies a function used to log errors generating from the server. + +#### Example of custom logging callback + #include + #include + + using namespace httpserver; + + void custom_access_log(const std::string& url) { + std::cout << "ACCESSING: " << url << std::endl; + } + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .log_access(custom_access_log); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v http://localhost:8080/hello + +You'll notice how, on the terminal runing your server, the logs will now be printed in output for each request received. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/custom_access_log.cpp). + +### TLS/HTTPS +* _.use_ssl() and .no_ssl():_ Determines whether to run in HTTPS-mode or not. If you set this as on and libhttpserver was compiled without SSL support, the library will throw an exception at start of the server. `off` by default. +* _.cred_type(**const http::http_utils::cred_type_T&** cred_type):_ Daemon credentials type. Either certificate or anonymous. Acceptable values are: + * `NONE`: No credentials. + * `CERTIFICATE`: Certificate credential. + * `ANON`: Anonymous credential. + * `SRP`: SRP credential. + * `PSK`: PSK credential. + * `IA`: IA credential. +* _.https_mem_key(**const std::string&** filename):_ String representing the path to a file containing the private key to be used by the HTTPS daemon. This must be used in conjunction with `https_mem_cert`. +* _.https_mem_cert(**const std::string&** filename):_ String representing the path to a file containing the certificate to be used by the HTTPS daemon. This must be used in conjunction with `https_mem_key`. +* _.https_mem_trust(**const std::string&** filename):_ String representing the path to a file containing the CA certificate to be used by the HTTPS daemon to authenticate and trust clients certificates. The presence of this option activates the request of certificate to the client. The request to the client is marked optional, and it is the responsibility of the server to check the presence of the certificate if needed. Note that most browsers will only present a client certificate only if they have one matching the specified CA, not sending any certificate otherwise. +* _.https_priorities(**const std::string&** priority_string):_ SSL/TLS protocol version and ciphers. Must be followed by a string specifying the SSL/TLS protocol versions and ciphers that are acceptable for the application. The string is passed unchanged to gnutls_priority_init. If this option is not specified, `"NORMAL"` is used. + +#### Minimal example using HTTPS + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v -k 'https://localhost:8080/hello' + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_https.cpp). + +### IP Blacklisting/Whitelisting +libhttpserver supports IP blacklisting and whitelisting as an internal feature. This section explains the startup options related with IP blacklisting/whitelisting. See the [specific section](#ip-blacklisting-and-whitelisting) to read more about the topic. +* _.ban_system() and .no_ban_system:_ Can be used to enable/disable the ban system. `on` by default. +* _.default_policy(**const http::http_utils::policy_T&** default_policy):_ Specifies what should be the default behavior when receiving a request. Possible values are `ACCEPT` and `REJECT`. Default is `ACCEPT`. + +### Authentication Parameters +* _.basic_auth() and .no_basic_auth:_ Can be used to enable/disable parsing of the basic authorization header sent by the client. `on` by default. +* _.digest_auth() and .no_digest_auth:_ Can be used to enable/disable parsing of the digested authentication data sent by the client. `on` by default. +* _.nonce_nc_size(**int** nonce_size):_ Size of an array of nonce and nonce counter map. This option represents the size (number of elements) of a map of a nonce and a nonce-counter. If this option is not specified, a default value of 4 will be used (which might be too small for servers handling many requests). +You should calculate the value of NC_SIZE based on the number of connections per second multiplied by your expected session duration plus a factor of about two for hash table collisions. For example, if you expect 100 digest-authenticated connections per second and the average user to stay on your site for 5 minutes, then you likely need a value of about 60000. On the other hand, if you can only expect only 10 digest-authenticated connections per second, tolerate browsers getting a fresh nonce for each request and expect a HTTP request latency of 250 ms, then a value of about 5 should be fine. +* _.digest_auth_random(**const std::string&** nonce_seed):_ Digest Authentication nonce’s seed. For security, you SHOULD provide a fresh random nonce when actually using Digest Authentication with libhttpserver in production. + +### Examples of chaining syntax to create a webserver + + webserver ws = create_webserver(8080) + .no_ssl() + .no_ipv6() + .no_debug() + .no_pedantic() + .no_basic_auth() + .no_digest_auth() + .no_comet() + .no_regex_checking() + .no_ban_system() + .no_post_process(); +## + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem"); + +### Starting and stopping a webserver +Once a webserver is created, you can manage its execution through the following methods on the `webserver` class: +* _**void** webserver::start(**bool** blocking):_ Allows to start a server. If the `blocking` flag is passed as `true`, it will block the execution of the current thread until a call to stop on the same webserver object is performed. +* _**void** webserver::stop():_ Allows to stop a server. It immediately stops it. +* _**bool** webserver::is_running():_ Checks if a server is running +* _**void** webserver::sweet_kill():_ Allows to stop a server. It doesn't guarantee an immediate halt to allow for thread termination and connection closure. + +[Back to TOC](#table-of-contents) + +## The Resource Object +The `http_resource` class represents a logical collection of HTTP methods that will be associated to a URL when registered on the webserver. The class is **designed for extension** and it is where most of your code should ideally live. When the webserver matches a request against a resource (see: [resource registration](#registering-resources)), the method correspondent to the one in the request (GET, POST, etc..) (see below) is called on the resource. + +Given this, the `http_resource` class contains the following extensible methods (also called `handlers` or `render methods`): +* _**const std::shared_ptr** http_resource::render_GET(**const http_request&** req):_ Invoked on an HTTP GET request. +* _**const std::shared_ptr** http_resource::render_POST(**const http_request&** req):_ Invoked on an HTTP POST request. +* _**const std::shared_ptr** http_resource::render_PUT(**const http_request&** req):_ Invoked on an HTTP PUT request. +* _**const std::shared_ptr** http_resource::render_HEAD(**const http_request&** req):_ Invoked on an HTTP HEAD request. +* _**const std::shared_ptr** http_resource::render_DELETE(**const http_request&** req):_ Invoked on an HTTP DELETE request. +* _**const std::shared_ptr** http_resource::render_TRACE(**const http_request&** req):_ Invoked on an HTTP TRACE request. +* _**const std::shared_ptr** http_resource::render_OPTIONS(**const http_request&** req):_ Invoked on an HTTP OPTIONS request. +* _**const std::shared_ptr** http_resource::render_CONNECT(**const http_request&** req):_ Invoked on an HTTP CONNECT request. +* _**const std::shared_ptr** http_resource::render(**const http_request&** req):_ Invoked as a backup method if the matching method is not implemented. It can be used whenever you want all the invocations on a URL to activate the same behavior regardless of the HTTP method requested. The default implementation of the `render` method returns an empty response with a `404`. + +#### Example of implementation of render methods + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request&) { + return std::shared_ptr(new string_response("GET: Hello, World!")); + } + + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("OTHER: Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following commands from a terminal: + * `curl -XGET -v http://localhost:8080/hello`: will return `GET: Hello, World!`. + * `curl -XPOST -v http://localhost:8080/hello`: will return `OTHER: Hello, World!`. You can try requesting other methods beside `POST` to verify how the same message will be returned. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/handlers.cpp). + +### Allowing and disallowing methods on a resource +By default, all methods an a resource are allowed, meaning that an HTTP request with that method will be invoked. It is possible to mark methods as `not allowed` on a resource. When a method not allowed is requested on a resource, the default `method_not_allowed` method is invoked - the default can be overriden as explain in the section [Custom defaulted error messages](custom-defaulted-error-messages). +The base `http_resource` class has a set of methods that can be used to allow and disallow HTTP methods. +* _**void** http_resource::set_allowing(**const std::string&** method, **bool** allowed):_ Used to allow or disallow a method. The `method` parameter is a string representing an HTTP method (GET, POST, PUT, etc...). +* _**void** http_resource::allow_all():_ Marks all HTTP methods as allowed. +* _**void** http_resource::disallow_all():_ Marks all HTTP methods as not allowed. + +#### Example of methods allowed/disallowed + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + hwr.disallow_all(); + hwr.set_allowing("GET", true); + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v http://localhost:8080/hello + +If you try to run the following command, you'll see a `method_not_allowed` error: +* `curl -XPOST -v http://localhost:8080/hello`. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/allowing_disallowing_methods.cpp). + +[Back to TOC](#table-of-contents) + +## Registering resources +Once you have created your resource and extended its methods, you'll have to register the resource on the webserver. Registering a resource will associate it with an endpoint and allows the webserver to route it. +The `webserver` class offers a method to register a resource: +* _**bool** register_resource(**const std::string&** endpoint, **http_resource*** resource, **bool** family = `false`):_ Registers the `resource` to an `endpoint`. The endpoint is a string representing the path on your webserver from where you want your resource to be served from (e.g. `"/path/to/resource"`). The optional `family` parameter allows to register a resource as a "family" resource that will match any path nested into the one specified. For example, if family is set to `true` and endpoint is set to `"/path"`, the webserver will route to the resource not only the requests against `"/path"` but also everything in its nested path `"/path/on/the/previous/one"`. + +### Specifying endpoints +There are essentially four ways to specify an endpoint string: +* **A simple path (e.g. `"/path/to/resource"`).** In this case, the webserver will try to match exactly the value of the endpoint. +* **A regular exception.** In this case, the webserver will try to match the URL of the request with the regex passed. For example, if passing `"/path/as/decimal/[0-9]+`, requests on URLs like `"/path/as/decimal/5"` or `"/path/as/decimal/42"` will be matched; instead, URLs like `"/path/as/decimal/three"` will not. +* **A parametrized path. (e.g. `"/path/to/resource/with/{arg1}/{arg2}/in/url"`)**. In this case, the webserver will match the argument with any value passed. In addition to this, the arguments will be passed to the resource as part of the arguments (readable from the `http_request::get_arg` method - see [here](#parsing-requests)). For example, if passing `"/path/to/resource/with/{arg1}/{arg2}/in/url"` will match any request on URL with any value in place of `{arg1}` and `{arg2}`. +* **A parametrized path with custom parameters.** This is the same of a normal parametrized path, but allows to specify a regular expression for the argument (e.g. `"/path/to/resource/with/{arg1|[0-9]+}/{arg2|[a-z]+}/in/url"`. In this case, the webserver will match the arguments with any value passed that satisfies the regex. In addition to this, as above, the arguments will be passed to the resource as part of the arguments (readable from the `http_request::get_arg` method - see [here](#parsing-requests)). For example, if passing `"/path/to/resource/with/{arg1|[0-9]+}/{arg2|[a-z]+}/in/url"` will match requests on URLs like `"/path/to/resource/with/10/AA/in/url"` but not like `""/path/to/resource/with/BB/10/in/url""` +* Any of the above marked as `family`. Will match any request on URLs having path that is prefixed by the path passed. For example, if family is set to `true` and endpoint is set to `"/path"`, the webserver will route to the resource not only the requests against `"/path"` but also everything in its nested path `"/path/on/the/previous/one"`. + + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + class handling_multiple_resource : public http_resource { + public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("Your URL: " + req.get_path())); + } + }; + + class url_args_resource : public http_resource { + public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2"))); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + + handling_multiple_resource hmr; + ws.register_resource("/family", &hmr, true); + ws.register_resource("/with_regex_[0-9]+", &hmr); + + url_args_resource uar; + ws.register_resource("/url/with/{arg1}/and/{arg2}", &uar); + ws.register_resource("/url/with/parametric/args/{arg1|[0-9]+}/and/{arg2|[A-Z]+}", &uar); + + ws.start(true); + + return 0; + } + +To test the above example, you can run the following commands from a terminal: + +* `curl -XGET -v http://localhost:8080/hello`: will return the `Hello, World!` message. +* `curl -XGET -v http://localhost:8080/family`: will return the `Your URL: /family` message. +* `curl -XGET -v http://localhost:8080/family/with/suffix`: will return the `Your URL: /family/with/suffix` message. +* `curl -XGET -v http://localhost:8080/with_regex_10`: will return the `Your URL: /with_regex_10` message. +* `curl -XGET -v http://localhost:8080/url/with/AA/and/BB`: will return the `ARGS: AA and BB` message. You can change `AA` and `BB` with any value and observe how the URL is still matched and parameters are read. +* `curl -XGET -v http://localhost:8080/url/with/parametric/args/10/and/AA`: will return the `ARGS: 10 and AA` message. You can change `10` and `AA` with any value matching the regexes and observe how the URL is still matched and parameters are read. + +Conversely, you can observe how these URL will not be matched (al the following will give you a `not found` message): +* `curl -XGET -v http://localhost:8080/with_regex_A` +* `curl -XGET -v http://localhost:8080/url/with/parametric/args/AA/and/BB` + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/url_registration.cpp). + +[Back to TOC](#table-of-contents) + +## Parsing requests +As seen in the documentation of [http_resource](#the-resource-object), every extensible method takes in input a `http_request` object. The webserver takes the responsibility to extract the data from the HTTP request on the network and does all the heavy lifting to build the instance of `http_request`. + +The `http_request` class has a set of methods you will have access to when implementing your handlers: +* _**const std::string&** get_path() **const**:_ Returns the path as requested from the HTTP client. +* _**const std::vector\&** get_path_pieces() **const**:_ Returns the components of the path requested by the HTTP client (each piece of the path split by `'/'`. +* _**const std::string&** get_path_piece(int index) **const**:_ Returns one piece of the path requested by the HTTP client. The piece is selected through the `index` parameter (0-indexed). +* _**const std::string&** get_method() **const**:_ Returns the method requested by the HTTP client. +* _**const std::string** get_header(**const std::string&** key) **const**:_ Returns the header with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise. +* _**const std::string** get_cookie(**const std::string&** key) **const**:_ Returns the cookie with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise. +* _**const std::string** get_footer(**const std::string&** key) **const**:_ Returns the footer with name equal to `key` if present in the HTTP request (only for http 1.1 chunked encodings). Returns an `empty string` otherwise. +* _**const std::string** get_arg(**const std::string&** key) **const**:_ Returns the argument with name equal to `key` if present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default). +* _**const std::map** get_headers() **const**:_ Returns a map containing all the headers present in the HTTP request. +* _**const std::map** get_cookies() **const**:_ Returns a map containing all the cookies present in the HTTP request. +* _**const std::map** get_footers() **const**:_ Returns a map containing all the footers present in the HTTP request (only for http 1.1 chunked encodings). +* _**const std::map** get_args() **const**:_ Returns all the arguments present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default). +* _**const std::string&** get_content() **const**:_ Returns the body of the HTTP request. +* _**bool** content_too_large() **const**:_ Returns `true` if the body length of the HTTP request sent by the client is longer than the max allowed on the server. +* _**const std::string** get_querystring() **const**:_ Returns the `querystring` of the HTTP request. +* _**const std::string&** get_version() **const**:_ Returns the HTTP version of the client request. +* _**const std::string** get_requestor() **const**:_ Returns the IP from which the client is sending the request. +* _**unsigned short** get_requestor_port() **const**:_ Returns the port from which the client is sending the request. +* _**const std::string** get_user() **const**:_ Returns the `user` as self-identified through basic authentication. The content of the user header will be parsed only if basic authentication is enabled on the server (enabled by default). +* _**const std::string** get_pass() **const**:_ Returns the `password` as self-identified through basic authentication. The content of the password header will be parsed only if basic authentication is enabled on the server (enabled by default). +* _**const std::string** get_digested_user() **const**:_ Returns the `digested user` as self-identified through digest authentication. The content of the user header will be parsed only if digest authentication is enabled on the server (enabled by default). +* _**bool** check_digest_auth(**const std::string&** realm, **const std::string&** password, **int** nonce_timeout, **bool&** reload_nonce) **const**:_ Allows to check the validity of the authentication token sent through digest authentication (if the provided values in the WWW-Authenticate header are valid and sound according to RFC2716). Takes in input the `realm` of validity of the authentication, the `password` as known to the server to compare against, the `nonce_timeout` to indicate how long the nonce is valid and `reload_nonce` a boolean that will be set by the method to indicate a nonce being reloaded. The method returns `true` if the authentication is valid, `false` otherwise. + +#### Example of handler reading arguments from a request + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("Hello: " + req.get_arg("name"))); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v "http://localhost:8080/hello?name=John" + +You will receive the message `Hello: John` in reply. Given that the body post processing is enabled, you can also run `curl -d "name=John" -X POST http://localhost:8080/hello` to obtain the same result. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/hello_with_get_arg.cpp). + +[Back to TOC](#table-of-contents) + +## Building responses to requests +As seen in the documentation of [http_resource](#the-resource-object), every extensible method returns in output a `http_response` object. The webserver takes the responsibility to convert the `http_response` object you create into a response on the network. + +There are 5 types of response that you can create - we will describe them here through their constructors: +* _string_response(**const std::string&** content, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ The most basic type of response. It uses the `content` string passed in construction as body of the HTTP response. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _file_response(**const std::string&** filename, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ Uses the `filename` passed in construction as pointer to a file on disk. The body of the HTTP response will be set using the content of the file. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _basic_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during basic authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _digest_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **const std::string&** opaque = `""`, **bool** reload_nonce = `false`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during digest authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The `opaque` represents a value that gets passed to the client and expected to be passed again to the server as-is. This value can be a hexadecimal or base64 string. The `reload_nonce` parameter tells the server to reload the nonce (you should use the value returned by the `check_digest_auth` method on the `http_request`. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _deferred_response(**ssize_t(*cycle_callback_ptr)(shared_ptr<T>, char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default). + * The `cycle_callback_ptr` has this shape: + _**ssize_t** cycle_callback(**shared_ptr<T> closure_data, char*** buf, **size_t** max_size)_. + You are supposed to implement a function in this shape and provide it to the `deferred_repsonse` method. The webserver will provide a `char*` to the function. It is responsibility of the function to allocate it and fill its content. The method is supposed to respect the `max_size` parameter passed in input. The function must return a `ssize_t` value representing the actual size you filled the `buf` with. Any value different from `-1` will keep the resume the connection, deliver the content and suspend it again (with a `100 CONTINUE`). If the method returns `-1`, the webserver will complete the communication with the client and close the connection. You can also pass a `shared_ptr` pointing to a data object of your choice (this will be templetized with a class of your choice). The server will guarantee that this object is passed at each invocation of the method allowing the client code to use it as a memory buffer during computation. + +### Setting additional properties of the response +The `http_response` class offers an additional set of methods to "decorate" your responses. This set of methods is: +* _**void** with_header(**const std::string&** key, **const std::string&** value):_ Sets an HTTP header with name set to `key` and value set to `value`. +* _**void** with_footer(**const std::string&** key, **const std::string&** value):_ Sets an HTTP footer with name set to `key` and value set to `value`. +* _**void** with_cookie(**const std::string&** key, **const std::string&** value):_ Sets an HTTP cookie with name set to `key` and value set to `value` (only for http 1.1 chunked encodings). +* _**void** shoutCAST():_ Mark the response as a `shoutCAST` one. + +### Example of response setting headers + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + std::shared_ptr response = std::shared_ptr(new string_response("Hello, World!")); + response->with_header("MyHeader", "MyValue"); + return response; + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you could run the following command from a terminal: + + curl -XGET -v "http://localhost:8080/hello" + +You will receive the message custom header in reply. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/setting_headers.cpp). + +[Back to TOC](#table-of-contents) + +## IP Blacklisting and Whitelisting +libhttpserver provides natively a system to blacklist and whitelist IP addresses. To enable/disable the system, it is possible to use the `ban_system` and `no_ban_system` methods on the `create_webserver` class. In the same way, you can specify what you want to be your "default behavior" (allow by default or disallow by default) by using the `default_policy` method (see [here](#create-and-work-with-a-webserver)). + +The system supports both IPV4 and IPV6 and manages them transparently. The only requirement is for ipv6 to be enabled on your server - you'll have to enable this by using the `use_ipv6` method on `create_webserver`. + +You can explicitly ban or allow an IP address using the following methods on the `webserver` class: +* _**void** ban_ip(**const std::string&** ip):_ Adds one IP (or a range of IPs) to the list of the banned ones. Takes in input a `string` that contains the IP (or range of IPs) to ban. To use when the `default_policy` is `ACCEPT`. +* _**void** allow_ip(**const std::string&** ip):_ Adds one IP (or a range of IPs) to the list of the allowed ones. Takes in input a `string` that contains the IP (or range of IPs) to allow. To use when the `default_policy` is `REJECT`. +* _**void** unban_ip(**const std::string&** ip):_ Removes one IP (or a range of IPs) from the list of the banned ones. Takes in input a `string` that contains the IP (or range of IPs) to remove from the list. To use when the `default_policy` is `REJECT`. +* _**void** disallow_ip(**const std::string&** ip):_ Removes one IP (or a range of IPs) from the list of the allowed ones. Takes in input a `string` that contains the IP (or range of IPs) to remove from the list. To use when the `default_policy` is `REJECT`. + +### IP String Format +The IP string format can represent both IPV4 and IPV6. Addresses will be normalized by the webserver to operate in the same sapce. Any valid IPV4 or IPV6 textual representation works. +It is also possible to specify ranges of IPs. To do so, omit the octect you want to express as a range and specify a `'*'` in its place. +Examples of valid IPs include: +* `"192.168.5.5"`: standard IPV4 +* `"192.168.*.*"`: range of IPV4 addresses. In the example, everything between `192.168.0.0` and `192.168.255.255`. +* `"2001:db8:8714:3a90::12"`: standard IPV6 - clustered empty ranges are fully supported. +* `"2001:db8:8714:3a90:*:*"`: range of IPV6 addresses. +* `"::ffff:192.0.2.128"`: IPV4 IPs nested into IPV6. +* `"::192.0.2.128"`: IPV4 IPs nested into IPV6 (without `'ffff'` prefix) +* `"::ffff:192.0.*.*"`: ranges of IPV4 IPs nested into IPV6. + +#### Example of IP Whitelisting/Blacklisting + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .default_policy(http::http_utils::REJECT); + + ws.allow_ip("127.0.0.1"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you could run the following command from a terminal: + + curl -XGET -v "http://localhost:8080/hello" + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_ip_ban.cpp). + +[Back to TOC](#table-of-contents) + +## Authentication +libhttpserver support three types of client authentication. + +Basic authentication uses a simple authentication method based on BASE64 algorithm. Username and password are exchanged in clear between the client and the server, so this method must only be used for non-sensitive content or when the session is protected with https. When using basic authentication libhttpserver will have access to the clear password, possibly allowing to create a chained authentication toward an external authentication server. You can enable/disable support for Basic authentication through the `basic_auth` and `no_basic_auth` methods of the `create_webserver` class. + +Digest authentication uses a one-way authentication method based on MD5 hash algorithm. Only the hash will transit over the network, hence protecting the user password. The nonce will prevent replay attacks. This method is appropriate for general use, especially when https is not used to encrypt the session. You can enable/disable support for Digest authentication through the `digest_auth` and `no_digest_auth` methods of the `create_webserver` class. + +Client certificate authentication uses a X.509 certificate from the client. This is the strongest authentication mechanism but it requires the use of HTTPS. Client certificate authentication can be used simultaneously with Basic or Digest Authentication in order to provide a two levels authentication (like for instance separate machine and user authentication). You can enable/disable support for Certificate authentication through the `use_ssl` and `no_ssl` methods of the `create_webserver` class. + +### Using Basic Authentication + #include + + using namespace httpserver; + + class user_pass_resource : public httpserver::http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + if (req.get_user() != "myuser" || req.get_pass() != "mypass") { + return std::shared_ptr(new basic_auth_fail_response("FAIL", "test@example.com")); + } + return std::shared_ptr(new string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + user_pass_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v -u myuser:mypass "http://localhost:8080/hello" + +You will receive back the user and password you passed in input. Try to pass the wrong credentials to see the failure. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/basic_authentication.cpp). + +### Using Digest Authentication + #include + + #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" + + using namespace httpserver; + + class digest_resource : public httpserver::http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + if (req.get_digested_user() == "") { + return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); + } + else { + bool reload_nonce = false; + if(!req.check_digest_auth("test@example.com", "mypass", 300, reload_nonce)) { + return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, reload_nonce)); + } + } + return std::shared_ptr(new string_response("SUCCESS", 200, "text/plain")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + digest_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v --digest --user myuser:mypass localhost:8080/hello + +You will receive a `SUCCESS` in response (observe the response message from the server in detail and you'll see the full interaction). Try to pass the wrong credentials or send a request without `digest` active to see the failure. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/digest_authentication.cpp). + +[Back to TOC](#table-of-contents) + +## HTTP Utils +libhttpserver provides a set of constants to help you develop your HTTP server. It would be redudant to list them here; so, please, consult the list directly [here](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp). + +[Back to TOC](#table-of-contents) + +## Other Examples + +#### Example of returning a response from a file + #include + + using namespace httpserver; + + class file_response_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + return std::shared_ptr(new file_response("test_content", 200, "text/plain")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + file_response_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v localhost:8080/hello + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_file_response.cpp). + +#### Example of a deferred response through callback + #include + + using namespace httpserver; + + static int counter = 0; + + ssize_t test_callback (std::shared_ptr closure_data, char* buf, size_t max) { + if (counter == 2) { + return -1; + } + else { + memset(buf, 0, max); + strcat(buf, " test "); + counter++; + return std::string(buf).size(); + } + } + + class deferred_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + return std::shared_ptr >(new deferred_response(test_callback, nullptr, "cycle callback response")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + deferred_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v localhost:8080/hello + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_deferred.cpp). + +#### Example of a deferred response through callback (passing additional data along) + #include + #include + + using namespace httpserver; + + std::atomic counter; + + ssize_t test_callback (std::shared_ptr > closure_data, char* buf, size_t max) { + int reqid; + if (closure_data == nullptr) { + reqid = -1; + } else { + reqid = *closure_data; + } + + // only first 5 connections can be established + if (reqid >= 5) { + return -1; + } else { + // respond corresponding request IDs to the clients + std::string str = ""; + str += std::to_string(reqid) + " "; + memset(buf, 0, max); + std::copy(str.begin(), str.end(), buf); + + // keep sending reqid + sleep(1); + + return (ssize_t)max; + } + } + + class deferred_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + std::shared_ptr > closure_data(new std::atomic(counter++)); + return std::shared_ptr > >(new deferred_response >(test_callback, closure_data, "cycle callback response")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + deferred_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v localhost:8080/hello + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/deferred_with_accumulator.cpp). + +[Back to TOC](#table-of-contents) + +## Copying +This manual is for libhttpserver, C++ library for creating an embedded Rest HTTP server (and more). + +> Permission is granted to copy, distribute and/or modify this document +> under the terms of the GNU Free Documentation License, Version 1.3 +> or any later version published by the Free Software Foundation; +> with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +> Texts. A copy of the license is included in the section entitled GNU +> Free Documentation License. + +[Back to TOC](#table-of-contents) + +## GNU Lesser General Public License +Version 2.1, February 1999 + +Copyright © 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +_This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1._ + +### Preamble + +The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + +To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + +Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + +When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + +We call this license the “Lesser” General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + +For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + +Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +“work based on the library” and a “work that uses the library”. The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + +### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +**0.** This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called “this License”). +Each licensee is addressed as “you”. + +A “library” means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + +The “Library”, below, refers to any such software library or work +which has been distributed under these terms. A “work based on the +Library” means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term “modification”.) + +“Source code” for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + +**1.** You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + +You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + +**2.** You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +* **a)** The modified work must itself be a software library. +* **b)** You must cause the files modified to carry prominent notices +stating that you changed the files and the date of any change. +* **c)** You must cause the whole of the work to be licensed at no +charge to all third parties under the terms of this License. +* **d)** If a facility in the modified Library refers to a function or a +table of data to be supplied by an application program that uses +the facility, other than as an argument passed when the facility +is invoked, then you must make a good faith effort to ensure that, +in the event an application does not supply such function or +table, the facility still operates, and performs whatever part of +its purpose remains meaningful. +(For example, a function in a library to compute square roots has +a purpose that is entirely well-defined independent of the +application. Therefore, Subsection 2d requires that any +application-supplied function or table used by this function must +be optional: if the application does not supply it, the square +root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +**3.** You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + +Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + +**4.** You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + +**5.** A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a “work that uses the Library”. Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + +However, linking a “work that uses the Library” with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a “work that uses the +library”. The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + +When a “work that uses the Library” uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + +**6.** As an exception to the Sections above, you may also combine or +link a “work that uses the Library” with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + +* **a)** Accompany the work with the complete corresponding +machine-readable source code for the Library including whatever +changes were used in the work (which must be distributed under +Sections 1 and 2 above); and, if the work is an executable linked +with the Library, with the complete machine-readable “work that +uses the Library”, as object code and/or source code, so that the +user can modify the Library and then relink to produce a modified +executable containing the modified Library. (It is understood +that the user who changes the contents of definitions files in the +Library will not necessarily be able to recompile the application +to use the modified definitions.) +* **b)** Use a suitable shared library mechanism for linking with the +Library. A suitable mechanism is one that (1) uses at run time a +copy of the library already present on the user's computer system, +rather than copying library functions into the executable, and (2) +will operate properly with a modified version of the library, if +the user installs one, as long as the modified version is +interface-compatible with the version that the work was made with. +* **c)** Accompany the work with a written offer, valid for at +least three years, to give the same user the materials +specified in Subsection 6a, above, for a charge no more +than the cost of performing this distribution. +* **d)** If distribution of the work is made by offering access to copy +from a designated place, offer equivalent access to copy the above +specified materials from the same place. +* **e)** Verify that the user has already received a copy of these +materials or that you have already sent this user a copy. + +For an executable, the required form of the “work that uses the +Library” must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + +It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + +**7.** You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + +* **a)** Accompany the combined library with a copy of the same work +based on the Library, uncombined with any other library +facilities. This must be distributed under the terms of the +Sections above. +* **b)** Give prominent notice with the combined library of the fact +that part of it is a work based on the Library, and explaining +where to find the accompanying uncombined form of the same work. + +**8.** You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + +**9.** You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +**10.** Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + +**11.** If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +**12.** If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + +**13.** The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +“any later version”, you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + +**14.** If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + +### NO WARRANTY + +**15.** BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY “AS IS” WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +**16.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + +To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +“copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a “copyright disclaimer” for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + +[Back to TOC](#table-of-contents) + +## GNU Free Documentation License + +Version 1.3, 3 November 2008 + +Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <> + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +### 0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document “free” in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of “copyleft”, which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +### 1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The “Document”, below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as “you”. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A “Modified Version” of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A “Secondary Section” is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The “Invariant Sections” are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The “Cover Texts” are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A “Transparent” copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not “Transparent” is called “Opaque”. + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The “Title Page” means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, “Title Page” means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +The “publisher” means any person or entity that distributes copies of +the Document to the public. + +A section “Entitled XYZ” means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as “Acknowledgements”, +“Dedications”, “Endorsements”, or “History”.) To “Preserve the Title” +of such a section when you modify the Document means that it remains a +section “Entitled XYZ” according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +### 2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no +other conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +### 3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to +give them a chance to provide you with an updated version of the +Document. + + +### 4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +* **A.** Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. +* **B.** List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. +* **C.** State on the Title page the name of the publisher of the +Modified Version, as the publisher. +* **D.** Preserve all the copyright notices of the Document. +* **E.** Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. +* **F.** Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. +* **G.** Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. +* **H.** Include an unaltered copy of this License. +* **I.** Preserve the section Entitled “History”, Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled “History” in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. +* **J.** Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the “History” section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. +* **K.** For any section Entitled “Acknowledgements” or “Dedications”, +Preserve the Title of the section, and preserve in the section all +the substance and tone of each of the contributor acknowledgements +and/or dedications given therein. +* **L.** Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. +* **M.** Delete any section Entitled “Endorsements”. Such a section +may not be included in the Modified Version. +* **N.** Do not retitle any existing section to be Entitled “Endorsements” +or to conflict in title with any Invariant Section. +* **O.** Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled “Endorsements”, provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +### 5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled “History” +in the various original documents, forming one section Entitled +“History”; likewise combine any sections Entitled “Acknowledgements”, +and any sections Entitled “Dedications”. You must delete all sections +Entitled “Endorsements”. + + +### 6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other +documents released under this License, and replace the individual +copies of this License in the various documents with a single copy +that is included in the collection, provided that you follow the rules +of this License for verbatim copying of each of the documents in all +other respects. + +You may extract a single document from such a collection, and +distribute it individually under this License, provided you insert a +copy of this License into the extracted document, and follow this +License in all other respects regarding verbatim copying of that +document. + + +### 7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an “aggregate” if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +### 8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled “Acknowledgements”, +“Dedications”, or “History”, the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +### 9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense, or distribute it is void, and +will automatically terminate your rights under this License. + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, receipt of a copy of some or all of the same material does +not give you any rights to use it. + + +### 10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions of the +GNU Free Documentation License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. See +<>. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License “or any later version” applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. If the Document +specifies that a proxy can decide which future versions of this +License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the +Document. + +### 11. RELICENSING + +“Massive Multiauthor Collaboration Site” (or “MMC Site”) means any +World Wide Web server that publishes copyrightable works and also +provides prominent facilities for anybody to edit those works. A +public wiki that anybody can edit is an example of such a server. A +“Massive Multiauthor Collaboration” (or “MMC”) contained in the site +means any set of copyrightable works thus published on the MMC site. + +“CC-BY-SA” means the Creative Commons Attribution-Share Alike 3.0 +license published by Creative Commons Corporation, a not-for-profit +corporation with a principal place of business in San Francisco, +California, as well as future copyleft versions of that license +published by that same organization. + +“Incorporate” means to publish or republish a Document, in whole or in +part, as part of another Document. + +An MMC is “eligible for relicensing” if it is licensed under this +License, and if all works that were first published under this License +somewhere other than this MMC, and subsequently incorporated in whole or +in part into the MMC, (1) had no cover texts or invariant sections, and +(2) were thus incorporated prior to November 1, 2008. + +The operator of an MMC Site may republish an MMC contained in the site +under CC-BY-SA on the same site at any time before August 1, 2009, +provided the MMC is eligible for relicensing. + + +## ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled “GNU + Free Documentation License”. + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the `with...Texts.` line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +[Back to TOC](#table-of-contents) + +## Thanks + +This library has been originally developed under the zencoders flags and this community has always supported me all along this work so I am happy to put the logo on this readme. + + When you see this tree, know that you've came across ZenCoders + + with open('ZenCoders. + `num` in numbers synchronized + datetime d glob. sys.argv[2] . + def myclass `..` @@oscla org. . class { + displ hooks( public static void ma functor: + $myclass->method( impport sys, os.pipe ` @param name` + fcl if(system(cmd) myc. /de ` $card( array("a" srand + format lists: ++: conc ++ "my an WHERE for( == myi + `sys: myvalue(myvalue) sys.t Console.W try{ rais using + connec SELECT * FROM table mycnf acco desc and or selector::clas at + openldap string sys. print "zenc der " { 'a': `ls -l` > appe &firs + import Tkinter paste( $obh &a or it myval bro roll: :: [] require a + case `` super. +y expr say " %rooms 1 --account fb- yy + proc meth Animate => send(D, open) putd EndIf 10 whi myc` cont + and main (--) import loop $$ or end onload UNION WITH tab timer 150 *2 + end. begin True GtkLabel *label doto partition te let auto i<- (i + d ); + .mushup ``/. ^/zenc/ myclass->her flv op <> element >> 71 or + QFileDi : and .. with myc toA channel::bo myc isEmpty a not bodt; + class T public pol str mycalc d pt &&a *i fc add ^ac + ::ZenCoders::core::namespac boost::function st f = std: ;; int assert + cout << endl public genera #include "b ost ::ac myna const cast mys + ac size_t return ran int (*getNextValue)(void) ff double sa_family_t famil + pu a do puts(" ac int main(int argc, char* "%5d struct nam + cs float for typedef enum puts getchar() + if( else #define fp FILE* f char* s + i++ strcat( %s int + 31] total+= do + }do while(1) sle + getc strcpy( a for + prin scanf(%d, & get + int void myfunc(int pa retu + BEQ BNEQZ R1 10 ANDI R1 R2 SYS + XOR SYSCALL 5 SLTIU MFLO 15 SW JAL + BNE BLTZAL R1 1 LUI 001 NOOP MULTU SLLV + MOV R1 ADD R1 R2 JUMP 10 1001 BEQ R1 R2 1 ANDI + 1101 1010001100 111 001 01 1010 101100 1001 100 + 110110 100 0 01 101 01100 100 100 1000100011 + 11101001001 00 11 100 11 10100010 + 000101001001 10 1001 101000101 + 010010010010110101001010 + +For further information: +visit our website https://zencoders.github.io + +**Author:** Sebastiano Merlino + +[Back to TOC](#table-of-contents) diff --git a/3rd_party/libhttpserver-0.18.2/acinclude.m4 b/3rd_party/libhttpserver-0.18.2/acinclude.m4 new file mode 100644 index 00000000..d9a951c0 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/acinclude.m4 @@ -0,0 +1,321 @@ +# This file is part of Autoconf. -*- Autoconf -*- + +# Copyright (C) 2004 Oren Ben-Kiki +# This file is distributed under the same terms as the Autoconf macro files. + +########## CHANGELOG ################## +# 2009-01-14 Martin Mann +# * DX_ARG_ABLE : new variable 'DX_FLAG_DX_CURRENT_FEATURE' +# * DX_CLEAR_DEPEND : use of explicit variable 'DX_FLAG_DX_CURRENT_FEATURE' +# in AC_SUBST instead of 'DX_FLAG[]DX_CURRENT_FEATURE' which is rejected by +# newer autotools + +# Generate automatic documentation using Doxygen. Works in concert with the +# aminclude.m4 file and a compatible doxygen configuration file. Defines the +# following public macros: +# +# DX_???_FEATURE(ON|OFF) - control the default setting fo a Doxygen feature. +# Supported features are 'DOXYGEN' itself, 'DOT' for generating graphics, +# 'HTML' for plain HTML, 'CHM' for compressed HTML help (for MS users), 'CHI' +# for generating a seperate .chi file by the .chm file, and 'MAN', 'RTF', +# 'XML', 'PDF' and 'PS' for the appropriate output formats. The environment +# variable DOXYGEN_PAPER_SIZE may be specified to override the default 'a4wide' +# paper size. +# +# By default, HTML, PDF and PS documentation is generated as this seems to be +# the most popular and portable combination. MAN pages created by Doxygen are +# usually problematic, though by picking an appropriate subset and doing some +# massaging they might be better than nothing. CHM and RTF are specific for MS +# (note that you can't generate both HTML and CHM at the same time). The XML is +# rather useless unless you apply specialized post-processing to it. +# +# The macro mainly controls the default state of the feature. The use can +# override the default by specifying --enable or --disable. The macros ensure +# that contradictory flags are not given (e.g., --enable-doxygen-html and +# --enable-doxygen-chm, --enable-doxygen-anything with --disable-doxygen, etc.) +# Finally, each feature will be automatically disabled (with a warning) if the +# required programs are missing. +# +# Once all the feature defaults have been specified, call DX_INIT_DOXYGEN with +# the following parameters: a one-word name for the project for use as a +# filename base etc., an optional configuration file name (the default is +# 'Doxyfile', the same as Doxygen's default), and an optional output directory +# name (the default is 'doxygen-doc'). + +## ----------## +## Defaults. ## +## ----------## + +DX_ENV="" +AC_DEFUN([DX_FEATURE_doc], ON) +AC_DEFUN([DX_FEATURE_dot], ON) +AC_DEFUN([DX_FEATURE_man], OFF) +AC_DEFUN([DX_FEATURE_html], ON) +AC_DEFUN([DX_FEATURE_chm], OFF) +AC_DEFUN([DX_FEATURE_chi], OFF) +AC_DEFUN([DX_FEATURE_rtf], OFF) +AC_DEFUN([DX_FEATURE_xml], OFF) +AC_DEFUN([DX_FEATURE_pdf], ON) +AC_DEFUN([DX_FEATURE_ps], ON) + +## --------------- ## +## Private macros. ## +## --------------- ## + +# DX_ENV_APPEND(VARIABLE, VALUE) +# ------------------------------ +# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen. +AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])]) + +# DX_DIRNAME_EXPR +# --------------- +# Expand into a shell expression prints the directory part of a path. +AC_DEFUN([DX_DIRNAME_EXPR], + [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']]) + +# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF) +# ------------------------------------- +# Expands according to the M4 (static) status of the feature. +AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])]) + +# DX_REQUIRE_PROG(VARIABLE, PROGRAM) +# ---------------------------------- +# Require the specified program to be found for the DX_CURRENT_FEATURE to work. +AC_DEFUN([DX_REQUIRE_PROG], [ +AC_PATH_TOOL([$1], [$2]) +if test "$DX_FLAG_DX_CURRENT_FEATURE$$1" = 1; then + AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION]) + AC_SUBST([DX_FLAG_DX_CURRENT_FEATURE], 0) +fi +]) + +# DX_TEST_FEATURE(FEATURE) +# ------------------------ +# Expand to a shell expression testing whether the feature is active. +AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1]) + +# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE) +# ------------------------------------------------- +# Verify that a required features has the right state before trying to turn on +# the DX_CURRENT_FEATURE. +AC_DEFUN([DX_CHECK_DEPEND], [ +test "$DX_FLAG_$1" = "$2" \ +|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1, + requires, contradicts) doxygen-DX_CURRENT_FEATURE]) +]) + +# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE) +# ---------------------------------------------------------- +# Turn off the DX_CURRENT_FEATURE if the required feature is off. +AC_DEFUN([DX_CLEAR_DEPEND], [ +test "$DX_FLAG_$1" = "$2" || AC_SUBST([DX_FLAG_DX_CURRENT_FEATURE], 0) +]) + + +# DX_FEATURE_ARG(FEATURE, DESCRIPTION, +# CHECK_DEPEND, CLEAR_DEPEND, +# REQUIRE, DO-IF-ON, DO-IF-OFF) +# -------------------------------------------- +# Parse the command-line option controlling a feature. CHECK_DEPEND is called +# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND), +# otherwise CLEAR_DEPEND is called to turn off the default state if a required +# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional +# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and +# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature. +AC_DEFUN([DX_ARG_ABLE], [ + AC_DEFUN([DX_CURRENT_FEATURE], [$1]) + AC_DEFUN([DX_FLAG_DX_CURRENT_FEATURE], [DX_FLAG_$1]) + AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2]) + AC_ARG_ENABLE(doxygen-$1, + [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1], + [--enable-doxygen-$1]), + DX_IF_FEATURE([$1], [don't $2], [$2]))], + [ +case "$enableval" in +#( +y|Y|yes|Yes|YES) + AC_SUBST([DX_FLAG_$1], 1) + $3 +;; #( +n|N|no|No|NO) + AC_SUBST([DX_FLAG_$1], 0) +;; #( +*) + AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1]) +;; +esac +], [ +AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)]) +$4 +]) +if DX_TEST_FEATURE([$1]); then + $5 + : +fi +if DX_TEST_FEATURE([$1]); then + AM_CONDITIONAL(DX_COND_$1, :) + $6 + : +else + AM_CONDITIONAL(DX_COND_$1, false) + $7 + : +fi +]) + +## -------------- ## +## Public macros. ## +## -------------- ## + +# DX_XXX_FEATURE(DEFAULT_STATE) +# ----------------------------- +AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])]) +AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])]) +AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])]) +AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])]) +AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])]) +AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])]) +AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) +AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) +AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])]) +AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])]) + +# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR]) +# --------------------------------------------------------- +# PROJECT also serves as the base name for the documentation files. +# The default CONFIG-FILE is "Doxyfile" and OUTPUT-DOC-DIR is "doxygen-doc". +AC_DEFUN([DX_INIT_DOXYGEN], [ + +# Files: +AC_SUBST([DX_PROJECT], [$1]) +AC_SUBST([DX_CONFIG], [ifelse([$2], [], Doxyfile, [$2])]) +AC_SUBST([DX_DOCDIR], [ifelse([$3], [], doxygen-doc, [$3])]) + +# Environment variables used inside doxygen.cfg: +DX_ENV_APPEND(SRCDIR, $srcdir) +DX_ENV_APPEND(PROJECT, $DX_PROJECT) +DX_ENV_APPEND(DOCDIR, $DX_DOCDIR) +DX_ENV_APPEND(VERSION, $PACKAGE_VERSION) + +# Doxygen itself: +DX_ARG_ABLE(doc, [generate any doxygen documentation], + [], + [], + [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen) + DX_REQUIRE_PROG([DX_PERL], perl)], + [DX_ENV_APPEND(PERL_PATH, $DX_PERL)]) + +# Dot for graphics: +DX_ARG_ABLE(dot, [generate graphics for doxygen documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_DOT], dot)], + [DX_ENV_APPEND(HAVE_DOT, YES) + DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])], + [DX_ENV_APPEND(HAVE_DOT, NO)]) + +# Man pages generation: +DX_ARG_ABLE(man, [generate doxygen manual pages], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_MAN, YES)], + [DX_ENV_APPEND(GENERATE_MAN, NO)]) + +# RTF file generation: +DX_ARG_ABLE(rtf, [generate doxygen RTF documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_RTF, YES)], + [DX_ENV_APPEND(GENERATE_RTF, NO)]) + +# XML file generation: +DX_ARG_ABLE(xml, [generate doxygen XML documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_XML, YES)], + [DX_ENV_APPEND(GENERATE_XML, NO)]) + +# (Compressed) HTML help generation: +DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_HHC], hhc)], + [DX_ENV_APPEND(HHC_PATH, $DX_HHC) + DX_ENV_APPEND(GENERATE_HTML, YES) + DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], + [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) + +# Seperate CHI file generation. +DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file], + [DX_CHECK_DEPEND(chm, 1)], + [DX_CLEAR_DEPEND(chm, 1)], + [], + [DX_ENV_APPEND(GENERATE_CHI, YES)], + [DX_ENV_APPEND(GENERATE_CHI, NO)]) + +# Plain HTML pages generation: +DX_ARG_ABLE(html, [generate doxygen plain HTML documentation], + [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)], + [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)], + [], + [DX_ENV_APPEND(GENERATE_HTML, YES)], + [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)]) + +# PostScript file generation: +DX_ARG_ABLE(ps, [generate doxygen PostScript documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_LATEX], latex) + DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) + DX_REQUIRE_PROG([DX_DVIPS], dvips) + DX_REQUIRE_PROG([DX_EGREP], egrep)]) + +# PDF file generation: +DX_ARG_ABLE(pdf, [generate doxygen PDF documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex) + DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) + DX_REQUIRE_PROG([DX_EGREP], egrep)]) + +# LaTeX generation for PS and/or PDF: +if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then + AM_CONDITIONAL(DX_COND_latex, :) + DX_ENV_APPEND(GENERATE_LATEX, YES) +else + AM_CONDITIONAL(DX_COND_latex, false) + DX_ENV_APPEND(GENERATE_LATEX, NO) +fi + +# Paper size for PS and/or PDF: +AC_ARG_VAR(DOXYGEN_PAPER_SIZE, + [a4wide (default), a4, letter, legal or executive]) +case "$DOXYGEN_PAPER_SIZE" in +#( +"") + AC_SUBST(DOXYGEN_PAPER_SIZE, "") +;; #( +a4wide|a4|letter|legal|executive) + DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE) +;; #( +*) + AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE']) +;; +esac + +#For debugging: +#echo DX_FLAG_doc=$DX_FLAG_doc +#echo DX_FLAG_dot=$DX_FLAG_dot +#echo DX_FLAG_man=$DX_FLAG_man +#echo DX_FLAG_html=$DX_FLAG_html +#echo DX_FLAG_chm=$DX_FLAG_chm +#echo DX_FLAG_chi=$DX_FLAG_chi +#echo DX_FLAG_rtf=$DX_FLAG_rtf +#echo DX_FLAG_xml=$DX_FLAG_xml +#echo DX_FLAG_pdf=$DX_FLAG_pdf +#echo DX_FLAG_ps=$DX_FLAG_ps +#echo DX_ENV=$DX_ENV +]) diff --git a/3rd_party/libhttpserver-0.18.2/aminclude.am b/3rd_party/libhttpserver-0.18.2/aminclude.am new file mode 100644 index 00000000..420049ec --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/aminclude.am @@ -0,0 +1,186 @@ +# Copyright (C) 2004 Oren Ben-Kiki +# This file is distributed under the same terms as the Automake macro files. + +# Generate automatic documentation using Doxygen. Goals and variables values +# are controlled by the various DX_COND_??? conditionals set by autoconf. +# +# The provided goals are: +# doxygen-doc: Generate all doxygen documentation. +# doxygen-run: Run doxygen, which will generate some of the documentation +# (HTML, CHM, CHI, MAN, RTF, XML) but will not do the post +# processing required for the rest of it (PS, PDF, and some MAN). +# doxygen-man: Rename some doxygen generated man pages. +# doxygen-ps: Generate doxygen PostScript documentation. +# doxygen-pdf: Generate doxygen PDF documentation. +# +# Note that by default these are not integrated into the automake goals. If +# doxygen is used to generate man pages, you can achieve this integration by +# setting man3_MANS to the list of man pages generated and then adding the +# dependency: +# +# $(man3_MANS): doxygen-doc +# +# This will cause make to run doxygen and generate all the documentation. +# +# The following variable is intended for use in Makefile.am: +# +# DX_CLEANFILES = everything to clean. +# +# This is usually added to MOSTLYCLEANFILES. + +## --------------------------------- ## +## Format-independent Doxygen rules. ## +## --------------------------------- ## + +if DX_COND_doc + +## ------------------------------- ## +## Rules specific for HTML output. ## +## ------------------------------- ## + +if DX_COND_html + +DX_CLEAN_HTML = @DX_DOCDIR@/html + +endif DX_COND_html + +## ------------------------------ ## +## Rules specific for CHM output. ## +## ------------------------------ ## + +if DX_COND_chm + +DX_CLEAN_CHM = @DX_DOCDIR@/chm + +if DX_COND_chi + +DX_CLEAN_CHI = @DX_DOCDIR@/@PACKAGE@.chi + +endif DX_COND_chi + +endif DX_COND_chm + +## ------------------------------ ## +## Rules specific for MAN output. ## +## ------------------------------ ## + +if DX_COND_man + +DX_CLEAN_MAN = @DX_DOCDIR@/man + +endif DX_COND_man + +## ------------------------------ ## +## Rules specific for RTF output. ## +## ------------------------------ ## + +if DX_COND_rtf + +DX_CLEAN_RTF = @DX_DOCDIR@/rtf + +endif DX_COND_rtf + +## ------------------------------ ## +## Rules specific for XML output. ## +## ------------------------------ ## + +if DX_COND_xml + +DX_CLEAN_XML = @DX_DOCDIR@/xml + +endif DX_COND_xml + +## ----------------------------- ## +## Rules specific for PS output. ## +## ----------------------------- ## + +if DX_COND_ps + +DX_CLEAN_PS = @DX_DOCDIR@/@PACKAGE@.ps + +DX_PS_GOAL = doxygen-ps + +doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps + +@DX_DOCDIR@/@PACKAGE@.ps: @DX_DOCDIR@/@PACKAGE@.tag + cd @DX_DOCDIR@/latex; \ + rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \ + $(DX_LATEX) refman.tex; \ + $(MAKEINDEX_PATH) refman.idx; \ + $(DX_LATEX) refman.tex; \ + countdown=5; \ + while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \ + refman.log > /dev/null 2>&1 \ + && test $$countdown -gt 0; do \ + $(DX_LATEX) refman.tex; \ + countdown=`expr $$countdown - 1`; \ + done; \ + $(DX_DVIPS) -o ../@PACKAGE@.ps refman.dvi + +endif DX_COND_ps + +## ------------------------------ ## +## Rules specific for PDF output. ## +## ------------------------------ ## + +if DX_COND_pdf + +DX_CLEAN_PDF = @DX_DOCDIR@/@PACKAGE@.pdf + +DX_PDF_GOAL = doxygen-pdf + +doxygen-pdf: @DX_DOCDIR@/@PACKAGE@.pdf + +@DX_DOCDIR@/@PACKAGE@.pdf: @DX_DOCDIR@/@PACKAGE@.tag + cd @DX_DOCDIR@/latex; \ + rm -f *.aux *.toc *.idx *.ind *.ilg *.log *.out; \ + $(DX_PDFLATEX) refman.tex; \ + $(DX_MAKEINDEX) refman.idx; \ + $(DX_PDFLATEX) refman.tex; \ + countdown=5; \ + while $(DX_EGREP) 'Rerun (LaTeX|to get cross-references right)' \ + refman.log > /dev/null 2>&1 \ + && test $$countdown -gt 0; do \ + $(DX_PDFLATEX) refman.tex; \ + countdown=`expr $$countdown - 1`; \ + done; \ + mv refman.pdf ../@PACKAGE@.pdf + +endif DX_COND_pdf + +## ------------------------------------------------- ## +## Rules specific for LaTeX (shared for PS and PDF). ## +## ------------------------------------------------- ## + +if DX_COND_latex + +DX_CLEAN_LATEX = @DX_DOCDIR@/latex + +endif DX_COND_latex + +.PHONY: doxygen-run doxygen-doc $(DX_PS_GOAL) $(DX_PDF_GOAL) + +.INTERMEDIATE: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL) + +doxygen-run: @DX_DOCDIR@/@PACKAGE@.tag + +doxygen-doc: doxygen-run $(DX_PS_GOAL) $(DX_PDF_GOAL) + +@DX_DOCDIR@/@PACKAGE@.tag: $(DX_CONFIG) $(pkginclude_HEADERS) + rm -rf @DX_DOCDIR@ + $(DX_ENV) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG) + +DX_CLEANFILES = \ + @DX_DOCDIR@/@PACKAGE@.tag \ + -r \ + $(DX_CLEAN_HTML) \ + $(DX_CLEAN_CHM) \ + $(DX_CLEAN_CHI) \ + $(DX_CLEAN_MAN) \ + $(DX_CLEAN_RTF) \ + $(DX_CLEAN_XML) \ + $(DX_CLEAN_PS) \ + $(DX_CLEAN_PDF) \ + $(DX_CLEAN_LATEX) + +endif DX_COND_doc diff --git a/3rd_party/libhttpserver-0.18.2/appveyor.yml b/3rd_party/libhttpserver-0.18.2/appveyor.yml new file mode 100644 index 00000000..c86df7e5 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/appveyor.yml @@ -0,0 +1,24 @@ +platform: x64 + +environment: + matrix: + - compiler: msys2 + MINGW_CHOST: x86_64-w64-mingw32 + MSYS2_ARCH: x86_64 +init: + - 'echo Building libhttpserver %version% for Windows' + - 'echo System architecture: %PLATFORM%' + - 'echo Repo build branch is: %APPVEYOR_REPO_BRANCH%' + - 'echo Build folder is: %APPVEYOR_BUILD_FOLDER%' + - 'echo Repo build commit is: %APPVEYOR_REPO_COMMIT%' + - 'echo Cygwin root is: %CYG_ROOT%' +install: + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -msys2 -c "pacman --noconfirm --force -S --needed mingw-w64-$MSYS2_ARCH-{libtool,make,pkg-config,libsystre,doxygen,gnutls,graphviz,curl}"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && tar -xzf libmicrohttpd-0.9.59.tar.gz"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER/libmicrohttpd-0.9.59 && ./configure --disable-examples --enable-poll=no --prefix /C/msys64 && make && make install"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && ./bootstrap"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && mkdir build && cd build && MANIFEST_TOOL=no; ../configure --disable-fastopen --prefix /C/msys64 CXXFLAGS=-I/C/msys64/include LDFLAGS=-L/C/msys64/lib; make"' +build_script: + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER/build && make check"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER/build && cat test/test-suite.log"' diff --git a/3rd_party/libhttpserver-0.18.2/bootstrap b/3rd_party/libhttpserver-0.18.2/bootstrap new file mode 100644 index 00000000..bc1ad731 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/bootstrap @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# +# This file is part of libhttpserver +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +aclocal -I m4 +autoheader +if [[ "$OSTYPE" == "darwin"* ]]; then + glibtoolize --automake +else + libtoolize --automake +fi +automake --add-missing +autoconf + +# Optionally do the build as well. +if [ "$1" = "-build" -o "$1" = "--build" ] ; then + shift + ./configure "$@" + make + make check +fi diff --git a/3rd_party/libhttpserver-0.18.2/ci-report-coverage b/3rd_party/libhttpserver-0.18.2/ci-report-coverage new file mode 100644 index 00000000..d8f72e16 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/ci-report-coverage @@ -0,0 +1,19 @@ +#!/bin/bash + +find ../ -name *.gcda + +echo "Copying files to the correct locations for correct gcov analysis" +cp -R ../src/* ./src/ +cp -R ../src/details/* ./src/details/ +cp -R ../src/httpserver/* ./src/httpserver/ +cp -R ../test/* ./test/ +cp -R ../test/integ/* ./test/integ/ +cp -R ../test/unit/* ./test/unit/ + +echo "Sending json report" +for filename in `find . | egrep '\.cpp'`; +do + gcov -n -o . $filename > /dev/null; +done + +codecov diff --git a/3rd_party/libhttpserver-0.18.2/cmakemodule/FindLibHttpServer.cmake b/3rd_party/libhttpserver-0.18.2/cmakemodule/FindLibHttpServer.cmake new file mode 100644 index 00000000..8617c252 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/cmakemodule/FindLibHttpServer.cmake @@ -0,0 +1,31 @@ +# - Find LibHttpServer + +if(LIBHTTPSERVER_INCLUDE_DIRS AND LIBHTTPSERVER_LIBRARIES) + set(LIBHTTPSERVER_FOUND TRUE) + +else(LIBHTTPSERVER_INCLUDE_DIRS AND LIBHTTPSERVER_LIBRARIES) + find_path(LIBHTTPSERVER_INCLUDE_DIRS httpserverpp + /usr/include + /usr/include/httpserver + /usr/local/include/ + /usr/local/include/httpserver + ) + + find_library(LIBHTTPSERVER_LIBRARIES NAMES httpserver + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + ) + + if(LIBHTTPSERVER_INCLUDE_DIRS AND LIBHTTPSERVER_LIBRARIES) + set(LIBHTTPSERVER_FOUND TRUE) + message(STATUS "Found libhttpserver: ${LIBHTTPSERVER_INCLUDE_DIRS}, ${LIBHTTPSERVER_LIBRARIES}") + else(LIBHTTPSERVER_INCLUDE_DIRS AND LIBHTTPSERVER_LIBRARIES) + set(LIBHTTPSERVER_FOUND FALSE) + message(STATUS "libhttpserver not found.") + endif(LIBHTTPSERVER_INCLUDE_DIRS AND LIBHTTPSERVER_LIBRARIES) + + mark_as_advanced(LIBHTTPSERVER_INCLUDE_DIRS LIBHTTPSERVER_LIBRARIES) + +endif(LIBHTTPSERVER_INCLUDE_DIRS AND LIBHTTPSERVER_LIBRARIES) diff --git a/3rd_party/libhttpserver-0.18.2/configure.ac b/3rd_party/libhttpserver-0.18.2/configure.ac new file mode 100644 index 00000000..8fc0e314 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/configure.ac @@ -0,0 +1,378 @@ +# +# This file is part of libhttpserver +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.57) +m4_define([libhttpserver_MAJOR_VERSION],[0])dnl +m4_define([libhttpserver_MINOR_VERSION],[18])dnl +m4_define([libhttpserver_REVISION],[1])dnl +m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl +m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl +AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) +AM_INIT_AUTOMAKE([subdir-objects]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CANONICAL_HOST + +AX_VALGRIND_DFLT +AX_VALGRIND_CHECK + +OLD_CXXFLAGS=$CXXFLAGS +LT_INIT +AC_PROG_CC +AC_PROG_CXX() +AC_PROG_LN_S +CXXFLAGS=$OLD_CXXFLAGS +AC_LANG([C++]) +AC_SYS_LARGEFILE + +native_srcdir=$srcdir + +AC_MSG_CHECKING([whether it is possible to compile in the same directory]) +AC_ARG_ENABLE([same-directory-build], + [AS_HELP_STRING([--enable-same-directory-build], + [enable to compile in the same directory. This is heavily discouraged. (def=no)])], + [samedirectory="$enableval"], + [samedirectory=no]) +AC_MSG_RESULT([$samedirectory]) + +if test x"$samedirectory" = x"no"; then + if test "`cd $srcdir; /bin/pwd`" = "`/bin/pwd`"; then + AC_MSG_ERROR("you must configure in a separate build directory") + fi +fi + +is_windows=yes; +NETWORK_LIBS="" +case "$host" in + *-mingw*) + NETWORK_HEADER="winsock2.h" + ADDITIONAL_LIBS="-lpthread -no-undefined" + NETWORK_LIBS="-lws2_32" + native_srcdir=$(cd $srcdir; pwd -W) + ;; + *-cygwin*) + NETWORK_HEADER="arpa/inet.h" + ADDITIONAL_LIBS="-lpthread -no-undefined" + ;; + *) + NETWORK_HEADER="arpa/inet.h" + ADDITIONAL_LIBS="" + is_windows=no + ;; +esac + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADER([stdint.h],[],[AC_MSG_ERROR("stdint.h not found")]) +AC_CHECK_HEADER([inttypes.h],[],[AC_MSG_ERROR("inttypes.h not found")]) +AC_CHECK_HEADER([errno.h],[],[AC_MSG_ERROR("errno.h not found")]) +AC_CHECK_HEADER([unistd.h],[],[AC_MSG_ERROR("unistd.h not found")]) +AC_CHECK_HEADER([ctype.h],[],[AC_MSG_ERROR("cctype not found")]) +AC_CHECK_HEADER([sys/stat.h],[],[AC_MSG_ERROR("sys/stat.h not found")]) +AC_CHECK_HEADER([sys/types.h],[],[AC_MSG_ERROR("sys/types.h not found")]) +AC_CHECK_HEADER([$NETWORK_HEADER],[],[AC_MSG_ERROR("$NETWORK_HEADER not found")]) +AC_CHECK_HEADER([signal.h],[],[AC_MSG_ERROR("signal.h not found")]) + +AC_CHECK_HEADER([gnutls/gnutls.h],[have_gnutls="yes"],[AC_MSG_WARN("gnutls/gnutls.h not found. TLS will be disabled"); have_gnutls="no"]) + +# Checks for libmicrohttpd +if test x"$host" = x"$build"; then + AC_CHECK_HEADER([microhttpd.h], + AC_CHECK_LIB([microhttpd], [MHD_get_fdset2], + [AC_MSG_CHECKING([for libmicrohttpd >= 0.9.52]) + AC_COMPILE_IFELSE( + [AC_LANG_SOURCE([ + #include + #if (MHD_VERSION < 0x00095102) + #error needs at least version 0.9.52 + #endif + int main () { return 0; } + ])], + [], + [AC_MSG_ERROR("libmicrohttpd is too old - install libmicrohttpd >= 0.9.52")] + ) + ], + [AC_MSG_ERROR(["libmicrohttpd not found"])] + ), + [AC_MSG_ERROR(["microhttpd.h not found"])] + ) + + CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" + LDFLAGS="$LIBMICROHTTPD_LIBS $NETWORK_LIBS $ADDITIONAL_LIBS $LDFLAGS" + + cond_cross_compile="no" +else + AC_CHECK_HEADER([microhttpd.h], + AC_CHECK_LIB([microhttpd], [MHD_get_fdset2], + [], + [AC_MSG_ERROR(["libmicrohttpd not found"])] + ), + [AC_MSG_ERROR(["microhttpd.h not found"])] + ) + + CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $CXXFLAGS" + LDFLAGS="$NETWORK_LIBS $ADDITIONAL_LIBS $LDFLAGS" + + cond_cross_compile="yes" +fi + +AM_CONDITIONAL([COND_CROSS_COMPILE],[test x"$cond_cross_compile" = x"yes"]) +AC_SUBST(COND_CROSS_COMPILE) + +AC_MSG_CHECKING([whether to build with TCP_FASTOPEN support]) +AC_ARG_ENABLE([fastopen], + [AS_HELP_STRING([--enable-fastopen], + [enable use of TCP_FASTOPEN (def=yes)])], + [fastopen="$enableval"], + [fastopen=yes]) +AC_MSG_RESULT([$fastopen]) + +is_fastopen_supported=no; +if test x"$fastopen" = x"yes"; then + if test x"$is_windows" = x"no"; then + if test `uname -r |cut -d. -f1` -ge 3; then + if test `uname -r |cut -d. -f2` -ge 7; then + CXXFLAGS="-DUSE_FASTOPEN $CXXFLAGS"; + is_fastopen_supported=yes; + fi + fi + fi +fi + +AC_ARG_ENABLE([[poll]], + [AS_HELP_STRING([[--enable-poll[=ARG]]], [enable poll support (yes, no, auto) [auto]])], + [enable_poll=${enableval}], + [enable_poll='auto'] + ) + +if test "$enable_poll" != "no"; then + if test "$os_is_native_w32" != "yes"; then + AC_CHECK_HEADERS([poll.h], + [ + AC_CHECK_FUNCS([poll], [have_poll='yes'], [have_poll='no']) + ], [], [AC_INCLUDES_DEFAULT]) + else + AC_MSG_CHECKING([for WSAPoll()]) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[ +#include + ]], [[ +WSAPOLLFD fda[2]; +WSAPoll(fda, 2, 0);]])], + [ + have_poll='yes' + AC_DEFINE([HAVE_POLL],[1]) + ], [have_poll='no']) + AC_MSG_RESULT([$have_poll]) + fi + if test "$enable_poll" = "yes" && test "$have_poll" != "yes"; then + AC_MSG_ERROR([[Support for poll was explicitly requested but cannot be enabled on this platform.]]) + fi + enable_poll="$have_poll" +fi + +if test x"$enable_poll" = x"yes"; then + AM_CXXFLAGS="$AM_CXXFLAGS -DENABLE_POLL" + AM_CFLAGS="$AM_CXXFLAGS -DENABLE_POLL" +fi + +AC_ARG_ENABLE([[epoll]], + [AS_HELP_STRING([[--enable-epoll[=ARG]]], [enable epoll support (yes, no, auto) [auto]])], + [enable_epoll=${enableval}], + [enable_epoll='auto'] + ) + +if test "$enable_epoll" != "no"; then + AX_HAVE_EPOLL + if test "${ax_cv_have_epoll}" = "yes"; then + AC_DEFINE([[EPOLL_SUPPORT]],[[1]],[Define to 1 to enable epoll support]) + enable_epoll='yes' + else + if test "$enable_epoll" = "yes"; then + AC_MSG_ERROR([[Support for epoll was explicitly requested but cannot be enabled on this platform.]]) + fi + enable_epoll='no' + fi +fi + +AM_CONDITIONAL([MHD_HAVE_EPOLL], [[test "x$enable_epoll" = xyes]]) + +if test "x$enable_epoll" = "xyes"; then + AC_CACHE_CHECK([for epoll_create1()], [mhd_cv_have_epoll_create1], [ + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[ +#include + ]], [[ +int fd; +fd = epoll_create1(EPOLL_CLOEXEC);]])], + [mhd_cv_have_epoll_create1=yes], + [mhd_cv_have_epoll_create1=no])]) + AS_IF([test "x$mhd_cv_have_epoll_create1" = "xyes"],[ + AC_DEFINE([[HAVE_EPOLL_CREATE1]], [[1]], [Define if you have epoll_create1 function.])]) +fi + +if test x"$enable_epoll" = x"yes"; then + AM_CXXFLAGS="$AM_CXXFLAGS -DENABLE_EPOLL" + AM_CFLAGS="$AM_CXXFLAGS -DENABLE_EPOLL" +fi + +AC_MSG_CHECKING([whether to link statically]) +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static], + [enable use static linking (def=yes)])], + [static="$enableval"], + [static=yes]) +AC_MSG_RESULT([$static]) + +if test x"$static" = x"$yes"; then + LDFLAGS="-static $LDFLAGS"; +fi + +m4_pattern_allow([AC_TYPE_SIZE_T]) +m4_pattern_allow([AC_TYPE_UINT16_T]) +m4_pattern_allow([AC_TYPE_UINT32_T]) +m4_pattern_allow([AC_TYPE_UINT64_T]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_SIZE_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T + +LHT_LIBDEPS="-lmicrohttpd" + +AC_MSG_CHECKING([whether to build with debug information]) +AC_ARG_ENABLE([debug], + [AS_HELP_STRING([--enable-debug], + [enable debug data generation (def=no)])], + [debugit="$enableval"], + [debugit=no]) +AC_MSG_RESULT([$debugit]) + +AM_LDFLAGS="-lstdc++" + +if test x"$debugit" = x"yes"; then + AC_DEFINE([DEBUG],[],[Debug Mode]) + AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" + AM_CFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" +else + AC_DEFINE([NDEBUG],[],[No-debug Mode]) + AM_CXXFLAGS="$AM_CXXFLAGS -O3" + AM_CFLAGS="$AM_CXXFLAGS -O3" +fi + +case $host_os in + darwin* ) + AM_CXXFLAGS="$AM_CXXFLAGS -DDARWIN" + AM_CFLAGS="$AM_CFLAGS -DDARWIN" + ;; + freebsd* ) + AM_LDFLAGS="" + ;; +esac + +AC_MSG_CHECKING([whether to build with coverage information]) +AC_ARG_ENABLE([coverage], + [AS_HELP_STRING([--enable-coverage], + [enable coverage data generation (def=no)])], + [coverit="$enableval"], + [coverit=no]) +AC_MSG_RESULT([$coverit]) + +if test x"$coverit" = x"yes"; then + case $host_os in + darwin* ) + echo "Coverage not supported on OSX" + cond_gcov="no" + ;; + *) + AC_CHECK_PROG(GCOV, gcov, gcov) + cond_gcov="yes" + ;; + esac +fi + +AC_ARG_ENABLE([[examples]], + [AS_HELP_STRING([[--disable-examples]], [do not build any examples])], , + [enable_examples=yes]) +test "x$enable_examples" = "xno" || enable_examples=yes +AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$enable_examples" = "xyes"]) + +AM_CONDITIONAL([COND_GCOV],[test x"$cond_gcov" = x"yes"]) +AC_SUBST(COND_GCOV) + +if test x"have_gnutls" = x"yes"; then + AM_CXXFLAGS="$AM_CXXFLAGS -DHAVE_GNUTLS" + AM_CFLAGS="$AM_CXXFLAGS -DHAVE_GNUTLS" +fi + +DX_HTML_FEATURE(ON) +DX_CHM_FEATURE(OFF) +DX_CHI_FEATURE(OFF) +DX_MAN_FEATURE(ON) +DX_RTF_FEATURE(OFF) +DX_XML_FEATURE(OFF) +DX_PDF_FEATURE(OFF) +DX_PS_FEATURE(OFF) +DX_INIT_DOXYGEN([$PACKAGE_NAME],[doxyconfig.in]) + +LDFLAGS="$LDFLAGS -version-number libhttpserver_LDF_VERSION" + +AC_SUBST(LHT_LIBDEPS) +AC_SUBST(AM_CXXFLAGS) +AC_SUBST(AM_CFLAGS) +AC_SUBST(AM_LDFLAGS) +AC_SUBST(CPPFLAGS) +AC_SUBST(LIBS) +AC_SUBST(LDFLAGS) +AC_SUBST(EXT_LIB_PATH) +AC_SUBST(EXT_LIBS) + +AC_CONFIG_FILES([test/test_content:test/test_content]) +AC_CONFIG_FILES([test/cert.pem:test/cert.pem]) +AC_CONFIG_FILES([test/key.pem:test/key.pem]) +AC_CONFIG_FILES([test/test_root_ca.pem:test/test_root_ca.pem]) +AC_CONFIG_FILES([test/libhttpserver.supp:test/libhttpserver.supp]) +AC_CONFIG_FILES([examples/cert.pem:examples/cert.pem]) +AC_CONFIG_FILES([examples/key.pem:examples/key.pem]) +AC_CONFIG_FILES([examples/test_content:examples/test_content]) + +AC_OUTPUT( + libhttpserver.pc + Makefile + doc/Makefile + src/Makefile + test/Makefile + examples/Makefile +) + +AC_MSG_NOTICE([Configuration Summary: + Operating System: ${host_os} + Target directory: ${prefix} + License : LGPL only + Debug : ${debugit} + TLS Enabled : ${have_gnutls} + TCP_FASTOPEN : ${is_fastopen_supported} + poll support : ${enable_poll=no} + epoll support : ${enable_epoll=no} + Static : ${static} + Build examples : ${enable_examples} +]) diff --git a/3rd_party/libhttpserver-0.18.2/custom_iwyu.imp b/3rd_party/libhttpserver-0.18.2/custom_iwyu.imp new file mode 100644 index 00000000..e814a310 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/custom_iwyu.imp @@ -0,0 +1,17 @@ +[ + { include: ["\"microhttpd.h\"", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + + { symbol: ["std::exception", "private", "", "public"]}, + { symbol: ["std::shared_ptr", "private", "", "public"]}, + { symbol: ["std::uint16_t", "private", "", "public"]}, + { symbol: ["std::uint64_t", "private", "", "public"]}, + { symbol: ["std::istringstream", "private", "", "public"]}, + { symbol: ["std::stringstream", "private", "", "public"]}, + { symbol: ["std::ifstream", "private", "", "public"]}, + + { symbol: ["uint16_t", "private", "", "public"]}, + { symbol: ["uint64_t", "private", "", "public"]}, + + { symbol: ["MHD_Connection", "private", "", "public"]}, +] diff --git a/3rd_party/libhttpserver-0.18.2/doc/Makefile.am b/3rd_party/libhttpserver-0.18.2/doc/Makefile.am new file mode 100644 index 00000000..3598b431 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/doc/Makefile.am @@ -0,0 +1,20 @@ +# +# This file is part of libhttpserver +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +man_MANS = libhttpserver.3 +EXTRA_DIST = $(man_MANS) diff --git a/3rd_party/libhttpserver-0.18.2/doc/libhttpserver.3 b/3rd_party/libhttpserver-0.18.2/doc/libhttpserver.3 new file mode 100644 index 00000000..edcea3a9 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/doc/libhttpserver.3 @@ -0,0 +1,59 @@ +.\" Copyright (c) 2014, Sebastiano Merlino +.\" +.\" %%%LICENSE_START(VERBATIM) +.\" Permission is granted to make and distribute verbatim copies of this +.\" manual provided the copyright notice and this permission notice are +.\" preserved on all copies. +.\" +.\" Permission is granted to copy and distribute modified versions of +.\" this manual under the conditions for verbatim copying, provided that +.\" the entire resulting derived work is distributed under the terms of +.\" a permission notice identical to this one. +.\" +.\" Since the Linux kernel and libraries are constantly changing, this +.\" manual page may be incorrect or out-of-date. The author(s) assume. +.\" no responsibility for errors or omissions, or for damages resulting. +.\" from the use of the information contained herein. The author(s) may. +.\" not have taken the same level of care in the production of this. +.\" manual, which is licensed free of charge, as they might when working. +.\" professionally. +.\" +.\" Formatted or processed versions of this manual, if unaccompanied by +.\" the source, must acknowledge the copyright and authors of this work. +.\" %%%LICENSE_END + +.TH LIBHTTPSERVER "3" "02 Mar 2013 "libhttpserver" +.SH "NAME" +libhttpserver \- C++ library for creating an embedded Rest HTTP server (and more) +.SH "SYNOPSIS" + + \fB#include + +.SH "DESCRIPTION" +.P +libhttpserver is an api made with the intent to allow to easily realize Rest based webservers. +.P +The details of the API are described in a detailed documentation and in doxygen generated code reference. +.P +.SH "SEE ALSO" +\fBcurl\fP(1), \fBlibcurl\fP(3), \fBlibmicrohttpd\fP(3) + +.SH "LEGAL NOTICE" +libhttpserver is released under the LGPL Version 2.1 or higher. For details on the license please read the appendix in the manual. + +.SH "FILES" +.TP +httpserver.hpp +libhttpserver include file +.TP +libhttpserver.so +libhttpserver library + +.SH "REPORTING BUGS" +Report bugs by using github issue tracker . + +.SH "AUTHORS" +GNU libhttpserver is designed and realized by Sebastiano Merlino . + +.SH "AVAILABILITY" +You can obtain the latest version from https://github.com/etr/libhttpserver . diff --git a/3rd_party/libhttpserver-0.18.2/examples/Makefile.am b/3rd_party/libhttpserver-0.18.2/examples/Makefile.am new file mode 100644 index 00000000..318a7a8d --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/Makefile.am @@ -0,0 +1,43 @@ +# +# This file is part of libhttpserver +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +LDADD = $(top_builddir)/src/libhttpserver.la +AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ +METASOURCES = AUTO +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads benchmark_nodelay deferred_with_accumulator + +hello_world_SOURCES = hello_world.cpp +service_SOURCES = service.cpp +minimal_hello_world_SOURCES = minimal_hello_world.cpp +custom_error_SOURCES = custom_error.cpp +allowing_disallowing_methods_SOURCES = allowing_disallowing_methods.cpp +handlers_SOURCES = handlers.cpp +hello_with_get_arg_SOURCES = hello_with_get_arg.cpp +setting_headers_SOURCES = setting_headers.cpp +custom_access_log_SOURCES = custom_access_log.cpp +basic_authentication_SOURCES = basic_authentication.cpp +digest_authentication_SOURCES = digest_authentication.cpp +minimal_https_SOURCES = minimal_https.cpp +minimal_file_response_SOURCES = minimal_file_response.cpp +minimal_deferred_SOURCES = minimal_deferred.cpp +deferred_with_accumulator_SOURCES = deferred_with_accumulator.cpp +url_registration_SOURCES = url_registration.cpp +minimal_ip_ban_SOURCES = minimal_ip_ban.cpp +benchmark_select_SOURCES = benchmark_select.cpp +benchmark_threads_SOURCES = benchmark_threads.cpp +benchmark_nodelay_SOURCES = benchmark_nodelay.cpp diff --git a/3rd_party/libhttpserver-0.18.2/examples/README.md b/3rd_party/libhttpserver-0.18.2/examples/README.md new file mode 100644 index 00000000..d2faca33 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/README.md @@ -0,0 +1,58 @@ +Example Programs +================ + +hello_world.cpp - a very simple example of using libhttpserver to + create a Rest server capable of receiving and processing + HTTP requests. The server will be listening on port + 8080. + + +service.cpp - an example using more of the libhttpserver API. + This creates a Rest server capable of running with + HTTP or HTTPS (provided that libhttpserver and + libmicrohttpd have been compiled with SSL support. + + The server can be configured via command line + arguments: + + -p - port number to listen on (default 8080) + -s - enable HTTPS + -k - server key filename (default "key.pem") + -c - server certificate filename (default "cert.pem") + +Creating Certificates +===================== +Self-signed certificates can be created using OpenSSL using the +following steps: + + $ openssl genrsa -des3 -passout pass:x -out server.pass.key 2048 + $ openssl rsa -passin pass:x -in server.pass.key -out server.key + $ openssl req -new -key server.key -out server.csr + $ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt + +On the last step when prompted for a challenge password it can be left +empty. + +Thanks to https://devcenter.heroku.com/articles/ssl-certificate-self +for these instructions. + +Keystore configuration +====================== +If using a local client such as RestClient +(https://github.com/wiztools/rest-client) for testing the Rest server +then a keystore needs to be established. These commands should be +bundled with your Java installation. + +$ keytool -noprompt -import -keystore /path/to/restclient.store -alias +restclient -file /path/to/server.crt + +The keys in the store can be listed as follows: + +$ keytool -list -v -keystore /path/to/restclient.store + +The client can then be configured to use this keystore. Thanks to +http://rubensgomes.blogspot.com/2012/01/how-to-set-up-restclient-for-ssl.html +for instructions on configuring RestClient. + + + diff --git a/3rd_party/libhttpserver-0.18.2/examples/allowing_disallowing_methods.cpp b/3rd_party/libhttpserver-0.18.2/examples/allowing_disallowing_methods.cpp new file mode 100644 index 00000000..69295af4 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/allowing_disallowing_methods.cpp @@ -0,0 +1,42 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + hwr.disallow_all(); + hwr.set_allowing("GET", true); + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/basic_authentication.cpp b/3rd_party/libhttpserver-0.18.2/examples/basic_authentication.cpp new file mode 100644 index 00000000..84de823e --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/basic_authentication.cpp @@ -0,0 +1,46 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class user_pass_resource : public httpserver::http_resource +{ + public: + const std::shared_ptr render_GET(const http_request& req) + { + if (req.get_user() != "myuser" || req.get_pass() != "mypass") + { + return std::shared_ptr(new basic_auth_fail_response("FAIL", "test@example.com")); + } + return std::shared_ptr(new string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + user_pass_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/benchmark_nodelay.cpp b/3rd_party/libhttpserver-0.18.2/examples/benchmark_nodelay.cpp new file mode 100644 index 00000000..418f9f1c --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/benchmark_nodelay.cpp @@ -0,0 +1,42 @@ +#include +#include + +#include + +#define PATH "/plaintext" +#define BODY "Hello, World!" + +using namespace httpserver; + +class hello_world_resource : public http_resource { + public: + hello_world_resource(const std::shared_ptr& resp): + resp(resp) + { + } + + const std::shared_ptr render(const http_request&) { + return resp; + } + + private: + std::shared_ptr resp; +}; + +int main(int argc, char** argv) +{ + webserver ws = create_webserver(atoi(argv[1])) + .start_method(http::http_utils::INTERNAL_SELECT) + .tcp_nodelay() + .max_threads(atoi(argv[2])); + + std::shared_ptr hello = std::shared_ptr(new string_response(BODY, 200)); + hello->with_header("Server", "libhttpserver"); + + hello_world_resource hwr(hello); + ws.register_resource(PATH, &hwr, false); + + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/benchmark_select.cpp b/3rd_party/libhttpserver-0.18.2/examples/benchmark_select.cpp new file mode 100644 index 00000000..62a18140 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/benchmark_select.cpp @@ -0,0 +1,41 @@ +#include +#include + +#include + +#define PATH "/plaintext" +#define BODY "Hello, World!" + +using namespace httpserver; + +class hello_world_resource : public http_resource { + public: + hello_world_resource(const std::shared_ptr& resp): + resp(resp) + { + } + + const std::shared_ptr render(const http_request&) { + return resp; + } + + private: + std::shared_ptr resp; +}; + +int main(int argc, char** argv) +{ + webserver ws = create_webserver(atoi(argv[1])) + .start_method(http::http_utils::INTERNAL_SELECT) + .max_threads(atoi(argv[2])); + + std::shared_ptr hello = std::shared_ptr(new string_response(BODY, 200)); + hello->with_header("Server", "libhttpserver"); + + hello_world_resource hwr(hello); + ws.register_resource(PATH, &hwr, false); + + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/benchmark_threads.cpp b/3rd_party/libhttpserver-0.18.2/examples/benchmark_threads.cpp new file mode 100644 index 00000000..827b1c35 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/benchmark_threads.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include + +#define PATH "/plaintext" +#define BODY "Hello, World!" + +using namespace httpserver; + +class hello_world_resource : public http_resource { + public: + hello_world_resource(const std::shared_ptr& resp): + resp(resp) + { + } + + const std::shared_ptr render(const http_request&) { + return resp; + } + + private: + std::shared_ptr resp; +}; + +int main(int argc, char** argv) +{ + webserver ws = create_webserver(atoi(argv[1])) + .start_method(http::http_utils::THREAD_PER_CONNECTION); + + std::shared_ptr hello = std::shared_ptr(new string_response(BODY, 200)); + hello->with_header("Server", "libhttpserver"); + + hello_world_resource hwr(hello); + ws.register_resource(PATH, &hwr, false); + + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/cert.pem b/3rd_party/libhttpserver-0.18.2/examples/cert.pem new file mode 100644 index 00000000..2c766dff --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICpjCCAZCgAwIBAgIESEPtjjALBgkqhkiG9w0BAQUwADAeFw0wODA2MDIxMjU0 +MzhaFw0wOTA2MDIxMjU0NDZaMAAwggEfMAsGCSqGSIb3DQEBAQOCAQ4AMIIBCQKC +AQC03TyUvK5HmUAirRp067taIEO4bibh5nqolUoUdo/LeblMQV+qnrv/RNAMTx5X +fNLZ45/kbM9geF8qY0vsPyQvP4jumzK0LOJYuIwmHaUm9vbXnYieILiwCuTgjaud +3VkZDoQ9fteIo+6we9UTpVqZpxpbLulBMh/VsvX0cPJ1VFC7rT59o9hAUlFf9jX/ +GmKdYI79MtgVx0OPBjmmSD6kicBBfmfgkO7bIGwlRtsIyMznxbHu6VuoX/eVxrTv +rmCwgEXLWRZ6ru8MQl5YfqeGXXRVwMeXU961KefbuvmEPccgCxm8FZ1C1cnDHFXh +siSgAzMBjC/b6KVhNQ4KnUdZAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0O +BBYEFJcUvpjvE5fF/yzUshkWDpdYiQh/MAsGCSqGSIb3DQEBBQOCAQEARP7eKSB2 +RNd6XjEjK0SrxtoTnxS3nw9sfcS7/qD1+XHdObtDFqGNSjGYFB3Gpx8fpQhCXdoN +8QUs3/5ZVa5yjZMQewWBgz8kNbnbH40F2y81MHITxxCe1Y+qqHWwVaYLsiOTqj2/ +0S3QjEJ9tvklmg7JX09HC4m5QRYfWBeQLD1u8ZjA1Sf1xJriomFVyRLI2VPO2bNe +JDMXWuP+8kMC7gEvUnJ7A92Y2yrhu3QI3bjPk8uSpHea19Q77tul1UVBJ5g+zpH3 +OsF5p0MyaVf09GTzcLds5nE/osTdXGUyHJapWReVmPm3Zn6gqYlnzD99z+DPIgIV +RhZvQx74NQnS6g== +-----END CERTIFICATE----- diff --git a/3rd_party/libhttpserver-0.18.2/examples/custom_access_log.cpp b/3rd_party/libhttpserver-0.18.2/examples/custom_access_log.cpp new file mode 100644 index 00000000..afbc1774 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/custom_access_log.cpp @@ -0,0 +1,47 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +#include + +using namespace httpserver; + +void custom_access_log(const std::string& url) { + std::cout << "ACCESSING: " << url << std::endl; +} + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .log_access(custom_access_log); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/custom_error.cpp b/3rd_party/libhttpserver-0.18.2/examples/custom_error.cpp new file mode 100644 index 00000000..034f334c --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/custom_error.cpp @@ -0,0 +1,54 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +const std::shared_ptr not_found_custom(const http_request& req) +{ + return std::shared_ptr(new string_response("Not found custom", 404, "text/plain")); +} + +const std::shared_ptr not_allowed_custom(const http_request& req) +{ + return std::shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); +} + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .not_found_resource(not_found_custom) + .method_not_allowed_resource(not_allowed_custom); + + hello_world_resource hwr; + hwr.disallow_all(); + hwr.set_allowing("GET", true); + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/deferred_with_accumulator.cpp b/3rd_party/libhttpserver-0.18.2/examples/deferred_with_accumulator.cpp new file mode 100644 index 00000000..06578415 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/deferred_with_accumulator.cpp @@ -0,0 +1,74 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include +#include + +#include + +using namespace httpserver; + +std::atomic counter; + +ssize_t test_callback (std::shared_ptr > closure_data, char* buf, size_t max) { + int reqid; + if (closure_data == nullptr) { + reqid = -1; + } else { + reqid = *closure_data; + } + + // only first 5 connections can be established + if (reqid >= 5) { + return -1; + } else { + // respond corresponding request IDs to the clients + std::string str = ""; + str += std::to_string(reqid) + " "; + memset(buf, 0, max); + std::copy(str.begin(), str.end(), buf); + + // keep sending reqid + // sleep(1); ==> adapted for C++11 on non-*Nix systems + std::this_thread::sleep_for(std::chrono::seconds(1)); + + return (ssize_t)max; + } +} + +class deferred_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + std::shared_ptr > closure_data(new std::atomic(counter++)); + return std::shared_ptr > >(new deferred_response >(test_callback, closure_data, "cycle callback response")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + deferred_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} + diff --git a/3rd_party/libhttpserver-0.18.2/examples/digest_authentication.cpp b/3rd_party/libhttpserver-0.18.2/examples/digest_authentication.cpp new file mode 100644 index 00000000..55f119a0 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/digest_authentication.cpp @@ -0,0 +1,51 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +#define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" + +using namespace httpserver; + +class digest_resource : public httpserver::http_resource { +public: + const std::shared_ptr render_GET(const http_request& req) { + if (req.get_digested_user() == "") { + return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); + } + else { + bool reload_nonce = false; + if(!req.check_digest_auth("test@example.com", "mypass", 300, reload_nonce)) { + return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, reload_nonce)); + } + } + return std::shared_ptr(new string_response("SUCCESS", 200, "text/plain")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + digest_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/handlers.cpp b/3rd_party/libhttpserver-0.18.2/examples/handlers.cpp new file mode 100644 index 00000000..4ddad426 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/handlers.cpp @@ -0,0 +1,45 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render_GET(const http_request&) { + return std::shared_ptr(new string_response("GET: Hello, World!")); + } + + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("OTHER: Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} + diff --git a/3rd_party/libhttpserver-0.18.2/examples/hello_with_get_arg.cpp b/3rd_party/libhttpserver-0.18.2/examples/hello_with_get_arg.cpp new file mode 100644 index 00000000..07ae90c1 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/hello_with_get_arg.cpp @@ -0,0 +1,40 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("Hello: " + req.get_arg("name"))); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/hello_world.cpp b/3rd_party/libhttpserver-0.18.2/examples/hello_world.cpp new file mode 100644 index 00000000..6f8b4aad --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/hello_world.cpp @@ -0,0 +1,67 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&); + void set_some_data(const std::string &s) {data = s;} + std::string data; +}; + +//using the render method you are able to catch each type of request you receive +const std::shared_ptr hello_world_resource::render(const http_request& req) +{ + //it is possible to store data inside the resource object that can be altered + //through the requests + std::cout << "Data was: " << data << std::endl; + std::string datapar = req.get_arg("data"); + set_some_data(datapar == "" ? "no data passed!!!" : datapar); + std::cout << "Now data is:" << data << std::endl; + + //it is possible to send a response initializing an http_string_response + //that reads the content to send in response from a string. + return std::shared_ptr(new string_response("Hello World!!!", 200)); +} + +int main() +{ + //it is possible to create a webserver passing a great number of parameters. + //In this case we are just passing the port and the number of thread running. + webserver ws = create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT).max_threads(5); + + hello_world_resource hwr; + //this way we are registering the hello_world_resource to answer for the endpoint + //"/hello". The requested method is called (if the request is a GET we call the render_GET + //method. In case that the specific render method is not implemented, the generic "render" + //method is called. + ws.register_resource("/hello", &hwr, true); + + //This way we are putting the created webserver in listen. We pass true in order to have + //a blocking call; if we want the call to be non-blocking we can just pass false to the + //method. + ws.start(true); + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/key.pem b/3rd_party/libhttpserver-0.18.2/examples/key.pem new file mode 100644 index 00000000..a5848eed --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAtN08lLyuR5lAIq0adOu7WiBDuG4m4eZ6qJVKFHaPy3m5TEFf +qp67/0TQDE8eV3zS2eOf5GzPYHhfKmNL7D8kLz+I7psytCziWLiMJh2lJvb2152I +niC4sArk4I2rnd1ZGQ6EPX7XiKPusHvVE6VamacaWy7pQTIf1bL19HDydVRQu60+ +faPYQFJRX/Y1/xpinWCO/TLYFcdDjwY5pkg+pInAQX5n4JDu2yBsJUbbCMjM58Wx +7ulbqF/3lca0765gsIBFy1kWeq7vDEJeWH6nhl10VcDHl1PetSnn27r5hD3HIAsZ +vBWdQtXJwxxV4bIkoAMzAYwv2+ilYTUOCp1HWQIDAQABAoIBAArOQv3R7gmqDspj +lDaTFOz0C4e70QfjGMX0sWnakYnDGn6DU19iv3GnX1S072ejtgc9kcJ4e8VUO79R +EmqpdRR7k8dJr3RTUCyjzf/C+qiCzcmhCFYGN3KRHA6MeEnkvRuBogX4i5EG1k5l +/5t+YBTZBnqXKWlzQLKoUAiMLPg0eRWh+6q7H4N7kdWWBmTpako7TEqpIwuEnPGx +u3EPuTR+LN6lF55WBePbCHccUHUQaXuav18NuDkcJmCiMArK9SKb+h0RqLD6oMI/ +dKD6n8cZXeMBkK+C8U/K0sN2hFHACsu30b9XfdnljgP9v+BP8GhnB0nCB6tNBCPo +32srOwECgYEAxWh3iBT4lWqL6bZavVbnhmvtif4nHv2t2/hOs/CAq8iLAw0oWGZc ++JEZTUDMvFRlulr0kcaWra+4fN3OmJnjeuFXZq52lfMgXBIKBmoSaZpIh2aDY1Rd +RbEse7nQl9hTEPmYspiXLGtnAXW7HuWqVfFFP3ya8rUS3t4d07Hig8ECgYEA6ou6 +OHiBRTbtDqLIv8NghARc/AqwNWgEc9PelCPe5bdCOLBEyFjqKiT2MttnSSUc2Zob +XhYkHC6zN1Mlq30N0e3Q61YK9LxMdU1vsluXxNq2rfK1Scb1oOlOOtlbV3zA3VRF +hV3t1nOA9tFmUrwZi0CUMWJE/zbPAyhwWotKyZkCgYEAh0kFicPdbABdrCglXVae +SnfSjVwYkVuGd5Ze0WADvjYsVkYBHTvhgRNnRJMg+/vWz3Sf4Ps4rgUbqK8Vc20b +AU5G6H6tlCvPRGm0ZxrwTWDHTcuKRVs+pJE8C/qWoklE/AAhjluWVoGwUMbPGuiH +6Gf1bgHF6oj/Sq7rv/VLZ8ECgYBeq7ml05YyLuJutuwa4yzQ/MXfghzv4aVyb0F3 +QCdXR6o2IYgR6jnSewrZKlA9aPqFJrwHNR6sNXlnSmt5Fcf/RWO/qgJQGLUv3+rG +7kuLTNDR05azSdiZc7J89ID3Bkb+z2YkV+6JUiPq/Ei1+nDBEXb/m+/HqALU/nyj +P3gXeQKBgBusb8Rbd+KgxSA0hwY6aoRTPRt8LNvXdsB9vRcKKHUFQvxUWiUSS+L9 +/Qu1sJbrUquKOHqksV5wCnWnAKyJNJlhHuBToqQTgKXjuNmVdYSe631saiI7PHyC +eRJ6DxULPxABytJrYCRrNqmXi5TCiqR2mtfalEMOPxz8rUU8dYyx +-----END RSA PRIVATE KEY----- diff --git a/3rd_party/libhttpserver-0.18.2/examples/minimal_deferred.cpp b/3rd_party/libhttpserver-0.18.2/examples/minimal_deferred.cpp new file mode 100644 index 00000000..a7a3e51d --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/minimal_deferred.cpp @@ -0,0 +1,55 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +static int counter = 0; + +ssize_t test_callback (std::shared_ptr closure_data, char* buf, size_t max) { + if (counter == 2) { + return -1; + } + else { + memset(buf, 0, max); + strcat(buf, " test "); + counter++; + return std::string(buf).size(); + } +} + +class deferred_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + return std::shared_ptr >(new deferred_response(test_callback, nullptr, "cycle callback response")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + deferred_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} + diff --git a/3rd_party/libhttpserver-0.18.2/examples/minimal_file_response.cpp b/3rd_party/libhttpserver-0.18.2/examples/minimal_file_response.cpp new file mode 100644 index 00000000..b82d2929 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/minimal_file_response.cpp @@ -0,0 +1,42 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class file_response_resource : public http_resource +{ + public: + const std::shared_ptr render_GET(const http_request& req) + { + return std::shared_ptr(new file_response("test_content", 200, "text/plain")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + file_response_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/minimal_hello_world.cpp b/3rd_party/libhttpserver-0.18.2/examples/minimal_hello_world.cpp new file mode 100644 index 00000000..76489a0e --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/minimal_hello_world.cpp @@ -0,0 +1,40 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/minimal_https.cpp b/3rd_party/libhttpserver-0.18.2/examples/minimal_https.cpp new file mode 100644 index 00000000..def0452a --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/minimal_https.cpp @@ -0,0 +1,43 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/minimal_ip_ban.cpp b/3rd_party/libhttpserver-0.18.2/examples/minimal_ip_ban.cpp new file mode 100644 index 00000000..ea0923e7 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/minimal_ip_ban.cpp @@ -0,0 +1,43 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .default_policy(http::http_utils::REJECT); + + ws.allow_ip("127.0.0.1"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/service.cpp b/3rd_party/libhttpserver-0.18.2/examples/service.cpp new file mode 100644 index 00000000..e20c9a11 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/service.cpp @@ -0,0 +1,242 @@ +/* + This file is part of libhttpserver + Copyright (C) 2014 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +#include +#include + +#include + +using namespace httpserver; + +bool verbose=false; + +class service_resource: public http_resource { +public: + service_resource(); + + ~service_resource(); + + const std::shared_ptr render_GET(const http_request &req); + const std::shared_ptr render_PUT(const http_request &req); + const std::shared_ptr render_POST(const http_request &req); + const std::shared_ptr render(const http_request &req); + const std::shared_ptr render_HEAD(const http_request &req); + const std::shared_ptr render_OPTIONS(const http_request &req); + const std::shared_ptr render_CONNECT(const http_request &req); + const std::shared_ptr render_DELETE(const http_request &req); + +private: + + +}; + +service_resource::service_resource() +{} + +service_resource::~service_resource() +{} + +const std::shared_ptr +service_resource::render_GET(const http_request &req) +{ + std::cout << "service_resource::render_GET()" << std::endl; + + if (verbose) std::cout << req; + string_response* res = new string_response("GET response", 200); + + if (verbose) std::cout << *res; + + return std::shared_ptr(res); +} + + +const std::shared_ptr +service_resource::render_PUT(const http_request &req) +{ + std::cout << "service_resource::render_PUT()" << std::endl; + + if (verbose) std::cout << req; + + string_response* res = new string_response("PUT response", 200); + + if (verbose) std::cout << *res; + + return std::shared_ptr(res); +} + + +const std::shared_ptr +service_resource::render_POST(const http_request &req) +{ + std::cout << "service_resource::render_POST()" << std::endl; + + if (verbose) std::cout << req; + + string_response* res = new string_response("POST response", 200); + + if (verbose) std::cout << *res; + + return std::shared_ptr(res); +} + +const std::shared_ptr +service_resource::render(const http_request &req) +{ + std::cout << "service_resource::render()" << std::endl; + + if (verbose) std::cout << req; + + string_response* res = new string_response("generic response", 200); + + if (verbose) std::cout << *res; + + return std::shared_ptr(res); +} + + +const std::shared_ptr +service_resource::render_HEAD(const http_request &req) +{ + std::cout << "service_resource::render_HEAD()" << std::endl; + + if (verbose) std::cout << req; + + string_response* res = new string_response("HEAD response", 200); + + if (verbose) std::cout << *res; + + return std::shared_ptr(res); +} + +const std::shared_ptr +service_resource::render_OPTIONS(const http_request &req) +{ + std::cout << "service_resource::render_OPTIONS()" << std::endl; + + if (verbose) std::cout << req; + + string_response* res = new string_response("OPTIONS response", 200); + + if (verbose) std::cout << *res; + + return std::shared_ptr(res); +} + +const std::shared_ptr +service_resource::render_CONNECT(const http_request &req) +{ + std::cout << "service_resource::render_CONNECT()" << std::endl; + + if (verbose) std::cout << req; + + string_response* res = new string_response("CONNECT response", 200); + + if (verbose) std::cout << *res; + + return std::shared_ptr(res); +} + +const std::shared_ptr +service_resource::render_DELETE(const http_request &req) +{ + std::cout << "service_resource::render_DELETE()" << std::endl; + + if (verbose) std::cout << req; + + string_response* res = new string_response("DELETE response", 200); + + if (verbose) std::cout << *res; + + return std::shared_ptr(res); +} + +void usage() +{ + std::cout << "Usage:" << std::endl + << "service [-p ][-s [-k ][-c ]][-v]" << std::endl; +} + +int main(int argc, char **argv) +{ + uint16_t port=8080; + int c; + const char *key="key.pem"; + const char *cert="cert.pem"; + bool secure=false; + + while ((c = getopt(argc,argv,"p:k:c:sv?")) != EOF) { + switch (c) { + case 'p': + port=strtoul(optarg,NULL,10); + break; + case 'k': + key = optarg; + break; + case 'c': + cert=optarg; + break; + case 's': + secure=true; + break; + case 'v': + verbose=true; + break; + default: + usage(); + exit(1); + break; + } + } + + std::cout << "Using port " << port << std::endl; + if (secure) { + std::cout << "Key: " << key << " Certificate: " << cert + << std::endl; + } + + // + // Use builder to define webserver configuration options + // + create_webserver cw = create_webserver(port).max_threads(5); + + if (secure) { + cw.use_ssl().https_mem_key(key).https_mem_cert(cert); + } + + // + // Create webserver using the configured options + // + webserver ws = cw; + + // + // Create and register service resource available at /service + // + service_resource res; + ws.register_resource("/service",&res,true); + + // + // Start and block the webserver + // + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/setting_headers.cpp b/3rd_party/libhttpserver-0.18.2/examples/setting_headers.cpp new file mode 100644 index 00000000..0fb73c79 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/setting_headers.cpp @@ -0,0 +1,42 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + std::shared_ptr response = std::shared_ptr(new string_response("Hello, World!")); + response->with_header("MyHeader", "MyValue"); + return response; + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/examples/test_content b/3rd_party/libhttpserver-0.18.2/examples/test_content new file mode 100644 index 00000000..5f643138 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/test_content @@ -0,0 +1 @@ +test content of file diff --git a/3rd_party/libhttpserver-0.18.2/examples/url_registration.cpp b/3rd_party/libhttpserver-0.18.2/examples/url_registration.cpp new file mode 100644 index 00000000..0f8da6ad --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/examples/url_registration.cpp @@ -0,0 +1,63 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +class handling_multiple_resource : public http_resource { +public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("Your URL: " + req.get_path())); + } +}; + +class url_args_resource : public http_resource { +public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2"))); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + + handling_multiple_resource hmr; + ws.register_resource("/family", &hmr, true); + ws.register_resource("/with_regex_[0-9]+", &hmr); + + url_args_resource uar; + ws.register_resource("/url/with/{arg1}/and/{arg2}", &uar); + ws.register_resource("/url/with/parametric/args/{arg1|[0-9]+}/and/{arg2|[A-Z]+}", &uar); + + ws.start(true); + + return 0; +} diff --git a/3rd_party/libhttpserver-0.18.2/m4/ax_have_epoll.m4 b/3rd_party/libhttpserver-0.18.2/m4/ax_have_epoll.m4 new file mode 100644 index 00000000..9d9bc873 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/m4/ax_have_epoll.m4 @@ -0,0 +1,104 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_have_epoll.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_HAVE_EPOLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# AX_HAVE_EPOLL_PWAIT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# This macro determines whether the system supports the epoll I/O event +# interface. A neat usage example would be: +# +# AX_HAVE_EPOLL( +# [AX_CONFIG_FEATURE_ENABLE(epoll)], +# [AX_CONFIG_FEATURE_DISABLE(epoll)]) +# AX_CONFIG_FEATURE( +# [epoll], [This platform supports epoll(7)], +# [HAVE_EPOLL], [This platform supports epoll(7).]) +# +# The epoll interface was added to the Linux kernel in version 2.5.45, and +# the macro verifies that a kernel newer than this is installed. This +# check is somewhat unreliable if doesn't match the +# running kernel, but it is necessary regardless, because glibc comes with +# stubs for the epoll_create(), epoll_wait(), etc. that allow programs to +# compile and link even if the kernel is too old; the problem would then +# be detected only at runtime. +# +# Linux kernel version 2.6.19 adds the epoll_pwait() call in addition to +# epoll_wait(). The availability of that function can be tested with the +# second macro. Generally speaking, it is safe to assume that +# AX_HAVE_EPOLL would succeed if AX_HAVE_EPOLL_PWAIT has, but not the +# other way round. +# +# LICENSE +# +# Copyright (c) 2008 Peter Simons +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 11 + +AC_DEFUN([AX_HAVE_EPOLL], [dnl + ax_have_epoll_cppflags="${CPPFLAGS}" + AC_CHECK_HEADER([linux/version.h], [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) + AC_MSG_CHECKING([for Linux epoll(7) interface]) + AC_CACHE_VAL([ax_cv_have_epoll], [dnl + AC_LINK_IFELSE([dnl + AC_LANG_PROGRAM([dnl +#include +#ifdef HAVE_LINUX_VERSION_H +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45) +# error linux kernel version is too old to have epoll +# endif +#endif +], [dnl +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0);])], + [ax_cv_have_epoll=yes], + [ax_cv_have_epoll=no])]) + CPPFLAGS="${ax_have_epoll_cppflags}" + AS_IF([test "${ax_cv_have_epoll}" = "yes"], + [AC_MSG_RESULT([yes]) +$1],[AC_MSG_RESULT([no]) +$2]) +])dnl + +AC_DEFUN([AX_HAVE_EPOLL_PWAIT], [dnl + ax_have_epoll_cppflags="${CPPFLAGS}" + AC_CHECK_HEADER([linux/version.h], + [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) + AC_MSG_CHECKING([for Linux epoll(7) interface with signals extension]) + AC_CACHE_VAL([ax_cv_have_epoll_pwait], [dnl + AC_LINK_IFELSE([dnl + AC_LANG_PROGRAM([dnl +#ifdef HAVE_LINUX_VERSION_H +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +# error linux kernel version is too old to have epoll_pwait +# endif +#endif +#include +#include +], [dnl +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0); +rc = epoll_pwait(fd, &ev, 1, 0, (sigset_t const *)(0));])], + [ax_cv_have_epoll_pwait=yes], + [ax_cv_have_epoll_pwait=no])]) + CPPFLAGS="${ax_have_epoll_cppflags}" + AS_IF([test "${ax_cv_have_epoll_pwait}" = "yes"], + [AC_MSG_RESULT([yes]) +$1],[AC_MSG_RESULT([no]) +$2]) +])dnl diff --git a/3rd_party/libhttpserver-0.18.2/m4/ax_valgrind_check.m4 b/3rd_party/libhttpserver-0.18.2/m4/ax_valgrind_check.m4 new file mode 100644 index 00000000..70337981 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/m4/ax_valgrind_check.m4 @@ -0,0 +1,239 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_VALGRIND_DFLT(memcheck|helgrind|drd|sgcheck, on|off) +# AX_VALGRIND_CHECK() +# +# DESCRIPTION +# +# AX_VALGRIND_CHECK checks whether Valgrind is present and, if so, allows +# running `make check` under a variety of Valgrind tools to check for +# memory and threading errors. +# +# Defines VALGRIND_CHECK_RULES which should be substituted in your +# Makefile; and $enable_valgrind which can be used in subsequent configure +# output. VALGRIND_ENABLED is defined and substituted, and corresponds to +# the value of the --enable-valgrind option, which defaults to being +# enabled if Valgrind is installed and disabled otherwise. Individual +# Valgrind tools can be disabled via --disable-valgrind-, the +# default is configurable via the AX_VALGRIND_DFLT command or is to use +# all commands not disabled via AX_VALGRIND_DFLT. All AX_VALGRIND_DFLT +# calls must be made before the call to AX_VALGRIND_CHECK. +# +# If unit tests are written using a shell script and automake's +# LOG_COMPILER system, the $(VALGRIND) variable can be used within the +# shell scripts to enable Valgrind, as described here: +# +# https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html +# +# Usage example: +# +# configure.ac: +# +# AX_VALGRIND_DFLT([sgcheck], [off]) +# AX_VALGRIND_CHECK +# +# in each Makefile.am with tests: +# +# @VALGRIND_CHECK_RULES@ +# VALGRIND_SUPPRESSIONS_FILES = my-project.supp +# EXTRA_DIST = my-project.supp +# +# This results in a "check-valgrind" rule being added. Running `make +# check-valgrind` in that directory will recursively run the module's test +# suite (`make check`) once for each of the available Valgrind tools (out +# of memcheck, helgrind and drd) while the sgcheck will be skipped unless +# enabled again on the commandline with --enable-valgrind-sgcheck. The +# results for each check will be output to test-suite-$toolname.log. The +# target will succeed if there are zero errors and fail otherwise. +# +# Alternatively, a "check-valgrind-$TOOL" rule will be added, for $TOOL in +# memcheck, helgrind, drd and sgcheck. These are useful because often only +# some of those tools can be ran cleanly on a codebase. +# +# The macro supports running with and without libtool. +# +# LICENSE +# +# Copyright (c) 2014, 2015, 2016 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 17 + +dnl Configured tools +m4_define([valgrind_tool_list], [[memcheck], [helgrind], [drd], [sgcheck]]) +m4_set_add_all([valgrind_exp_tool_set], [sgcheck]) +m4_foreach([vgtool], [valgrind_tool_list], + [m4_define([en_dflt_valgrind_]vgtool, [on])]) + +AC_DEFUN([AX_VALGRIND_DFLT],[ + m4_define([en_dflt_valgrind_$1], [$2]) +])dnl + +AM_EXTRA_RECURSIVE_TARGETS([check-valgrind]) +m4_foreach([vgtool], [valgrind_tool_list], + [AM_EXTRA_RECURSIVE_TARGETS([check-valgrind-]vgtool)]) + +AC_DEFUN([AX_VALGRIND_CHECK],[ + dnl Check for --enable-valgrind + AC_ARG_ENABLE([valgrind], + [AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])], + [enable_valgrind=$enableval],[enable_valgrind=]) + + AS_IF([test "$enable_valgrind" != "no"],[ + # Check for Valgrind. + AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind]) + AS_IF([test "$VALGRIND" = ""],[ + AS_IF([test "$enable_valgrind" = "yes"],[ + AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind]) + ],[ + enable_valgrind=no + ]) + ],[ + enable_valgrind=yes + ]) + ]) + + AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"]) + AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind]) + + # Check for Valgrind tools we care about. + [valgrind_enabled_tools=] + m4_foreach([vgtool],[valgrind_tool_list],[ + AC_ARG_ENABLE([valgrind-]vgtool, + m4_if(m4_defn([en_dflt_valgrind_]vgtool),[off],dnl +[AS_HELP_STRING([--enable-valgrind-]vgtool, [Whether to use ]vgtool[ during the Valgrind tests])],dnl +[AS_HELP_STRING([--disable-valgrind-]vgtool, [Whether to skip ]vgtool[ during the Valgrind tests])]), + [enable_valgrind_]vgtool[=$enableval], + [enable_valgrind_]vgtool[=]) + AS_IF([test "$enable_valgrind" = "no"],[ + enable_valgrind_]vgtool[=no], + [test "$enable_valgrind_]vgtool[" ]dnl +m4_if(m4_defn([en_dflt_valgrind_]vgtool), [off], [= "yes"], [!= "no"]),[ + AC_CACHE_CHECK([for Valgrind tool ]vgtool, + [ax_cv_valgrind_tool_]vgtool,[ + ax_cv_valgrind_tool_]vgtool[=no + m4_set_contains([valgrind_exp_tool_set],vgtool, + [m4_define([vgtoolx],[exp-]vgtool)], + [m4_define([vgtoolx],vgtool)]) + AS_IF([`$VALGRIND --tool=]vgtoolx[ --help >/dev/null 2>&1`],[ + ax_cv_valgrind_tool_]vgtool[=yes + ]) + ]) + AS_IF([test "$ax_cv_valgrind_tool_]vgtool[" = "no"],[ + AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ + AC_MSG_ERROR([Valgrind does not support ]vgtool[; reconfigure with --disable-valgrind-]vgtool) + ],[ + enable_valgrind_]vgtool[=no + ]) + ],[ + enable_valgrind_]vgtool[=yes + ]) + ]) + AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ + valgrind_enabled_tools="$valgrind_enabled_tools ]m4_bpatsubst(vgtool,[^exp-])[" + ]) + AC_SUBST([ENABLE_VALGRIND_]vgtool,[$enable_valgrind_]vgtool) + ]) + AC_SUBST([valgrind_tools],["]m4_join([ ], valgrind_tool_list)["]) + AC_SUBST([valgrind_enabled_tools],[$valgrind_enabled_tools]) + +[VALGRIND_CHECK_RULES=' +# Valgrind check +# +# Optional: +# - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions +# files to load. (Default: empty) +# - VALGRIND_FLAGS: General flags to pass to all Valgrind tools. +# (Default: --num-callers=30) +# - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of: +# memcheck, helgrind, drd, sgcheck). (Default: various) + +# Optional variables +VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES)) +VALGRIND_FLAGS ?= --num-callers=30 +VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no +VALGRIND_helgrind_FLAGS ?= --history-level=approx +VALGRIND_drd_FLAGS ?= +VALGRIND_sgcheck_FLAGS ?= + +# Internal use +valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools))) + +valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS) +valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS) +valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS) +valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS) + +valgrind_quiet = $(valgrind_quiet_$(V)) +valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY)) +valgrind_quiet_0 = --quiet +valgrind_v_use = $(valgrind_v_use_$(V)) +valgrind_v_use_ = $(valgrind_v_use_$(AM_DEFAULT_VERBOSITY)) +valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%-am,%,$''@):; + +# Support running with and without libtool. +ifneq ($(LIBTOOL),) +valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute +else +valgrind_lt = +endif + +# Use recursive makes in order to ignore errors during check +check-valgrind-am: +ifeq ($(VALGRIND_ENABLED),yes) + $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k \ + $(foreach tool, $(valgrind_enabled_tools), check-valgrind-$(tool)) +else + @echo "Need to reconfigure with --enable-valgrind" +endif + +# Valgrind running +VALGRIND_TESTS_ENVIRONMENT = \ + $(TESTS_ENVIRONMENT) \ + env VALGRIND=$(VALGRIND) \ + G_SLICE=always-malloc,debug-blocks \ + G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly + +VALGRIND_LOG_COMPILER = \ + $(valgrind_lt) \ + $(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS) + +define valgrind_tool_rule +check-valgrind-$(1)-am: +ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes) +ifneq ($$(TESTS),) + $$(valgrind_v_use)$$(MAKE) check-TESTS \ + TESTS_ENVIRONMENT="$$(VALGRIND_TESTS_ENVIRONMENT)" \ + LOG_COMPILER="$$(VALGRIND_LOG_COMPILER)" \ + LOG_FLAGS="$$(valgrind_$(1)_flags)" \ + TEST_SUITE_LOG=test-suite-$(1).log +endif +else ifeq ($$(VALGRIND_ENABLED),yes) + @echo "Need to reconfigure with --enable-valgrind-$(1)" +else + @echo "Need to reconfigure with --enable-valgrind" +endif +endef + +$(foreach tool,$(valgrind_tools),$(eval $(call valgrind_tool_rule,$(tool)))) + +A''M_DISTCHECK_CONFIGURE_FLAGS ?= +A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind + +MOSTLYCLEANFILES ?= +MOSTLYCLEANFILES += $(valgrind_log_files) + +.PHONY: check-valgrind $(add-prefix check-valgrind-,$(valgrind_tools)) +'] + + AC_SUBST([VALGRIND_CHECK_RULES]) + m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])]) +]) diff --git a/3rd_party/libhttpserver-0.18.2/m4/python.m4 b/3rd_party/libhttpserver-0.18.2/m4/python.m4 new file mode 100644 index 00000000..229fd554 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/m4/python.m4 @@ -0,0 +1,168 @@ +## ------------------------ -*- Autoconf -*- +## Python file handling +## From Andrew Dalke +## Updated by James Henstridge +## ------------------------ +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# --------------------------------------------------------------------------- +# Adds support for distributing Python modules and packages. To +# install modules, copy them to $(pythondir), using the python_PYTHON +# automake variable. To install a package with the same name as the +# automake package, install to $(pkgpythondir), or use the +# pkgpython_PYTHON automake variable. +# +# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as +# locations to install python extension modules (shared libraries). +# Another macro is required to find the appropriate flags to compile +# extension modules. +# +# If your package is configured with a different prefix to python, +# users will have to add the install directory to the PYTHONPATH +# environment variable, or create a .pth file (see the python +# documentation for details). +# +# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will +# cause an error if the version of python installed on the system +# doesn't meet the requirement. MINIMUM-VERSION should consist of +# numbers and dots only. +AC_DEFUN([AM_PATH_PYTHON], + [ + dnl Find a Python interpreter. Python versions prior to 1.5 are not + dnl supported because the default installation locations changed from + dnl $prefix/lib/site-python in 1.4 to $prefix/lib/python1.5/site-packages + dnl in 1.5. + m4_define_default([_AM_PYTHON_INTERPRETER_LIST], + [python python2 python2.5 python2.4 python2.3 python2.2 dnl +python2.1 python2.0 python1.6 python1.5]) + + m4_if([$1],[],[ + dnl No version check is needed. + # Find any Python interpreter. + if test -z "$PYTHON"; then + AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :) + fi + am_display_PYTHON=python + ], [ + dnl A version check is needed. + if test -n "$PYTHON"; then + # If the user set $PYTHON, use it and don't search something else. + AC_MSG_CHECKING([whether $PYTHON version >= $1]) + AM_PYTHON_CHECK_VERSION([$PYTHON], [$1], + [AC_MSG_RESULT(yes)], + [AC_MSG_ERROR(too old)]) + am_display_PYTHON=$PYTHON + else + # Otherwise, try each interpreter until we find one that satisfies + # VERSION. + AC_CACHE_CHECK([for a Python interpreter with version >= $1], + [am_cv_pathless_PYTHON],[ + for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do + test "$am_cv_pathless_PYTHON" = none && break + AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break]) + done]) + # Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON. + if test "$am_cv_pathless_PYTHON" = none; then + PYTHON=: + else + AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON]) + fi + am_display_PYTHON=$am_cv_pathless_PYTHON + fi + ]) + + if test "$PYTHON" = :; then + dnl Run any user-specified action, or abort. + m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])]) + else + + dnl Query Python for its version number. Getting [:3] seems to be + dnl the best way to do this; it's what "site.py" does in the standard + dnl library. + + AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version], + [am_cv_python_version=`$PYTHON -c "import sys; print sys.version[[:3]]"`]) + AC_SUBST([PYTHON_VERSION], [$am_cv_python_version]) + + dnl Use the values of $prefix and $exec_prefix for the corresponding + dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made + dnl distinct variables so they can be overridden if need be. However, + dnl general consensus is that you shouldn't need this ability. + + AC_SUBST([PYTHON_PREFIX], ['${prefix}']) + AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}']) + + dnl At times (like when building shared libraries) you may want + dnl to know which OS platform Python thinks this is. + + AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform], + [am_cv_python_platform=`$PYTHON -c "import sys; print sys.platform"`]) + AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform]) + + + dnl Set up 4 directories: + + dnl pythondir -- where to install python scripts. This is the + dnl site-packages directory, not the python standard library + dnl directory like in previous automake betas. This behavior + dnl is more consistent with lispdir.m4 for example. + dnl Query distutils for this directory. distutils does not exist in + dnl Python 1.5, so we fall back to the hardcoded directory if it + dnl doesn't work. + AC_CACHE_CHECK([for $am_display_PYTHON script directory], + [am_cv_python_pythondir], + [am_cv_python_pythondir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(0,0,prefix='$PYTHON_PREFIX')" 2>/dev/null || + echo "$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages"`]) + AC_SUBST([pythondir], [$am_cv_python_pythondir]) + + dnl pkgpythondir -- $PACKAGE directory under pythondir. Was + dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is + dnl more consistent with the rest of automake. + + AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE]) + + dnl pyexecdir -- directory for installing python extension modules + dnl (shared libraries) + dnl Query distutils for this directory. distutils does not exist in + dnl Python 1.5, so we fall back to the hardcoded directory if it + dnl doesn't work. + AC_CACHE_CHECK([for $am_display_PYTHON extension module directory], + [am_cv_python_pyexecdir], + [am_cv_python_pyexecdir=`$PYTHON -c "from distutils import sysconfig; print sysconfig.get_python_lib(1,0,prefix='$PYTHON_EXEC_PREFIX')" 2>/dev/null || + echo "${PYTHON_EXEC_PREFIX}/lib/python${PYTHON_VERSION}/site-packages"`]) + AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir]) + + dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE) + + AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE]) + + dnl Run any user-specified action. + $2 + fi + +]) + + +# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) +# --------------------------------------------------------------------------- +# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION. +# Run ACTION-IF-FALSE otherwise. +# This test uses sys.hexversion instead of the string equivalent (first +# word of sys.version), in order to cope with versions such as 2.2c1. +# hexversion has been introduced in Python 1.5.2; it's probably not +# worth to support older versions (1.5.1 was released on October 31, 1998). +AC_DEFUN([AM_PYTHON_CHECK_VERSION], + [prog="import sys, string +# split strings by '.' and convert to numeric. Append some zeros +# because we need at least 4 digits for the hex conversion. +minver = map(int, string.split('$2', '.')) + [[0, 0, 0]] +minverhex = 0 +for i in xrange(0, 4): minverhex = (minverhex << 8) + minver[[i]] +sys.exit(sys.hexversion < minverhex)" + AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])]) diff --git a/3rd_party/libhttpserver-0.18.2/src/Makefile.am b/3rd_party/libhttpserver-0.18.2/src/Makefile.am new file mode 100644 index 00000000..5e549bbc --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/Makefile.am @@ -0,0 +1,46 @@ +# +# This file is part of libhttpserver +# Copyright (C) 2011-2019 Sebastiano Merlino +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/ +METASOURCES = AUTO +lib_LTLIBRARIES = libhttpserver.la +libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp string_response.cpp basic_auth_fail_response.cpp digest_auth_fail_response.cpp deferred_response.cpp file_response.cpp http_resource.cpp details/http_endpoint.cpp +noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp gettext.h +nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/string_response.hpp httpserver/basic_auth_fail_response.hpp httpserver/digest_auth_fail_response.hpp httpserver/deferred_response.hpp httpserver/file_response.hpp + +AM_CXXFLAGS += -fPIC -Wall + +if COND_GCOV +AM_CFLAGS += -O0 --coverage --no-inline +AM_CXXFLAGS += -O0 --coverage --no-inline +AM_LDFLAGS += -O0 --coverage -lgcov --no-inline +endif + +if !COND_CROSS_COMPILE +libhttpserver_la_LIBADD = -lmicrohttpd +endif + +libhttpserver_la_CFLAGS = $(AM_CFLAGS) +libhttpserver_la_CXXFLAGS = $(AM_CXXFLAGS) +libhttpserver_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined + +install-data-hook: + (mkdir -p $(DESTDIR)$(includedir) && cd $(DESTDIR)$(includedir) && $(LN_S) -f httpserver.hpp httpserverpp) + +uninstall-hook: + (cd $(DESTDIR)$(includedir) && rm -f httpserverpp) diff --git a/3rd_party/libhttpserver-0.18.2/src/basic_auth_fail_response.cpp b/3rd_party/libhttpserver-0.18.2/src/basic_auth_fail_response.cpp new file mode 100644 index 00000000..49d9f5a9 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/basic_auth_fail_response.cpp @@ -0,0 +1,41 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "httpserver/basic_auth_fail_response.hpp" +#include + +struct MHD_Connection; +struct MHD_Response; + +using namespace std; + +namespace httpserver +{ + +int basic_auth_fail_response::enqueue_response(MHD_Connection* connection, MHD_Response* response) +{ + return MHD_queue_basic_auth_fail_response( + connection, + realm.c_str(), + response + ); +} + +} diff --git a/3rd_party/libhttpserver-0.18.2/src/deferred_response.cpp b/3rd_party/libhttpserver-0.18.2/src/deferred_response.cpp new file mode 100644 index 00000000..f3d44527 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/deferred_response.cpp @@ -0,0 +1,41 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "httpserver/deferred_response.hpp" +#include + +struct MHD_Response; + +using namespace std; + +namespace httpserver +{ + +namespace details +{ + +MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t)) +{ + return MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 1024, cb, cls, NULL); +} + +} + +} diff --git a/3rd_party/libhttpserver-0.18.2/src/details/http_endpoint.cpp b/3rd_party/libhttpserver-0.18.2/src/details/http_endpoint.cpp new file mode 100644 index 00000000..584fb507 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/details/http_endpoint.cpp @@ -0,0 +1,194 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "httpserver/details/http_endpoint.hpp" +#include "httpserver/http_utils.hpp" + +using namespace std; + +namespace httpserver +{ + +namespace details +{ + +using namespace http; + +http_endpoint::~http_endpoint() +{ +} + +http_endpoint::http_endpoint +( + const string& url, + bool family, + bool registration, + bool use_regex +): + family_url(family), + reg_compiled(false) +{ + if (use_regex && !registration) + { + throw std::invalid_argument("Cannot use regex if not during registration"); + } + + url_normalized = use_regex ? "^/" : "/"; + vector parts; + +#ifdef CASE_INSENSITIVE + string_utilities::to_lower_copy(url, url_complete); +#else + url_complete = url; +#endif + + if (url_complete[url_complete.size() - 1] == '/') + { + url_complete = url_complete.substr(0, url_complete.size() - 1); + } + + if (url_complete[0] != '/') + { + url_complete = "/" + url_complete; + } + + parts = http_utils::tokenize_url(url); + string buffered; + bool first = true; + + for (unsigned int i = 0; i < parts.size(); i++) + { + if(!registration) + { + url_normalized += (first ? "" : "/") + parts[i]; + first = false; + + url_pieces.push_back(parts[i]); + + continue; + } + + if((parts[i] != "") && (parts[i][0] != '{')) + { + if(first) + { + url_normalized = (parts[i][0] == '^' ? "" : url_normalized) + parts[i]; + first = false; + } + else + { + url_normalized += "/" + parts[i]; + } + url_pieces.push_back(parts[i]); + + continue; + } + + if((parts[i].size() < 3) || (parts[i][0] != '{') || (parts[i][parts[i].size() - 1] != '}')) + throw std::invalid_argument("Bad URL format"); + + std::string::size_type bar = parts[i].find_first_of('|'); + url_pars.push_back(parts[i].substr(1, bar != string::npos ? bar - 1 : parts[i].size() - 2)); + url_normalized += (first ? "" : "/") + (bar != string::npos ? parts[i].substr(bar + 1, parts[i].size() - bar - 2) : "([^\\/]+)"); + + first = false; + + chunk_positions.push_back(i); + + url_pieces.push_back(parts[i]); + } + + if(use_regex) + { + url_normalized += "$"; + try + { + re_url_normalized = std::regex(url_normalized, std::regex::extended | std::regex::icase | std::regex::nosubs); + } + catch (std::regex_error& e) + { + throw std::invalid_argument("Not a valid regex in URL: " + url_normalized); + } + reg_compiled = true; + } +} + +http_endpoint::http_endpoint(const http_endpoint& h): + url_complete(h.url_complete), + url_normalized(h.url_normalized), + url_pars(h.url_pars), + url_pieces(h.url_pieces), + chunk_positions(h.chunk_positions), + re_url_normalized(h.re_url_normalized), + family_url(h.family_url), + reg_compiled(h.reg_compiled) +{ +} + +http_endpoint& http_endpoint::operator =(const http_endpoint& h) +{ + url_complete = h.url_complete; + url_normalized = h.url_normalized; + family_url = h.family_url; + reg_compiled = h.reg_compiled; + re_url_normalized = h.re_url_normalized; + url_pars = h.url_pars; + url_pieces = h.url_pieces; + chunk_positions = h.chunk_positions; + return *this; +} + +bool http_endpoint::operator <(const http_endpoint& b) const +{ + COMPARATOR(url_normalized, b.url_normalized, std::toupper); +} + +bool http_endpoint::match(const http_endpoint& url) const +{ + if (!reg_compiled) throw std::invalid_argument("Cannot run match. Regex suppressed."); + + if(!family_url || url.url_pieces.size() < url_pieces.size()) + { + return regex_match(url.url_complete, re_url_normalized); + } + + string nn = "/"; + bool first = true; + for(unsigned int i = 0; i < url_pieces.size(); i++) + { + nn += (first ? "" : "/") + url.url_pieces[i]; + first = false; + } + return regex_match(nn, re_url_normalized); +} + +}; + +}; diff --git a/3rd_party/libhttpserver-0.18.2/src/digest_auth_fail_response.cpp b/3rd_party/libhttpserver-0.18.2/src/digest_auth_fail_response.cpp new file mode 100644 index 00000000..ca95526d --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/digest_auth_fail_response.cpp @@ -0,0 +1,43 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "httpserver/digest_auth_fail_response.hpp" +#include + +struct MHD_Connection; +struct MHD_Response; + +using namespace std; + +namespace httpserver +{ + +int digest_auth_fail_response::enqueue_response(MHD_Connection* connection, MHD_Response* response) +{ + return MHD_queue_auth_fail_response( + connection, + realm.c_str(), + opaque.c_str(), + response, + reload_nonce ? MHD_YES : MHD_NO + ); +} + +} diff --git a/3rd_party/libhttpserver-0.18.2/src/file_response.cpp b/3rd_party/libhttpserver-0.18.2/src/file_response.cpp new file mode 100644 index 00000000..66d13a1c --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/file_response.cpp @@ -0,0 +1,52 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "httpserver/file_response.hpp" +#include +#include +#include +#include + +struct MHD_Response; + +using namespace std; + +namespace httpserver +{ + +MHD_Response* file_response::get_raw_response() +{ + int fd = open(filename.c_str(), O_RDONLY); + size_t size = lseek(fd, 0, SEEK_END); + if(size) + { + return MHD_create_response_from_fd(size, fd); + } + else + { + return MHD_create_response_from_buffer( + 0, + (void*) "", + MHD_RESPMEM_PERSISTENT + ); + } +} + +} diff --git a/3rd_party/libhttpserver-0.18.2/src/gettext.h b/3rd_party/libhttpserver-0.18.2/src/gettext.h new file mode 100644 index 00000000..4fb025be --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/gettext.h @@ -0,0 +1,111 @@ +/* Convenience header for conditional use of GNU . + Copyright (C) 1995-1998, 2000-2002, 2004-2006, 2009-2012 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef _LIBGETTEXT_H +#define _LIBGETTEXT_H 1 + +/* NLS can be disabled through the configure --disable-nls option. */ +#if ENABLE_NLS + +/* Get declarations of GNU message catalog functions. */ +# include + +/* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by + the gettext() and ngettext() macros. This is an alternative to calling + textdomain(), and is useful for libraries. */ +# ifdef DEFAULT_TEXT_DOMAIN +# undef gettext +# define gettext(Msgid) \ + dgettext (DEFAULT_TEXT_DOMAIN, Msgid) +# undef ngettext +# define ngettext(Msgid1, Msgid2, N) \ + dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N) +# endif + +#else + +/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which + chokes if dcgettext is defined as a macro. So include it now, to make + later inclusions of a NOP. We don't include + as well because people using "gettext.h" will not include , + and also including would fail on SunOS 4, whereas + is OK. */ +#if defined(__sun) +# include +#endif + +/* Many header files from the libstdc++ coming with g++ 3.3 or newer include + , which chokes if dcgettext is defined as a macro. So include + it now, to make later inclusions of a NOP. */ +#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3) +# include +# if (__GLIBC__ >= 2 && !defined __UCLIBC__) || _GLIBCXX_HAVE_LIBINTL_H +# include +# endif +#endif + +/* Disabled NLS. + The casts to 'const char *' serve the purpose of producing warnings + for invalid uses of the value returned from these functions. + On pre-ANSI systems without 'const', the config.h file is supposed to + contain "#define const". */ +# undef gettext +# define gettext(Msgid) ((const char *) (Msgid)) +# undef dgettext +# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) +# undef dcgettext +# define dcgettext(Domainname, Msgid, Category) \ + ((void) (Category), dgettext (Domainname, Msgid)) +# undef ngettext +# define ngettext(Msgid1, Msgid2, N) \ + ((N) == 1 \ + ? ((void) (Msgid2), (const char *) (Msgid1)) \ + : ((void) (Msgid1), (const char *) (Msgid2))) +# undef dngettext +# define dngettext(Domainname, Msgid1, Msgid2, N) \ + ((void) (Domainname), ngettext (Msgid1, Msgid2, N)) +# undef dcngettext +# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ + ((void) (Category), dngettext (Domainname, Msgid1, Msgid2, N)) +# undef textdomain +# define textdomain(Domainname) ((const char *) (Domainname)) +# undef bindtextdomain +# define bindtextdomain(Domainname, Dirname) \ + ((void) (Domainname), (const char *) (Dirname)) +# undef bind_textdomain_codeset +# define bind_textdomain_codeset(Domainname, Codeset) \ + ((void) (Domainname), (const char *) (Codeset)) + +#endif + +/* Prefer gnulib's setlocale override over libintl's setlocale override. */ +#ifdef GNULIB_defined_setlocale +# undef setlocale +# define setlocale rpl_setlocale +#endif + +/* A pseudo function call that serves as a marker for the automated + extraction of messages, but does not call gettext(). The run-time + translation is done at a different place in the code. + The argument, String, should be a literal string. Concatenated strings + and other string expressions won't work. + The macro's expansion is not parenthesized, so that it is suitable as + initializer for static 'char[]' or 'const char[]' variables. */ +#define gettext_noop(String) String + +#endif /* _LIBGETTEXT_H */ diff --git a/3rd_party/libhttpserver-0.18.2/src/http_request.cpp b/3rd_party/libhttpserver-0.18.2/src/http_request.cpp new file mode 100644 index 00000000..dd41e680 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/http_request.cpp @@ -0,0 +1,316 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +*/ + +#include "httpserver/http_request.hpp" +#include +#include +#include +#include "httpserver/http_utils.hpp" +#include "httpserver/string_utilities.hpp" + +using namespace std; + +namespace httpserver +{ + +const std::string http_request::EMPTY = ""; + +struct arguments_accumulator +{ + unescaper_ptr unescaper; + std::map* arguments; +}; + +void http_request::set_method(const std::string& method) +{ + this->method = string_utilities::to_upper_copy(method); +} + +bool http_request::check_digest_auth( + const std::string& realm, + const std::string& password, + int nonce_timeout, + bool& reload_nonce +) const +{ + std::string digested_user = get_digested_user(); + + int val = MHD_digest_auth_check( + underlying_connection, + realm.c_str(), + digested_user.c_str(), + password.c_str(), + nonce_timeout + ); + + if(val == MHD_INVALID_NONCE) + { + reload_nonce = true; + return false; + } + else if(val == MHD_NO) + { + reload_nonce = false; + return false; + } + reload_nonce = false; + return true; +} + +const std::string http_request::get_connection_value(const std::string& key, enum MHD_ValueKind kind) const +{ + const char* header_c = MHD_lookup_connection_value( + underlying_connection, + kind, + key.c_str() + ); + + if (header_c == NULL) return EMPTY; + + return header_c; +} + +MHD_Result http_request::build_request_header( + void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value +) +{ + std::map* dhr = static_cast*>(cls); + (*dhr)[key] = value; + return MHD_YES; +} + +const std::map http_request::get_headerlike_values(enum MHD_ValueKind kind) const +{ + std::map headers; + + MHD_get_connection_values( + underlying_connection, + kind, + &build_request_header, + (void*) &headers + ); + + return headers; +} + +const std::string http_request::get_header(const std::string& key) const +{ + return get_connection_value(key, MHD_HEADER_KIND); +} + +const std::map http_request::get_headers() const +{ + return get_headerlike_values(MHD_HEADER_KIND); +} + +const std::string http_request::get_footer(const std::string& key) const +{ + return get_connection_value(key, MHD_FOOTER_KIND); +} + +const std::map http_request::get_footers() const +{ + return get_headerlike_values(MHD_FOOTER_KIND); +} + +const std::string http_request::get_cookie(const std::string& key) const +{ + return get_connection_value(key, MHD_COOKIE_KIND); +} + +const std::map http_request::get_cookies() const +{ + return get_headerlike_values(MHD_COOKIE_KIND); +} + +const std::string http_request::get_arg(const std::string& key) const +{ + std::map::const_iterator it = args.find(key); + + if(it != args.end()) + { + return it->second; + } + + return get_connection_value(key, MHD_GET_ARGUMENT_KIND); +} + +const std::map http_request::get_args() const +{ + std::map arguments; + arguments.insert(args.begin(), args.end()); + + arguments_accumulator aa; + aa.unescaper = unescaper; + aa.arguments = &arguments; + + MHD_get_connection_values( + underlying_connection, + MHD_GET_ARGUMENT_KIND, + &build_request_args, + (void*) &aa + ); + + return arguments; +} + +const std::string http_request::get_querystring() const +{ + std::string querystring = ""; + + MHD_get_connection_values( + underlying_connection, + MHD_GET_ARGUMENT_KIND, + &build_request_querystring, + (void*) &querystring + ); + + return querystring; +} + +MHD_Result http_request::build_request_args( + void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *arg_value +) +{ + arguments_accumulator* aa = static_cast(cls); + std::string value = ((arg_value == NULL) ? "" : arg_value); + + http::base_unescaper(value, aa->unescaper); + (*aa->arguments)[key] = value; + return MHD_YES; +} + +MHD_Result http_request::build_request_querystring( + void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *arg_value +) +{ + std::string* querystring = static_cast(cls); + std::string value = ((arg_value == NULL) ? "" : arg_value); + { + std::vector buf(std::string(key).size() + value.size() + 3); + if(*querystring == "") + { + snprintf(&buf[0], buf.size(), "?%s=%s", key, value.c_str()); + *querystring = &buf[0]; + } + else + { + snprintf(&buf[0], buf.size(), "&%s=%s", key, value.c_str()); + *querystring += string(&buf[0]); + } + } + + return MHD_YES; +} + +const std::string http_request::get_user() const +{ + char* username = 0x0; + char* password = 0x0; + + username = MHD_basic_auth_get_username_password(underlying_connection, &password); + if (password != 0x0) free(password); + + std::string user; + if (username != 0x0) user = username; + + free(username); + + return user; +} + +const std::string http_request::get_pass() const +{ + char* username = 0x0; + char* password = 0x0; + + username = MHD_basic_auth_get_username_password(underlying_connection, &password); + if (username != 0x0) free(username); + + std::string pass; + if (password != 0x0) pass = password; + + free(password); + + return pass; +} + +const std::string http_request::get_digested_user() const +{ + char* digested_user_c = 0x0; + digested_user_c = MHD_digest_auth_get_username(underlying_connection); + + std::string digested_user = EMPTY; + if (digested_user_c != 0x0) + { + digested_user = digested_user_c; + free(digested_user_c); + } + + return digested_user; +} + +const std::string http_request::get_requestor() const +{ + const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( + underlying_connection, + MHD_CONNECTION_INFO_CLIENT_ADDRESS + ); + + return http::get_ip_str(conninfo->client_addr); +} + +unsigned short http_request::get_requestor_port() const +{ + const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( + underlying_connection, + MHD_CONNECTION_INFO_CLIENT_ADDRESS + ); + + return http::get_port(conninfo->client_addr); +} + +std::ostream &operator<< (std::ostream &os, const http_request &r) +{ + os << r.get_method() << " Request [user:\"" << r.get_user() << "\" pass:\"" << r.get_pass() << "\"] path:\"" + << r.get_path() << "\"" << std::endl; + + http::dump_header_map(os,"Headers",r.get_headers()); + http::dump_header_map(os,"Footers",r.get_footers()); + http::dump_header_map(os,"Cookies",r.get_cookies()); + http::dump_arg_map(os,"Query Args",r.get_args()); + + os << " Version [ " << r.get_version() << " ] Requestor [ " << r.get_requestor() + << " ] Port [ " << r.get_requestor_port() << " ]" << std::endl; + + return os; +} + +} diff --git a/3rd_party/libhttpserver-0.18.2/src/http_resource.cpp b/3rd_party/libhttpserver-0.18.2/src/http_resource.cpp new file mode 100644 index 00000000..46bdb0de --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/http_resource.cpp @@ -0,0 +1,55 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "httpserver/http_resource.hpp" +#include +#include "httpserver/string_response.hpp" + +namespace httpserver { class http_response; } + +using namespace std; + +namespace httpserver +{ +//RESOURCE +void resource_init(map& allowed_methods) +{ + allowed_methods[MHD_HTTP_METHOD_GET] = true; + allowed_methods[MHD_HTTP_METHOD_POST] = true; + allowed_methods[MHD_HTTP_METHOD_PUT] = true; + allowed_methods[MHD_HTTP_METHOD_HEAD] = true; + allowed_methods[MHD_HTTP_METHOD_DELETE] = true; + allowed_methods[MHD_HTTP_METHOD_TRACE] = true; + allowed_methods[MHD_HTTP_METHOD_CONNECT] = true; + allowed_methods[MHD_HTTP_METHOD_OPTIONS] = true; + allowed_methods[MHD_HTTP_METHOD_PATCH] = true; +} + +namespace details +{ + +shared_ptr empty_render(const http_request& r) +{ + return shared_ptr(new string_response()); +} + +}; + +}; diff --git a/3rd_party/libhttpserver-0.18.2/src/http_response.cpp b/3rd_party/libhttpserver-0.18.2/src/http_response.cpp new file mode 100644 index 00000000..2d261e3d --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/http_response.cpp @@ -0,0 +1,83 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "httpserver/http_response.hpp" +#include +#include +#include +#include "httpserver/http_utils.hpp" + +using namespace std; + +namespace httpserver +{ + +MHD_Response* http_response::get_raw_response() +{ + return MHD_create_response_from_buffer(0, (void*) "", MHD_RESPMEM_PERSISTENT); +} + +void http_response::decorate_response(MHD_Response* response) +{ + map::iterator it; + + for (it=headers.begin() ; it != headers.end(); ++it) + MHD_add_response_header( + response, + (*it).first.c_str(), + (*it).second.c_str() + ); + + for (it=footers.begin() ; it != footers.end(); ++it) + MHD_add_response_footer(response, + (*it).first.c_str(), + (*it).second.c_str() + ); + + for (it=cookies.begin(); it != cookies.end(); ++it) + MHD_add_response_header( + response, + "Set-Cookie", + ((*it).first + "=" + (*it).second).c_str() + ); +} + +int http_response::enqueue_response(MHD_Connection* connection, MHD_Response* response) +{ + return MHD_queue_response(connection, response_code, response); +} + +void http_response::shoutCAST() +{ + response_code |= http::http_utils::shoutcast_response; +} + +std::ostream &operator<< (std::ostream &os, const http_response &r) +{ + os << "Response [response_code:" << r.response_code << "]" << std::endl; + + http::dump_header_map(os,"Headers",r.headers); + http::dump_header_map(os,"Footers",r.footers); + http::dump_header_map(os,"Cookies",r.cookies); + + return os; +} + +} diff --git a/3rd_party/libhttpserver-0.18.2/src/http_utils.cpp b/3rd_party/libhttpserver-0.18.2/src/http_utils.cpp new file mode 100644 index 00000000..caefabc5 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/http_utils.cpp @@ -0,0 +1,627 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "httpserver/http_utils.hpp" + +#if defined(_WIN32) && ! defined(__CYGWIN__) +#include +#include +#else // WIN32 check +#include +#include +#include +#include +#include +#endif // WIN32 check + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "httpserver/string_utilities.hpp" + +#pragma GCC diagnostic ignored "-Warray-bounds" +#define CHECK_BIT(var,pos) ((var) & (1<<(pos))) +#define SET_BIT(var,pos) ((var) |= 1 << (pos)) +#define CLEAR_BIT(var,pos) ((var) &= ~(1<<(pos))) + +#if defined (__CYGWIN__) + +#if ! defined (NI_MAXHOST) +#define NI_MAXHOST 1025 +#endif // NI_MAXHOST + +#ifndef __u_char_defined +typedef unsigned char u_char; +#define __u_char_defined +#endif // __u_char_defined + +#endif // CYGWIN + +using namespace std; + +namespace httpserver { +namespace http { + +/* See also: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ + +const int http_utils::http_continue = MHD_HTTP_CONTINUE; +const int http_utils::http_switching_protocol = MHD_HTTP_SWITCHING_PROTOCOLS; +const int http_utils::http_processing = MHD_HTTP_PROCESSING; + +const int http_utils::http_ok = MHD_HTTP_OK; +const int http_utils::http_created = MHD_HTTP_CREATED; +const int http_utils::http_accepted = MHD_HTTP_ACCEPTED; +const int http_utils::http_non_authoritative_information = + MHD_HTTP_NON_AUTHORITATIVE_INFORMATION; +const int http_utils::http_no_content = MHD_HTTP_NO_CONTENT; +const int http_utils::http_reset_content = MHD_HTTP_RESET_CONTENT; +const int http_utils::http_partial_content = MHD_HTTP_PARTIAL_CONTENT; +const int http_utils::http_multi_status = MHD_HTTP_MULTI_STATUS; + +const int http_utils::http_multiple_choices = MHD_HTTP_MULTIPLE_CHOICES; +const int http_utils::http_moved_permanently = MHD_HTTP_MOVED_PERMANENTLY; +const int http_utils::http_found = MHD_HTTP_FOUND; +const int http_utils::http_see_other = MHD_HTTP_SEE_OTHER; +const int http_utils::http_not_modified = MHD_HTTP_NOT_MODIFIED; +const int http_utils::http_use_proxy = MHD_HTTP_USE_PROXY; +const int http_utils::http_switch_proxy = MHD_HTTP_SWITCH_PROXY; +const int http_utils::http_temporary_redirect = MHD_HTTP_TEMPORARY_REDIRECT; + +const int http_utils::http_bad_request = MHD_HTTP_BAD_REQUEST; +const int http_utils::http_unauthorized = MHD_HTTP_UNAUTHORIZED; +const int http_utils::http_payment_required = MHD_HTTP_PAYMENT_REQUIRED; +const int http_utils::http_forbidden = MHD_HTTP_FORBIDDEN; +const int http_utils::http_not_found = MHD_HTTP_NOT_FOUND; +const int http_utils::http_method_not_allowed = MHD_HTTP_METHOD_NOT_ALLOWED; +const int http_utils::http_method_not_acceptable = MHD_HTTP_NOT_ACCEPTABLE; +const int http_utils::http_proxy_authentication_required = + MHD_HTTP_PROXY_AUTHENTICATION_REQUIRED; +const int http_utils::http_request_timeout = MHD_HTTP_REQUEST_TIMEOUT; +const int http_utils::http_conflict = MHD_HTTP_CONFLICT; +const int http_utils::http_gone = MHD_HTTP_GONE; +const int http_utils::http_length_required = MHD_HTTP_LENGTH_REQUIRED; +const int http_utils::http_precondition_failed = MHD_HTTP_PRECONDITION_FAILED; +const int http_utils::http_request_entity_too_large = + MHD_HTTP_PAYLOAD_TOO_LARGE; +const int http_utils::http_request_uri_too_long = MHD_HTTP_URI_TOO_LONG; +const int http_utils::http_unsupported_media_type = + MHD_HTTP_UNSUPPORTED_MEDIA_TYPE; +const int http_utils::http_requested_range_not_satisfiable = + MHD_HTTP_RANGE_NOT_SATISFIABLE; +const int http_utils::http_expectation_failed = MHD_HTTP_EXPECTATION_FAILED; +const int http_utils::http_unprocessable_entity = MHD_HTTP_UNPROCESSABLE_ENTITY; +const int http_utils::http_locked = MHD_HTTP_LOCKED; +const int http_utils::http_failed_dependency = MHD_HTTP_FAILED_DEPENDENCY; +const int http_utils::http_unordered_collection = MHD_HTTP_UNORDERED_COLLECTION; +const int http_utils::http_upgrade_required = MHD_HTTP_UPGRADE_REQUIRED; +const int http_utils::http_retry_with = MHD_HTTP_RETRY_WITH; + +const int http_utils::http_internal_server_error = + MHD_HTTP_INTERNAL_SERVER_ERROR; +const int http_utils::http_not_implemented = MHD_HTTP_NOT_IMPLEMENTED; +const int http_utils::http_bad_gateway = MHD_HTTP_BAD_GATEWAY; +const int http_utils::http_service_unavailable = MHD_HTTP_SERVICE_UNAVAILABLE; +const int http_utils::http_gateway_timeout = MHD_HTTP_GATEWAY_TIMEOUT; +const int http_utils::http_version_not_supported = + MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED; +const int http_utils::http_variant_also_negotiated = + MHD_HTTP_VARIANT_ALSO_NEGOTIATES; +const int http_utils::http_insufficient_storage = MHD_HTTP_INSUFFICIENT_STORAGE; +const int http_utils::http_bandwidth_limit_exceeded = + MHD_HTTP_BANDWIDTH_LIMIT_EXCEEDED; +const int http_utils::http_not_extended = MHD_HTTP_NOT_EXTENDED; + +const int http_utils::shoutcast_response = MHD_ICY_FLAG; + +const std::string http_utils::http_header_accept = MHD_HTTP_HEADER_ACCEPT; +const std::string http_utils::http_header_accept_charset = + MHD_HTTP_HEADER_ACCEPT_CHARSET; +const std::string http_utils::http_header_accept_encoding = + MHD_HTTP_HEADER_ACCEPT_ENCODING; +const std::string http_utils::http_header_accept_language = + MHD_HTTP_HEADER_ACCEPT_LANGUAGE; +const std::string http_utils::http_header_accept_ranges = + MHD_HTTP_HEADER_ACCEPT_RANGES; +const std::string http_utils::http_header_age = MHD_HTTP_HEADER_AGE; +const std::string http_utils::http_header_allow = MHD_HTTP_HEADER_ALLOW; +const std::string http_utils::http_header_authorization = + MHD_HTTP_HEADER_AUTHORIZATION; +const std::string http_utils::http_header_cache_control = + MHD_HTTP_HEADER_CACHE_CONTROL; +const std::string http_utils::http_header_connection = + MHD_HTTP_HEADER_CONNECTION; +const std::string http_utils::http_header_content_encoding = + MHD_HTTP_HEADER_CONTENT_ENCODING; +const std::string http_utils::http_header_content_language = + MHD_HTTP_HEADER_CONTENT_LANGUAGE; +const std::string http_utils::http_header_content_length = + MHD_HTTP_HEADER_CONTENT_LENGTH; +const std::string http_utils::http_header_content_location = + MHD_HTTP_HEADER_CONTENT_LOCATION; +const std::string http_utils::http_header_content_md5 = + MHD_HTTP_HEADER_CONTENT_MD5; +const std::string http_utils::http_header_content_range = + MHD_HTTP_HEADER_CONTENT_RANGE; +const std::string http_utils::http_header_content_type = + MHD_HTTP_HEADER_CONTENT_TYPE; +const std::string http_utils::http_header_date = MHD_HTTP_HEADER_DATE; +const std::string http_utils::http_header_etag = MHD_HTTP_HEADER_ETAG; +const std::string http_utils::http_header_expect = MHD_HTTP_HEADER_EXPECT; +const std::string http_utils::http_header_expires = MHD_HTTP_HEADER_EXPIRES; +const std::string http_utils::http_header_from = MHD_HTTP_HEADER_FROM; +const std::string http_utils::http_header_host = MHD_HTTP_HEADER_HOST; +const std::string http_utils::http_header_if_match = MHD_HTTP_HEADER_IF_MATCH; +const std::string http_utils::http_header_if_modified_since = + MHD_HTTP_HEADER_IF_MODIFIED_SINCE; +const std::string http_utils::http_header_if_none_match = + MHD_HTTP_HEADER_IF_NONE_MATCH; +const std::string http_utils::http_header_if_range = MHD_HTTP_HEADER_IF_RANGE; +const std::string http_utils::http_header_if_unmodified_since = + MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE; +const std::string http_utils::http_header_last_modified = + MHD_HTTP_HEADER_LAST_MODIFIED; +const std::string http_utils::http_header_location = MHD_HTTP_HEADER_LOCATION; +const std::string http_utils::http_header_max_forwards = + MHD_HTTP_HEADER_MAX_FORWARDS; +const std::string http_utils::http_header_pragma = MHD_HTTP_HEADER_PRAGMA; +const std::string http_utils::http_header_proxy_authenticate = + MHD_HTTP_HEADER_PROXY_AUTHENTICATE; +const std::string http_utils::http_header_proxy_authentication = + MHD_HTTP_HEADER_PROXY_AUTHORIZATION; +const std::string http_utils::http_header_range = MHD_HTTP_HEADER_RANGE; +const std::string http_utils::http_header_referer = MHD_HTTP_HEADER_REFERER; +const std::string http_utils::http_header_retry_after = + MHD_HTTP_HEADER_RETRY_AFTER; +const std::string http_utils::http_header_server = MHD_HTTP_HEADER_SERVER; +const std::string http_utils::http_header_te = MHD_HTTP_HEADER_TE; +const std::string http_utils::http_header_trailer = MHD_HTTP_HEADER_TRAILER; +const std::string http_utils::http_header_transfer_encoding = + MHD_HTTP_HEADER_TRANSFER_ENCODING; +const std::string http_utils::http_header_upgrade = MHD_HTTP_HEADER_UPGRADE; +const std::string http_utils::http_header_user_agent = + MHD_HTTP_HEADER_USER_AGENT; +const std::string http_utils::http_header_vary = MHD_HTTP_HEADER_VARY; +const std::string http_utils::http_header_via = MHD_HTTP_HEADER_VIA; +const std::string http_utils::http_header_warning = MHD_HTTP_HEADER_WARNING; +const std::string http_utils::http_header_www_authenticate = + MHD_HTTP_HEADER_WWW_AUTHENTICATE; + +const std::string http_utils::http_version_1_0 = MHD_HTTP_VERSION_1_0; +const std::string http_utils::http_version_1_1 = MHD_HTTP_VERSION_1_1; + +const std::string http_utils::http_method_connect = MHD_HTTP_METHOD_CONNECT; +const std::string http_utils::http_method_delete = MHD_HTTP_METHOD_DELETE; +const std::string http_utils::http_method_get = MHD_HTTP_METHOD_GET; +const std::string http_utils::http_method_head = MHD_HTTP_METHOD_HEAD; +const std::string http_utils::http_method_options = MHD_HTTP_METHOD_OPTIONS; +const std::string http_utils::http_method_post = MHD_HTTP_METHOD_POST; +const std::string http_utils::http_method_put = MHD_HTTP_METHOD_PUT; +const std::string http_utils::http_method_trace = MHD_HTTP_METHOD_TRACE; +const std::string http_utils::http_method_patch = MHD_HTTP_METHOD_PATCH; + +const std::string http_utils::http_post_encoding_form_urlencoded = + MHD_HTTP_POST_ENCODING_FORM_URLENCODED; +const std::string http_utils::http_post_encoding_multipart_formdata = + MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA; + +const std::string http_utils::text_plain = "text/plain"; + +std::vector http_utils::tokenize_url( + const std::string& str, + const char separator +) +{ + return string_utilities::string_split(str, separator); +} + +std::string http_utils::standardize_url(const std::string& url) +{ + std::string n_url = url; + + std::string::iterator new_end = std::unique(n_url.begin(), n_url.end(), [](char a, char b) { return (a == b) && (a == '/'); }); + n_url.erase(new_end, n_url.end()); + + std::string::size_type n_url_length = n_url.length(); + + std::string result; + + if (n_url_length > 1 && n_url[n_url_length - 1] == '/') + { + result = n_url.substr(0, n_url_length - 1); + } + else + { + result = n_url; + } + + return result; +} + +std::string get_ip_str(const struct sockaddr *sa, socklen_t maxlen) +{ + if (!sa) throw std::invalid_argument("socket pointer is null"); + + char to_ret[NI_MAXHOST]; + if (AF_INET6 == sa->sa_family) + { + inet_ntop(AF_INET6, &(((sockaddr_in6*) sa)->sin6_addr), to_ret, INET6_ADDRSTRLEN); + return to_ret; + } + else if (AF_INET == sa->sa_family) + { + inet_ntop(AF_INET, &(((sockaddr_in*) sa)->sin_addr), to_ret, INET_ADDRSTRLEN); + return to_ret; + } + else + { + throw std::invalid_argument("IP family must be either AF_INET or AF_INET6"); + } +} + +unsigned short get_port(const struct sockaddr* sa) +{ + if (!sa) throw std::invalid_argument("socket pointer is null"); + + if (sa->sa_family == AF_INET) + { + return ((struct sockaddr_in*) sa)->sin_port; + } + else if (sa->sa_family == AF_INET6) + { + return ((struct sockaddr_in6*) sa)->sin6_port; + } + else + { + throw std::invalid_argument("IP family must be either AF_INET or AF_INET6"); + } +} + +size_t http_unescape(std::string& val) +{ + if (val.empty()) return 0; + + int rpos = 0; + int wpos = 0; + + unsigned int num; + + while ('\0' != val[rpos]) + { + switch (val[rpos]) + { + case '+': + val[wpos] = ' '; + wpos++; + rpos++; + break; + case '%': + if ( (1 == sscanf (val.substr(rpos + 1).c_str(), "%2x", &num)) || + (1 == sscanf (val.substr(rpos + 1).c_str(), "%2X", &num)) + ) + { + val[wpos] = (unsigned char) num; + wpos++; + rpos += 3; + break; + } + /* intentional fall through! */ + default: + val[wpos] = val[rpos]; + wpos++; + rpos++; + } + } + val[wpos] = '\0'; /* add 0-terminator */ + val.resize(wpos); + return wpos; /* = strlen(val) */ +} + +ip_representation::ip_representation(const struct sockaddr* ip) +{ + std::fill(pieces, pieces + 16, 0); + if(ip->sa_family == AF_INET) + { + ip_version = http_utils::IPV4; + for(int i=0;i<4;i++) + { + pieces[12+i]=((u_char*)&(((struct sockaddr_in *)ip)->sin_addr))[i]; + } + } + else + { + ip_version = http_utils::IPV6; + for (int i = 0; i < 16; i++) + { + pieces[i] = ((u_char*)&(((struct sockaddr_in6 *)ip)->sin6_addr))[i]; + } + } + mask = DEFAULT_MASK_VALUE; +} + +ip_representation::ip_representation(const std::string& ip) +{ + std::vector parts; + mask = DEFAULT_MASK_VALUE; + std::fill(pieces, pieces + 16, 0); + if(ip.find(':') != std::string::npos) //IPV6 + { + ip_version = http_utils::IPV6; + parts = string_utilities::string_split(ip, ':', false); + if (parts.size() > 8) + { + throw std::invalid_argument("IP is badly formatted. Max 8 parts in IPV6."); + } + + unsigned int omitted = 8 - (parts.size() - 1); + if (omitted != 0) + { + int empty_count = 0; + for (unsigned int i = 0; i < parts.size(); i++) + { + if (parts[i].size() == 0) empty_count++; + } + + if (empty_count > 1) + { + if (parts[parts.size() - 1].find(".") != std::string::npos) omitted -= 1; + + if (empty_count == 2 && parts[0] == "" && parts[1] == "") + { + omitted += 1; + parts = std::vector(parts.begin() + 1, parts.end()); + } + else + { + throw std::invalid_argument("IP is badly formatted. Cannot have more than one omitted segment in IPV6."); + } + } + } + + int y = 0; + for (unsigned int i = 0; i < parts.size(); i++) + { + if (parts[i] != "*") + { + if (parts[i].size() == 0) + { + for (unsigned int omitted_idx = 0; omitted_idx < omitted; omitted_idx++) + { + pieces[y] = 0; + pieces[y+1] = 0; + y += 2; + } + + continue; + } + + if (parts[i].size() < 4) + { + stringstream ss; + ss << setfill('0') << setw(4) << parts[i]; + parts[i] = ss.str(); + } + + if (parts[i].size() == 4) + { + pieces[y] = strtol((parts[i].substr(0,2)).c_str(),NULL,16); + pieces[y+1] = strtol( + (parts[i].substr(2,2)).c_str(), + NULL, + 16 + ); + + y += 2; + } + else + { + if(parts[i].find('.') != std::string::npos) + { + if(y != 12) + { + throw std::invalid_argument("IP is badly formatted. Missing parts before nested IPV4."); + } + + if (i != parts.size() - 1) + { + throw std::invalid_argument("IP is badly formatted. Nested IPV4 should be at the end"); + } + + vector subparts = string_utilities::string_split(parts[i], '.'); + if(subparts.size() == 4) + { + for (unsigned int k = 0; k < 10; k++) + { + if (pieces[k] != 0) throw std::invalid_argument("IP is badly formatted. Nested IPV4 can be preceded only by 0 (and, optionally, two 255 octects)"); + } + + if ((pieces[10] != 0 && pieces[10] != 255) || (pieces[11] != 0 && pieces[11] != 255)) + { + throw std::invalid_argument("IP is badly formatted. Nested IPV4 can be preceded only by 0 (and, optionally, two 255 octects)"); + } + + for(unsigned int ii = 0; ii < subparts.size(); ii++) + { + if(subparts[ii] != "*") + { + pieces[y+ii] = strtol( + subparts[ii].c_str(), + NULL, + 10 + ); + if (pieces[y+ii] > 255) throw std::invalid_argument("IP is badly formatted. 255 is max value for ip part."); + } + else + { + CLEAR_BIT(mask, y+ii); + } + } + } + else + { + throw std::invalid_argument("IP is badly formatted. Nested IPV4 can have max 4 parts."); + } + } + else + { + throw std::invalid_argument("IP is badly formatted. IPV6 parts can have max 4 characters (or nest an IPV4)"); + } + } + } + else + { + CLEAR_BIT(mask, y); + CLEAR_BIT(mask, y+1); + y+=2; + } + } + } + else //IPV4 + { + ip_version = http_utils::IPV4; + parts = string_utilities::string_split(ip, '.'); + if(parts.size() == 4) + { + for(unsigned int i = 0; i < parts.size(); i++) + { + if(parts[i] != "*") + { + pieces[12+i] = strtol(parts[i].c_str(), NULL, 10); + if (pieces[12+i] > 255) throw std::invalid_argument("IP is badly formatted. 255 is max value for ip part."); + } + else + { + CLEAR_BIT(mask, 12+i); + } + } + } + else + { + throw std::invalid_argument("IP is badly formatted. Max 4 parts in IPV4."); + } + } +} + +bool ip_representation::operator <(const ip_representation& b) const +{ + long this_score = 0; + long b_score = 0; + for (int i = 0; i < 16; i++) + { + if (i == 10 || i == 11) continue; + + if (CHECK_BIT(mask, i) && CHECK_BIT(b.mask, i)) + { + this_score += (16 - i) * pieces[i]; + b_score += (16 - i) * b.pieces[i]; + } + } + + if (this_score == b_score && + ((pieces[10] == 0x00 || pieces[10] == 0xFF) && (b.pieces[10] == 0x00 || b.pieces[10] == 0xFF)) && + ((pieces[11] == 0x00 || pieces[11] == 0xFF) && (b.pieces[11] == 0x00 || b.pieces[11] == 0xFF)) + ) + { + return false; + } + + for (int i = 10; i < 12; i++) + { + if (CHECK_BIT(mask, i) && CHECK_BIT(b.mask, i)) + { + this_score += (16 - i) * pieces[i]; + b_score += (16 - i) * b.pieces[i]; + } + } + + return this_score < b_score; +} + +const std::string load_file (const std::string& filename) +{ + ifstream fp(filename.c_str(), ios::in | ios::binary | ios::ate); + if(fp.is_open()) + { + std::string content; + + fp.seekg(0, fp.end); + content.reserve(fp.tellg()); + fp.seekg(0, fp.beg); + + content.assign((std::istreambuf_iterator(fp)), std::istreambuf_iterator()); + return content; + } + else + { + throw std::invalid_argument("Unable to open file"); + } +} + +void dump_header_map(std::ostream &os, const std::string &prefix, + const std::map &map) +{ + std::map::const_iterator it = map.begin(); + std::map::const_iterator end = map.end(); + + if (map.size()) { + os << " " << prefix << " ["; + for (; it != end; ++it) { + os << (*it).first << ":\"" << (*it).second << "\" "; + } + os << "]" << std::endl; + } +} + +void dump_arg_map(std::ostream &os, const std::string &prefix, + const std::map &map) +{ + std::map::const_iterator it = map.begin(); + std::map::const_iterator end = map.end(); + + if (map.size()) { + os << " " << prefix << " ["; + for (; it != end; ++it) { + os << (*it).first << ":\"" << (*it).second << "\" "; + } + os << "]" << std::endl; + } +} + +size_t base_unescaper(std::string& s, unescaper_ptr unescaper) +{ + if(s[0] == 0) return 0; + + if(unescaper != 0x0) + { + unescaper(s); + return s.size(); + } + + return http_unescape(s); +} + +}; +}; diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver.hpp new file mode 100644 index 00000000..772554af --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver.hpp @@ -0,0 +1,37 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#ifndef _HTTPSERVER_HPP_ +#define _HTTPSERVER_HPP_ + +#define _HTTPSERVER_HPP_INSIDE_ + +#include "httpserver/basic_auth_fail_response.hpp" +#include "httpserver/deferred_response.hpp" +#include "httpserver/digest_auth_fail_response.hpp" +#include "httpserver/file_response.hpp" +#include "httpserver/http_request.hpp" +#include "httpserver/http_resource.hpp" +#include "httpserver/http_response.hpp" +#include "httpserver/http_utils.hpp" +#include "httpserver/string_response.hpp" +#include "httpserver/webserver.hpp" + +#endif diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/basic_auth_fail_response.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/basic_auth_fail_response.hpp new file mode 100644 index 00000000..a28fa3d9 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/basic_auth_fail_response.hpp @@ -0,0 +1,68 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _BASIC_AUTH_FAIL_RESPONSE_HPP_ +#define _BASIC_AUTH_FAIL_RESPONSE_HPP_ + +#include +#include "http_utils.hpp" +#include "httpserver/string_response.hpp" + +struct MHD_Connection; +struct MHD_Response; + +namespace httpserver +{ + +class basic_auth_fail_response : public string_response +{ + public: + basic_auth_fail_response() = default; + + explicit basic_auth_fail_response( + const std::string& content, + const std::string& realm = "", + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain + ): + string_response(content, response_code, content_type), + realm(realm) + { + } + + basic_auth_fail_response(const basic_auth_fail_response& other) = default; + basic_auth_fail_response(basic_auth_fail_response&& other) noexcept = default; + basic_auth_fail_response& operator=(const basic_auth_fail_response& b) = default; + basic_auth_fail_response& operator=(basic_auth_fail_response&& b) = default; + + ~basic_auth_fail_response() = default; + + int enqueue_response(MHD_Connection* connection, MHD_Response* response); + + private: + std::string realm = ""; +}; + +} +#endif // _BASIC_AUTH_FAIL_RESPONSE_HPP_ diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/create_webserver.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/create_webserver.hpp new file mode 100644 index 00000000..39cb3ecb --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/create_webserver.hpp @@ -0,0 +1,306 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _CREATE_WEBSERVER_HPP_ +#define _CREATE_WEBSERVER_HPP_ + +#include + +#include "httpserver/http_response.hpp" +#include "httpserver/http_utils.hpp" + +#define DEFAULT_WS_TIMEOUT 180 +#define DEFAULT_WS_PORT 9898 + +namespace httpserver { + +class webserver; +class http_request; + +typedef const std::shared_ptr(*render_ptr)(const http_request&); +typedef bool(*validator_ptr)(const std::string&); +typedef void(*log_access_ptr)(const std::string&); +typedef void(*log_error_ptr)(const std::string&); + +class create_webserver +{ + public: + create_webserver() = default; + create_webserver(const create_webserver& b) = default; + create_webserver(create_webserver&& b) noexcept = default; + create_webserver& operator=(const create_webserver& b) = default; + create_webserver& operator=(create_webserver&& b) = default; + + explicit create_webserver(uint16_t port): + _port(port) + { + } + + create_webserver& port(uint16_t port) { _port = port; return *this; } + create_webserver& start_method( + const http::http_utils::start_method_T& start_method + ) + { + _start_method = start_method; return *this; + } + create_webserver& max_threads(int max_threads) + { + _max_threads = max_threads; return *this; + } + create_webserver& max_connections(int max_connections) + { + _max_connections = max_connections; return *this; + } + create_webserver& memory_limit(int memory_limit) + { + _memory_limit = memory_limit; return *this; + } + create_webserver& content_size_limit(size_t content_size_limit) + { + _content_size_limit = content_size_limit; return *this; + } + create_webserver& connection_timeout(int connection_timeout) + { + _connection_timeout = connection_timeout; return *this; + } + create_webserver& per_IP_connection_limit(int per_IP_connection_limit) + { + _per_IP_connection_limit = per_IP_connection_limit; return *this; + } + create_webserver& log_access(log_access_ptr log_access) + { + _log_access = log_access; return *this; + } + create_webserver& log_error(log_error_ptr log_error) + { + _log_error = log_error; return *this; + } + create_webserver& validator(validator_ptr validator) + { + _validator = validator; return *this; + } + create_webserver& unescaper(unescaper_ptr unescaper) + { + _unescaper = unescaper; return *this; + } + create_webserver& bind_address(const struct sockaddr* bind_address) + { + _bind_address = bind_address; return *this; + } + create_webserver& bind_socket(int bind_socket) + { + _bind_socket = bind_socket; return *this; + } + create_webserver& max_thread_stack_size(int max_thread_stack_size) + { + _max_thread_stack_size = max_thread_stack_size; return *this; + } + create_webserver& use_ssl() { _use_ssl = true; return *this; } + create_webserver& no_ssl() { _use_ssl = false; return *this; } + create_webserver& use_ipv6() { _use_ipv6 = true; return *this; } + create_webserver& no_ipv6() { _use_ipv6 = false; return *this; } + create_webserver& use_dual_stack() { _use_dual_stack = true; return *this; } + create_webserver& no_dual_stack() { _use_dual_stack = false; return *this; } + create_webserver& debug() { _debug = true; return *this; } + create_webserver& no_debug() { _debug = false; return *this; } + create_webserver& pedantic() { _pedantic = true; return *this; } + create_webserver& no_pedantic() { _pedantic = false; return *this; } + create_webserver& https_mem_key(const std::string& https_mem_key) + { + _https_mem_key = http::load_file(https_mem_key); + return *this; + } + create_webserver& https_mem_cert(const std::string& https_mem_cert) + { + _https_mem_cert = http::load_file(https_mem_cert); + return *this; + } + create_webserver& https_mem_trust(const std::string& https_mem_trust) + { + _https_mem_trust = http::load_file(https_mem_trust); + return *this; + } + create_webserver& raw_https_mem_key(const std::string& https_mem_key) + { + _https_mem_key = https_mem_key; return *this; + } + create_webserver& raw_https_mem_cert(const std::string& https_mem_cert) + { + _https_mem_cert = https_mem_cert; return *this; + } + create_webserver& raw_https_mem_trust( + const std::string& https_mem_trust + ) + { + _https_mem_trust = https_mem_trust; return *this; + } + create_webserver& https_priorities(const std::string& https_priorities) + { + _https_priorities = https_priorities; return *this; + } + create_webserver& cred_type(const http::http_utils::cred_type_T& cred_type) + { + _cred_type = cred_type; return *this; + } + create_webserver& digest_auth_random( + const std::string& digest_auth_random + ) + { + _digest_auth_random = digest_auth_random; return *this; + } + create_webserver& nonce_nc_size(int nonce_nc_size) + { + _nonce_nc_size = nonce_nc_size; return *this; + } + create_webserver& default_policy( + const http::http_utils::policy_T& default_policy + ) + { + _default_policy = default_policy; return *this; + } + create_webserver& basic_auth() + { + _basic_auth_enabled = true; return *this; + } + create_webserver& no_basic_auth() + { + _basic_auth_enabled = false; return *this; + } + create_webserver& digest_auth() + { + _digest_auth_enabled = true; return *this; + } + create_webserver& no_digest_auth() + { + _digest_auth_enabled = false; return *this; + } + create_webserver& deferred() + { + _deferred_enabled = true; return *this; + } + create_webserver& no_deferred() + { + _deferred_enabled = false; return *this; + } + create_webserver& regex_checking() + { + _regex_checking = true; return *this; + } + create_webserver& no_regex_checking() + { + _regex_checking = false; return *this; + } + create_webserver& ban_system() + { + _ban_system_enabled = true; return *this; + } + create_webserver& no_ban_system() + { + _ban_system_enabled = false; return *this; + } + create_webserver& post_process() + { + _post_process_enabled = true; return *this; + } + create_webserver& no_post_process() + { + _post_process_enabled = false; return *this; + } + create_webserver& single_resource() + { + _single_resource = true; return *this; + } + create_webserver& no_single_resource() + { + _single_resource = false; return *this; + } + create_webserver& tcp_nodelay() + { + _tcp_nodelay = true; return *this; + } + create_webserver& not_found_resource(render_ptr not_found_resource) + { + _not_found_resource = not_found_resource; return *this; + } + create_webserver& method_not_allowed_resource( + render_ptr method_not_allowed_resource + ) + { + _method_not_allowed_resource = method_not_allowed_resource; + return *this; + } + create_webserver& internal_error_resource( + render_ptr internal_error_resource + ) + { + _internal_error_resource = internal_error_resource; return *this; + } + + private: + uint16_t _port = DEFAULT_WS_PORT; + http::http_utils::start_method_T _start_method = http::http_utils::INTERNAL_SELECT; + int _max_threads = 0; + int _max_connections = 0; + int _memory_limit = 0; + size_t _content_size_limit = static_cast(-1); + int _connection_timeout = DEFAULT_WS_TIMEOUT; + int _per_IP_connection_limit = 0; + log_access_ptr _log_access = 0x0; + log_error_ptr _log_error = 0x0; + validator_ptr _validator = 0x0; + unescaper_ptr _unescaper = 0x0; + const struct sockaddr* _bind_address = 0x0; + int _bind_socket = 0; + int _max_thread_stack_size = 0; + bool _use_ssl = false; + bool _use_ipv6 = false; + bool _use_dual_stack = false; + bool _debug = false; + bool _pedantic = false; + std::string _https_mem_key = ""; + std::string _https_mem_cert = ""; + std::string _https_mem_trust = ""; + std::string _https_priorities = ""; + http::http_utils::cred_type_T _cred_type = http::http_utils::NONE; + std::string _digest_auth_random = ""; + int _nonce_nc_size = 0; + http::http_utils::policy_T _default_policy = http::http_utils::ACCEPT; + bool _basic_auth_enabled = true; + bool _digest_auth_enabled = true; + bool _regex_checking = true; + bool _ban_system_enabled = true; + bool _post_process_enabled = true; + bool _deferred_enabled = false; + bool _single_resource = false; + bool _tcp_nodelay = false; + render_ptr _not_found_resource = 0x0; + render_ptr _method_not_allowed_resource = 0x0; + render_ptr _internal_error_resource = 0x0; + + friend class webserver; +}; + +} //httpserver + +#endif //_CREATE_WEBSERVER_HPP_ diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/deferred_response.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/deferred_response.hpp new file mode 100644 index 00000000..9e4601e2 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/deferred_response.hpp @@ -0,0 +1,87 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _DEFERRED_RESPONSE_HPP_ +#define _DEFERRED_RESPONSE_HPP_ + +#include +#include +#include +#include +#include +#include "http_utils.hpp" +#include "httpserver/string_response.hpp" + +struct MHD_Response; + +namespace httpserver +{ + +namespace details +{ + MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t)); +} + +template +class deferred_response : public string_response +{ + public: + explicit deferred_response( + ssize_t(*cycle_callback)(std::shared_ptr, char*, size_t), + std::shared_ptr closure_data, + const std::string& content = "", + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain + ): + string_response(content, response_code, content_type), + cycle_callback(cycle_callback), + closure_data(closure_data) + { + } + + deferred_response(const deferred_response& other) = default; + deferred_response(deferred_response&& other) noexcept = default; + deferred_response& operator=(const deferred_response& b) = default; + deferred_response& operator=(deferred_response&& b) = default; + + ~deferred_response() = default; + + MHD_Response* get_raw_response() + { + return details::get_raw_response_helper((void*) this, &cb); + } + + private: + ssize_t (*cycle_callback)(std::shared_ptr, char*, size_t); + std::shared_ptr closure_data; + + static ssize_t cb(void* cls, uint64_t, char* buf, size_t max) + { + deferred_response* dfr = static_cast*>(cls); + return dfr->cycle_callback(dfr->closure_data, buf, max); + } +}; + +} +#endif // _DEFERRED_RESPONSE_HPP_ diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/details/http_endpoint.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/details/http_endpoint.hpp new file mode 100644 index 00000000..37fd0d8c --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/details/http_endpoint.hpp @@ -0,0 +1,206 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _HTTP_ENDPOINT_HPP_ +#define _HTTP_ENDPOINT_HPP_ + +#include +#include +#include +#include +#include + +namespace httpserver +{ + +namespace details +{ + +class http_resource; + +/** + * Class representing an Http Endpoint. It is an abstraction used by the APIs. +**/ +class http_endpoint +{ + public: + /** + * Copy constructor. It is useful expecially to copy regex_t structure that contains dinamically allocated data. + * @param h The http_endpoint to copy + **/ + http_endpoint(const http_endpoint& h); + + /** + * Class Destructor + **/ + ~http_endpoint(); //if inlined it causes problems during ruby wrapper compiling + + /** + * Operator overload for "less than operator". It is used to order endpoints in maps. + * @param b The http_endpoint to compare to + * @return boolean indicating if this is less than b. + **/ + bool operator <(const http_endpoint& b) const; + + /** + * Operator overload for "assignment operator". It is used to copy endpoints to existing objects. + * Is is functional expecially to copy regex_t structure that contains dinamically allocated data. + * @param h The http_endpoint to copy + * @return a reference to the http_endpoint obtained + **/ + http_endpoint& operator =(const http_endpoint& h); + + /** + * Method indicating if this endpoint 'matches' with the one passed. A passed endpoint matches a registered endpoint if + * the regex represented by the registered endpoint matches the passed one. + * @param url The endpoint to match + * @return true if the passed endpoint matches this. + **/ + bool match(const http_endpoint& url) const; + + /** + * Method used to get the complete endpoint url + * @return a string representing the url + **/ + const std::string& get_url_complete() const + { + return url_complete; + } + + const std::string& get_url_normalized() const + { + return url_normalized; + } + + /** + * Method used to get all pars defined inside an url. + * @return a vector of strings representing all found pars. + **/ + const std::vector& get_url_pars() const + { + return url_pars; + } + + /** + * Method used to get all pieces of an url; considering an url splitted by '/'. + * @return a vector of strings representing all found pieces. + **/ + const std::vector& get_url_pieces() const + { + return url_pieces; + } + + /** + * Method used to get indexes of all parameters inside url + * @return a vector of int indicating all positions. + **/ + const std::vector& get_chunk_positions() const + { + return chunk_positions; + } + + bool is_family_url() const + { + return family_url; + } + + bool is_regex_compiled() const + { + return reg_compiled; + } + + /** + * Default constructor of the class. + **/ + http_endpoint(): + url_complete("/"), + url_normalized("/"), + re_url_normalized(std::regex("")), // initialize empty + family_url(false), + reg_compiled(false) + { + } + + /** + * Constructor of the class http_endpoint. It is used to initialize an http_endpoint starting from a string form URL. + * @param url The string representation of the endpoint. All endpoints are in the form "/path/to/resource". + * @param family boolean that indicates if the endpoint is a family endpoint. + * A family endpoint is an endpoint that identifies a root and all its child like the same resource. + * For example, if I identify "/path/" like a family endpoint and I associate to it the resource "A", also + * "/path/to/res/" is automatically associated to resource "A". Default is false. + * @param registration boolean that indicates to the system if this is an endpoint that need to be registered to a webserver + * or it is simply an endpoint to be used for comparisons. Default is false. + * @param use_regex boolean that indicates if regexes are checked or not. Default is true. + **/ + http_endpoint(const std::string& url, + bool family = false, + bool registration = false, + bool use_regex = false + ); + private: + /** + * The complete url extracted + **/ + std::string url_complete; + + /** + * The url standardized in order to use standard comparisons or regexes + **/ + std::string url_normalized; + + /** + * Vector containing parameters extracted from url + **/ + std::vector url_pars; + + /** + * Pieces the url can be splitted into (consider '/' as separator) + **/ + std::vector url_pieces; + + /** + * Position of url pieces representing parameters + **/ + std::vector chunk_positions; + + /** + * Regex used in comparisons + **/ + std::regex re_url_normalized; + + /** + * Boolean indicating wheter the endpoint represents a family + **/ + bool family_url; + + /** + * Boolean indicating if the regex is compiled + **/ + bool reg_compiled; +}; + +}; + +}; +#endif diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/details/modded_request.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/details/modded_request.hpp new file mode 100644 index 00000000..1ebe5b12 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/details/modded_request.hpp @@ -0,0 +1,76 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _MODDED_REQUEST_HPP_ +#define _MODDED_REQUEST_HPP_ + +#include "httpserver/http_request.hpp" + +namespace httpserver +{ + +namespace details +{ + +struct modded_request +{ + struct MHD_PostProcessor *pp = 0x0; + std::string* complete_uri = 0x0; + std::string* standardized_url = 0x0; + webserver* ws = 0x0; + + const std::shared_ptr (httpserver::http_resource::*callback)(const httpserver::http_request&); + + http_request* dhr = 0x0; + std::shared_ptr dhrs; + bool second = false; + bool has_body = false; + + modded_request() = default; + + modded_request(const modded_request& b) = default; + modded_request(modded_request&& b) = default; + + modded_request& operator=(const modded_request& b) = default; + modded_request& operator=(modded_request&& b) = default; + + ~modded_request() + { + if (NULL != pp) + { + MHD_destroy_post_processor (pp); + } + if(second) + delete dhr; //TODO: verify. It could be an error + delete complete_uri; + delete standardized_url; + } + +}; + +} //details + +} //httpserver + +#endif //_MODDED_REQUEST_HPP_ diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/digest_auth_fail_response.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/digest_auth_fail_response.hpp new file mode 100644 index 00000000..50abcee2 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/digest_auth_fail_response.hpp @@ -0,0 +1,75 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _DIGEST_AUTH_FAIL_RESPONSE_HPP_ +#define _DIGEST_AUTH_FAIL_RESPONSE_HPP_ + +#include +#include "http_utils.hpp" +#include "httpserver/string_response.hpp" + +struct MHD_Connection; +struct MHD_Response; + +namespace httpserver +{ + +class digest_auth_fail_response : public string_response +{ + public: + digest_auth_fail_response() = default; + + digest_auth_fail_response( + const std::string& content, + const std::string& realm = "", + const std::string& opaque = "", + bool reload_nonce = false, + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain + ): + string_response(content, response_code, content_type), + realm(realm), + opaque(opaque), + reload_nonce(reload_nonce) + { + } + + digest_auth_fail_response(const digest_auth_fail_response& other) = default; + digest_auth_fail_response(digest_auth_fail_response&& other) noexcept = default; + digest_auth_fail_response& operator=(const digest_auth_fail_response& b) = default; + digest_auth_fail_response& operator=(digest_auth_fail_response&& b) = default; + + ~digest_auth_fail_response() = default; + + int enqueue_response(MHD_Connection* connection, MHD_Response* response); + + private: + std::string realm = ""; + std::string opaque = ""; + bool reload_nonce = false; +}; + +} + +#endif // _DIGEST_AUTH_FAIL_RESPONSE_HPP_ diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/file_response.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/file_response.hpp new file mode 100644 index 00000000..0c9386fb --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/file_response.hpp @@ -0,0 +1,67 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _FILE_RESPONSE_HPP_ +#define _FILE_RESPONSE_HPP_ + +#include +#include "http_utils.hpp" +#include "httpserver/http_response.hpp" + +struct MHD_Response; + +namespace httpserver +{ + +class file_response : public http_response +{ + public: + file_response() = default; + + explicit file_response( + const std::string& filename, + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain + ): + http_response(response_code, content_type), + filename(filename) + { + } + + file_response(const file_response& other) = default; + file_response(file_response&& other) noexcept = default; + + file_response& operator=(const file_response& b) = default; + file_response& operator=(file_response&& b) = default; + + ~file_response() = default; + + MHD_Response* get_raw_response(); + + private: + std::string filename = ""; +}; + +} +#endif // _FILE_RESPONSE_HPP_ diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/http_request.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/http_request.hpp new file mode 100644 index 00000000..6aacbfe6 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/http_request.hpp @@ -0,0 +1,355 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _HTTP_REQUEST_HPP_ +#define _HTTP_REQUEST_HPP_ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "httpserver/http_utils.hpp" + +struct MHD_Connection; + +namespace httpserver +{ + +/** + * Class representing an abstraction for an Http Request. It is used from classes using these apis to receive information through http protocol. +**/ +class http_request +{ + public: + static const std::string EMPTY; + + /** + * Method used to get the username eventually passed through basic authentication. + * @return string representation of the username. + **/ + const std::string get_user() const; + + /** + * Method used to get the username extracted from a digest authentication + * @return the username + **/ + const std::string get_digested_user() const; + + /** + * Method used to get the password eventually passed through basic authentication. + * @return string representation of the password. + **/ + const std::string get_pass() const; + + /** + * Method used to get the path requested + * @return string representing the path requested. + **/ + const std::string& get_path() const + { + return path; + } + + /** + * Method used to get all pieces of the path requested; considering an url splitted by '/'. + * @return a vector of strings containing all pieces + **/ + const std::vector get_path_pieces() const + { + return http::http_utils::tokenize_url(path); + } + + /** + * Method used to obtain a specified piece of the path; considering an url splitted by '/'. + * @param index the index of the piece selected + * @return the selected piece in form of string + **/ + const std::string get_path_piece(int index) const + { + std::vector post_path = get_path_pieces(); + if(((int)(post_path.size())) > index) + return post_path[index]; + return EMPTY; + } + + /** + * Method used to get the METHOD used to make the request. + * @return string representing the method. + **/ + const std::string& get_method() const + { + return method; + } + + /** + * Method used to get all headers passed with the request. + * @param result a map > that will be filled with all headers + * @result the size of the map + **/ + const std::map get_headers() const; + + /** + * Method used to get all footers passed with the request. + * @param result a map > that will be filled with all footers + * @result the size of the map + **/ + const std::map get_footers() const; + + /** + * Method used to get all cookies passed with the request. + * @param result a map > that will be filled with all cookies + * @result the size of the map + **/ + const std::map get_cookies() const; + + /** + * Method used to get all args passed with the request. + * @param result a map > that will be filled with all args + * @result the size of the map + **/ + const std::map get_args() const; + + /** + * Method used to get a specific header passed with the request. + * @param key the specific header to get the value from + * @return the value of the header. + **/ + const std::string get_header(const std::string& key) const; + + const std::string get_cookie(const std::string& key) const; + + /** + * Method used to get a specific footer passed with the request. + * @param key the specific footer to get the value from + * @return the value of the footer. + **/ + const std::string get_footer(const std::string& key) const; + + /** + * Method used to get a specific argument passed with the request. + * @param ket the specific argument to get the value from + * @return the value of the arg. + **/ + const std::string get_arg(const std::string& key) const; + + /** + * Method used to get the content of the request. + * @return the content in string representation + **/ + const std::string& get_content() const + { + return content; + } + + /** + * Method to check whether the size of the content reached or exceeded content_size_limit. + * @return boolean + **/ + bool content_too_large() const + { + return content.size()>=content_size_limit; + } + /** + * Method used to get the content of the query string.. + * @return the query string in string representation + **/ + const std::string get_querystring() const; + + /** + * Method used to get the version of the request. + * @return the version in string representation + **/ + const std::string& get_version() const + { + return version; + } + + /** + * Method used to get the requestor. + * @return the requestor + **/ + const std::string get_requestor() const; + + /** + * Method used to get the requestor port used. + * @return the requestor port + **/ + unsigned short get_requestor_port() const; + + bool check_digest_auth(const std::string& realm, + const std::string& password, + int nonce_timeout, bool& reload_nonce + ) const; + + friend std::ostream &operator<< (std::ostream &os, http_request &r); + + private: + /** + * Default constructor of the class. It is a specific responsibility of apis to initialize this type of objects. + **/ + http_request() = default; + + http_request(MHD_Connection* underlying_connection, unescaper_ptr unescaper): + underlying_connection(underlying_connection), + unescaper(unescaper) + { + } + + /** + * Copy constructor. + * @param b http_request b to copy attributes from. + **/ + http_request(const http_request& b) = default; + http_request(http_request&& b) noexcept = default; + + http_request& operator=(const http_request& b) = default; + http_request& operator=(http_request&& b) = default; + + std::string path; + std::string method; + std::map args; + std::string content = ""; + size_t content_size_limit = static_cast(-1); + std::string version; + + struct MHD_Connection* underlying_connection = 0x0; + + unescaper_ptr unescaper = 0x0; + + static MHD_Result build_request_header(void *cls, enum MHD_ValueKind kind, + const char *key, const char *value + ); + + static MHD_Result build_request_args(void *cls, enum MHD_ValueKind kind, + const char *key, const char *value + ); + + static MHD_Result build_request_querystring(void *cls, enum MHD_ValueKind kind, + const char *key, const char *value + ); + + /** + * Method used to set an argument value by key. + * @param key The name identifying the argument + * @param value The value assumed by the argument + **/ + void set_arg(const std::string& key, const std::string& value) + { + args[key] = value.substr(0,content_size_limit); + } + + /** + * Method used to set an argument value by key. + * @param key The name identifying the argument + * @param value The value assumed by the argument + * @param size The size in number of char of the value parameter. + **/ + void set_arg(const char* key, const char* value, size_t size) + { + args[key] = std::string(value, std::min(size, content_size_limit)); + } + + /** + * Method used to set the content of the request + * @param content The content to set. + **/ + void set_content(const std::string& content) + { + this->content = content.substr(0,content_size_limit); + } + + /** + * Method used to set the maximum size of the content + * @param content_size_limit The limit on the maximum size of the content and arg's. + **/ + void set_content_size_limit(size_t content_size_limit) + { + this->content_size_limit = content_size_limit; + } + + /** + * Method used to append content to the request preserving the previous inserted content + * @param content The content to append. + * @param size The size of the data to append. + **/ + void grow_content(const char* content, size_t size) + { + this->content.append(content, size); + if (this->content.size() > content_size_limit) + { + this->content.resize (content_size_limit); + } + } + + /** + * Method used to set the path requested. + * @param path The path searched by the request. + **/ + void set_path(const std::string& path) + { + this->path = path; + } + + /** + * Method used to set the request METHOD + * @param method The method to set for the request + **/ + void set_method(const std::string& method); + + /** + * Method used to set the request http version (ie http 1.1) + * @param version The version to set in form of string + **/ + void set_version(const std::string& version) + { + this->version = version; + } + + /** + * Method used to set all arguments of the request. + * @param args The args key-value map to set for the request. + **/ + void set_args(const std::map& args) + { + std::map::const_iterator it; + for(it = args.begin(); it != args.end(); ++it) + this->args[it->first] = it->second.substr(0,content_size_limit); + } + + const std::string get_connection_value(const std::string& key, enum MHD_ValueKind kind) const; + const std::map get_headerlike_values(enum MHD_ValueKind kind) const; + + friend class webserver; +}; + +std::ostream &operator<< (std::ostream &os, const http_request &r); + +}; +#endif diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/http_resource.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/http_resource.hpp new file mode 100644 index 00000000..04f67cb8 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/http_resource.hpp @@ -0,0 +1,225 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _http_resource_hpp_ +#define _http_resource_hpp_ + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include + +namespace httpserver { class http_request; } +namespace httpserver { class http_response; } + +namespace httpserver { + +namespace details { std::shared_ptr empty_render(const http_request& r); }; + +/** + * Class representing a callable http resource. +**/ + +void resource_init(std::map& res); + +class http_resource +{ + public: + /** + * Class destructor + **/ + virtual ~http_resource() = default; + + /** + * Method used to answer to a generic request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render(const http_request& req) + { + return details::empty_render(req); + } + /** + * Method used to answer to a GET request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_GET(const http_request& req) + { + return render(req); + } + /** + * Method used to answer to a POST request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_POST(const http_request& req) + { + return render(req); + } + /** + * Method used to answer to a PUT request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_PUT(const http_request& req) + { + return render(req); + } + /** + * Method used to answer to a HEAD request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_HEAD(const http_request& req) + { + return render(req); + } + /** + * Method used to answer to a DELETE request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_DELETE(const http_request& req) + { + return render(req); + } + /** + * Method used to answer to a TRACE request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_TRACE(const http_request& req) + { + return render(req); + } + /** + * Method used to answer to a OPTIONS request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_OPTIONS(const http_request& req) + { + return render(req); + } + /** + * Method used to answer to a PATCH request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_PATCH(const http_request& req) + { + return render(req); + } + /** + * Method used to answer to a CONNECT request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_CONNECT(const http_request& req) + { + return render(req); + } + /** + * Method used to set if a specific method is allowed or not on this request + * @param method method to set permission on + * @param allowed boolean indicating if the method is allowed or not + **/ + void set_allowing(const std::string& method, bool allowed) + { + if(allowed_methods.count(method)) + { + allowed_methods[method] = allowed; + } + } + /** + * Method used to implicitly allow all methods + **/ + void allow_all() + { + std::map::iterator it; + for ( it=allowed_methods.begin() ; it != allowed_methods.end(); ++it ) + allowed_methods[(*it).first] = true; + } + /** + * Method used to implicitly disallow all methods + **/ + void disallow_all() + { + std::map::iterator it; + for ( it=allowed_methods.begin() ; it != allowed_methods.end(); ++it ) + allowed_methods[(*it).first] = false; + } + /** + * Method used to discover if an http method is allowed or not for this resource + * @param method Method to discover allowings + * @return true if the method is allowed + **/ + bool is_allowed(const std::string& method) + { + if(allowed_methods.count(method)) + { + return allowed_methods[method]; + } + else + { +#ifdef DEBUG + std::map::iterator it; + for(it = allowed_methods.begin(); it != allowed_methods.end(); ++it) + { + std::cout << (*it).first << " -> " << (*it).second << std::endl; + } +#endif //DEBUG + return false; + } + } + protected: + /** + * Constructor of the class + **/ + http_resource() + { + resource_init(allowed_methods); + } + + /** + * Copy constructor + **/ + http_resource(const http_resource& b) = default; + http_resource(http_resource&& b) noexcept = default; + http_resource& operator=(const http_resource& b) = default; + http_resource& operator=(http_resource&& b) = default; + + private: + friend class webserver; + friend void resource_init(std::map& res); + std::map allowed_methods; +}; + +} +#endif diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/http_response.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/http_response.hpp new file mode 100644 index 00000000..1f3f0971 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/http_response.hpp @@ -0,0 +1,157 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _HTTP_RESPONSE_HPP_ +#define _HTTP_RESPONSE_HPP_ + +#include +#include +#include +#include "httpserver/http_utils.hpp" + +struct MHD_Connection; +struct MHD_Response; + +namespace httpserver +{ + +/** + * Class representing an abstraction for an Http Response. It is used from classes using these apis to send information through http protocol. +**/ +class http_response +{ + public: + http_response() = default; + + explicit http_response(int response_code, const std::string& content_type): + response_code(response_code) + { + headers[http::http_utils::http_header_content_type] = content_type; + } + + /** + * Copy constructor + * @param b The http_response object to copy attributes value from. + **/ + http_response(const http_response& b) = default; + http_response(http_response&& b) noexcept = default; + + http_response& operator=(const http_response& b) = default; + http_response& operator=(http_response&& b) noexcept = default; + + virtual ~http_response() = default; + + /** + * Method used to get a specified header defined for the response + * @param key The header identification + * @return a string representing the value assumed by the header + **/ + const std::string& get_header(const std::string& key) + { + return headers[key]; + } + + /** + * Method used to get a specified footer defined for the response + * @param key The footer identification + * @return a string representing the value assumed by the footer + **/ + const std::string& get_footer(const std::string& key) + { + return footers[key]; + } + + const std::string& get_cookie(const std::string& key) + { + return cookies[key]; + } + + /** + * Method used to get all headers passed with the request. + * @return a map containing all headers. + **/ + const std::map& get_headers() const + { + return headers; + } + + /** + * Method used to get all footers passed with the request. + * @return a map containing all footers. + **/ + const std::map& get_footers() const + { + return footers; + } + + const std::map& get_cookies() const + { + return cookies; + } + + /** + * Method used to get the response code from the response + * @return The response code + **/ + int get_response_code() const + { + return response_code; + } + + void with_header(const std::string& key, const std::string& value) + { + headers[key] = value; + } + + void with_footer(const std::string& key, const std::string& value) + { + footers[key] = value; + } + + void with_cookie(const std::string& key, const std::string& value) + { + cookies[key] = value; + } + + void shoutCAST(); + + virtual MHD_Response* get_raw_response(); + virtual void decorate_response(MHD_Response* response); + virtual int enqueue_response(MHD_Connection* connection, MHD_Response* response); + + private: + int response_code = -1; + + std::map headers; + std::map footers; + std::map cookies; + + protected: + friend std::ostream &operator<< (std::ostream &os, const http_response &r); +}; + +std::ostream &operator<< (std::ostream &os, const http_response &r); + +}; +#endif diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/http_utils.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/http_utils.hpp new file mode 100644 index 00000000..e2aa0339 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/http_utils.hpp @@ -0,0 +1,395 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _HTTPUTILS_H_ +#define _HTTPUTILS_H_ + +#ifdef HAVE_GNUTLS +#include +#endif + +// needed to force Vista as a bare minimum to have inet_ntop (libmicro defines +// this to include XP support as a lower version). +#if defined(__MINGW32__) || defined(__CYGWIN32__) +#define _WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#endif + +// needed to have the fd_set definition ahead of microhttpd.h import +#if defined(__CYGWIN__) +#include +#endif + +#include +#include + +#if !defined(__MINGW32__) +#include +#endif + +#include +#include +#include +#include +#include +#include + +#define DEFAULT_MASK_VALUE 0xFFFF + +#if MHD_VERSION < 0x00097002 +typedef int MHD_Result; +#endif + +namespace httpserver { + +typedef void(*unescaper_ptr)(std::string&); + +namespace http { + +class http_utils +{ + public: + + enum cred_type_T + { + NONE = -1 +#ifdef HAVE_GNUTLS + ,CERTIFICATE = GNUTLS_CRD_CERTIFICATE, + ANON = GNUTLS_CRD_ANON, + SRP = GNUTLS_CRD_SRP, + PSK = GNUTLS_CRD_PSK, + IA = GNUTLS_CRD_IA +#endif + }; + + enum start_method_T + { +#if defined(__MINGW32__) || defined(__CYGWIN__) + #ifdef ENABLE_POLL + INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_POLL, + #else + INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY, + #endif +#else + #ifdef ENABLE_EPOLL + INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_EPOLL | MHD_USE_EPOLL_TURBO, + #else + INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY, + #endif +#endif +#ifdef ENABLE_POLL + THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL +#else + THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION +#endif + }; + + enum policy_T + { + ACCEPT, + REJECT + }; + + enum IP_version_T + { + IPV4 = 4, IPV6 = 16 + }; + + static const short http_method_connect_code; + static const short http_method_delete_code; + static const short http_method_get_code; + static const short http_method_head_code; + static const short http_method_options_code; + static const short http_method_post_code; + static const short http_method_put_code; + static const short http_method_trace_code; + static const short http_method_patch_code; + static const short http_method_unknown_code; + + static const int http_continue; + static const int http_switching_protocol; + static const int http_processing; + + static const int http_ok; + static const int http_created; + static const int http_accepted; + static const int http_non_authoritative_information; + static const int http_no_content; + static const int http_reset_content; + static const int http_partial_content; + static const int http_multi_status; + + static const int http_multiple_choices; + static const int http_moved_permanently; + static const int http_found; + static const int http_see_other; + static const int http_not_modified; + static const int http_use_proxy; + static const int http_switch_proxy; + static const int http_temporary_redirect; + + static const int http_bad_request; + static const int http_unauthorized; + static const int http_payment_required; + static const int http_forbidden; + static const int http_not_found; + static const int http_method_not_allowed; + static const int http_method_not_acceptable; + static const int http_proxy_authentication_required; + static const int http_request_timeout; + static const int http_conflict; + static const int http_gone; + static const int http_length_required; + static const int http_precondition_failed; + static const int http_request_entity_too_large; + static const int http_request_uri_too_long; + static const int http_unsupported_media_type; + static const int http_requested_range_not_satisfiable; + static const int http_expectation_failed; + static const int http_unprocessable_entity; + static const int http_locked; + static const int http_failed_dependency; + static const int http_unordered_collection; + static const int http_upgrade_required; + static const int http_retry_with; + + static const int http_internal_server_error; + static const int http_not_implemented; + static const int http_bad_gateway; + static const int http_service_unavailable; + static const int http_gateway_timeout; + static const int http_version_not_supported; + static const int http_variant_also_negotiated; + static const int http_insufficient_storage; + static const int http_bandwidth_limit_exceeded; + static const int http_not_extended; + + static const int shoutcast_response; + + /* See also: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ + static const std::string http_header_accept; + static const std::string http_header_accept_charset; + static const std::string http_header_accept_encoding; + static const std::string http_header_accept_language; + static const std::string http_header_accept_ranges; + static const std::string http_header_age; + static const std::string http_header_allow; + static const std::string http_header_authorization; + static const std::string http_header_cache_control; + static const std::string http_header_connection; + static const std::string http_header_content_encoding; + static const std::string http_header_content_language; + static const std::string http_header_content_length; + static const std::string http_header_content_location; + static const std::string http_header_content_md5; + static const std::string http_header_content_range; + static const std::string http_header_content_type; + static const std::string http_header_date; + static const std::string http_header_etag; + static const std::string http_header_expect; + static const std::string http_header_expires; + static const std::string http_header_from; + static const std::string http_header_host; + static const std::string http_header_if_match; + static const std::string http_header_if_modified_since; + static const std::string http_header_if_none_match; + static const std::string http_header_if_range; + static const std::string http_header_if_unmodified_since; + static const std::string http_header_last_modified; + static const std::string http_header_location; + static const std::string http_header_max_forwards; + static const std::string http_header_pragma; + static const std::string http_header_proxy_authenticate; + static const std::string http_header_proxy_authentication; + static const std::string http_header_range; + static const std::string http_header_referer; + static const std::string http_header_retry_after; + static const std::string http_header_server; + static const std::string http_header_te; + static const std::string http_header_trailer; + static const std::string http_header_transfer_encoding; + static const std::string http_header_upgrade; + static const std::string http_header_user_agent; + static const std::string http_header_vary; + static const std::string http_header_via; + static const std::string http_header_warning; + static const std::string http_header_www_authenticate; + + static const std::string http_version_1_0; + static const std::string http_version_1_1; + + static const std::string http_method_connect; + static const std::string http_method_delete; + static const std::string http_method_head; + static const std::string http_method_get; + static const std::string http_method_options; + static const std::string http_method_post; + static const std::string http_method_put; + static const std::string http_method_trace; + static const std::string http_method_patch; + + static const std::string http_post_encoding_form_urlencoded; + static const std::string http_post_encoding_multipart_formdata; + + static const std::string text_plain; + + static std::vector tokenize_url(const std::string&, + const char separator = '/' + ); + static std::string standardize_url(const std::string&); +}; + +#define COMPARATOR(x, y, op) \ + { \ + size_t l1 = (x).size();\ + size_t l2 = (y).size();\ + if (l1 < l2) return true;\ + if (l1 > l2) return false;\ + \ + for (size_t n = 0; n < l1; n++)\ + {\ + int xc = op((x)[n]);\ + int yc = op((y)[n]);\ + if (xc < yc) return true;\ + if (xc > yc) return false;\ + }\ + return false;\ + } + +class header_comparator { + public: + /** + * Operator used to compare strings. + * @param first string + * @param second string + **/ + bool operator()(const std::string& x,const std::string& y) const + { + COMPARATOR(x, y, std::toupper); + } +}; + +/** + * Operator Class that is used to compare two strings. The comparison can be sensitive or insensitive. + * The default comparison is case sensitive. To obtain insensitive comparison you have to pass in + * compilation phase the flag CASE_INSENSITIVE to the preprocessor. +**/ +class arg_comparator { + public: + /** + * Operator used to compare strings. + * @param first string + * @param second string + **/ + bool operator()(const std::string& x,const std::string& y) const + { +#ifdef CASE_INSENSITIVE + COMPARATOR(x, y, std::toupper); +#else + COMPARATOR(x, y, ); +#endif + } +}; + +struct ip_representation +{ + http_utils::IP_version_T ip_version; + unsigned short pieces[16]; + unsigned short mask; + + ip_representation(http_utils::IP_version_T ip_version) : + ip_version(ip_version) + { + mask = DEFAULT_MASK_VALUE; + std::fill(pieces, pieces + 16, 0); + } + + ip_representation(const std::string& ip); + ip_representation(const struct sockaddr* ip); + + bool operator <(const ip_representation& b) const; + int weight() const + { + //variable-precision SWAR algorithm + unsigned short x = mask; + x = x - ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + return (((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; + } +}; + +/** + * Method used to get an ip in form of string from a sockaddr structure + * @param sa The sockaddr object to find the ip address from + * @param maxlen Maxlen of the address (automatically discovered if not passed) + * @return string containing the ip address +**/ +std::string get_ip_str(const struct sockaddr *sa, socklen_t maxlen = 0); + +std::string get_ip_str_new(const struct sockaddr* sa, socklen_t maxlen = 0); +/** + * Method used to get a port from a sockaddr + * @param sa The sockaddr object to find the port from + * @return short representing the port +**/ +unsigned short get_port(const struct sockaddr* sa); + +/** + * Method to output the contents of a headers map to a std::ostream + * @param os The ostream + * @param prefix Prefix to identify the map + * @param map +**/ +void dump_header_map(std::ostream &os, const std::string &prefix, + const std::map &map); + +/** + * Method to output the contents of an arguments map to a std::ostream + * @param os The ostream + * @param prefix Prefix to identify the map + * @param map +**/ +void dump_arg_map(std::ostream &os, const std::string &prefix, + const std::map &map); + +/** + * Process escape sequences ('+'=space, %HH) Updates val in place; the + * result should be UTF-8 encoded and cannot be larger than the input. + * The result must also still be 0-terminated. + * + * @param val the string to unescape + * @return length of the resulting val (strlen(val) maybe + * shorter afterwards due to elimination of escape sequences) + */ +size_t http_unescape (std::string& val); + +const std::string load_file (const std::string& filename); + +size_t base_unescaper(std::string&, unescaper_ptr unescaper); + +}; +}; +#endif + diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/string_response.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/string_response.hpp new file mode 100644 index 00000000..43e7580d --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/string_response.hpp @@ -0,0 +1,68 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _STRING_RESPONSE_HPP_ +#define _STRING_RESPONSE_HPP_ + +#include +#include +#include "http_utils.hpp" +#include "httpserver/http_response.hpp" + +struct MHD_Response; + +namespace httpserver +{ + +class string_response : public http_response +{ + public: + string_response() = default; + + explicit string_response( + std::string content, + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain + ): + http_response(response_code, content_type), + content(std::move(content)) + { + } + + string_response(const string_response& other) = default; + string_response(string_response&& other) noexcept = default; + + string_response& operator=(const string_response& b) = default; + string_response& operator=(string_response&& b) = default; + + ~string_response() = default; + + MHD_Response* get_raw_response(); + + private: + std::string content = ""; +}; + +} +#endif // _STRING_RESPONSE_HPP_ diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/string_utilities.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/string_utilities.hpp new file mode 100644 index 00000000..e61762ab --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/string_utilities.hpp @@ -0,0 +1,51 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _STRING_UTILITIES_H_ +#define _STRING_UTILITIES_H_ + +#include +#include + +namespace httpserver +{ +namespace string_utilities +{ + +/** + * Function used to convert a string to its uppercase version. + * It generates a new string in output + * @param str The string to turn uppercase + * @return a string that is the uppercase version of the previous +**/ +const std::string to_upper_copy(const std::string& str); +const std::string to_lower_copy(const std::string& str); +const std::vector string_split(const std::string& s, + char sep = ' ', bool collapse = true +); +void to_upper(std::string& str); +}; +}; + +#endif diff --git a/3rd_party/libhttpserver-0.18.2/src/httpserver/webserver.hpp b/3rd_party/libhttpserver-0.18.2/src/httpserver/webserver.hpp new file mode 100644 index 00000000..02d626c5 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/httpserver/webserver.hpp @@ -0,0 +1,251 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _FRAMEWORK_WEBSERVER_HPP_ +#define _FRAMEWORK_WEBSERVER_HPP_ + +#define NOT_FOUND_ERROR "Not Found" +#define METHOD_ERROR "Method not Allowed" +#define NOT_METHOD_ERROR "Method not Acceptable" +#define GENERIC_ERROR "Internal Error" + +#include +#include +#include +#include +#include + +#if !defined(__MINGW32__) +#include +#endif + +#include +#include +#include +#include + +#include "http_utils.hpp" +#include "httpserver/create_webserver.hpp" +#include "httpserver/details/http_endpoint.hpp" + +namespace httpserver { class http_resource; } +namespace httpserver { class http_response; } +namespace httpserver { namespace details { struct modded_request; } } + +struct MHD_Connection; + +namespace httpserver { + +/** + * Class representing the webserver. Main class of the apis. +**/ +class webserver +{ + public: + webserver(const create_webserver& params); + /** + * Destructor of the class + **/ + ~webserver(); + /** + * Method used to start the webserver. + * This method can be blocking or not. + * @param blocking param indicating if the method is blocking or not + * @return a boolean indicating if the webserver is running or not. + **/ + bool start(bool blocking = false); + /** + * Method used to stop the webserver. + * @return true if the webserver is stopped. + **/ + bool stop(); + /** + * Method used to evaluate if the server is running or not. + * @return true if the webserver is running + **/ + bool is_running(); + /** + * Method used to register a resource with the webserver. + * @param resource The url pointing to the resource. This url could be also parametrized in the form /path/to/url/{par1}/and/{par2} + * or a regular expression. + * @param http_resource http_resource pointer to register. + * @param family boolean indicating whether the resource is registered for the endpoint and its child or not. + * @return true if the resource was registered + **/ + bool register_resource(const std::string& resource, + http_resource* res, bool family = false + ); + + void unregister_resource(const std::string& resource); + void ban_ip(const std::string& ip); + void allow_ip(const std::string& ip); + void unban_ip(const std::string& ip); + void disallow_ip(const std::string& ip); + + log_access_ptr get_access_logger() const + { + return log_access; + } + + log_error_ptr get_error_logger() const + { + return log_error; + } + + validator_ptr get_request_validator() const + { + return validator; + } + + unescaper_ptr get_unescaper() const + { + return unescaper; + } + + /** + * Method used to kill the webserver waiting for it to terminate + **/ + void sweet_kill(); + + protected: + webserver& operator=(const webserver& other); + + private: + const uint16_t port; + http::http_utils::start_method_T start_method; + const int max_threads; + const int max_connections; + const int memory_limit; + const size_t content_size_limit; + const int connection_timeout; + const int per_IP_connection_limit; + log_access_ptr log_access; + log_error_ptr log_error; + validator_ptr validator; + unescaper_ptr unescaper; + const struct sockaddr* bind_address; + /* Changed type to MHD_socket because this type will always reflect the + platform's actual socket type (e.g. SOCKET on windows, int on unixes)*/ + MHD_socket bind_socket; + const int max_thread_stack_size; + const bool use_ssl; + const bool use_ipv6; + const bool use_dual_stack; + const bool debug; + const bool pedantic; + const std::string https_mem_key; + const std::string https_mem_cert; + const std::string https_mem_trust; + const std::string https_priorities; + const http::http_utils::cred_type_T cred_type; + const std::string digest_auth_random; + const int nonce_nc_size; + bool running; + const http::http_utils::policy_T default_policy; + const bool basic_auth_enabled; + const bool digest_auth_enabled; + const bool regex_checking; + const bool ban_system_enabled; + const bool post_process_enabled; + const bool deferred_enabled; + bool single_resource; + bool tcp_nodelay; + pthread_mutex_t mutexwait; + pthread_cond_t mutexcond; + render_ptr not_found_resource; + render_ptr method_not_allowed_resource; + render_ptr internal_error_resource; + std::map registered_resources; + std::map registered_resources_str; + + std::set bans; + std::set allowances; + + struct MHD_Daemon* daemon; + + const std::shared_ptr method_not_allowed_page(details::modded_request* mr) const; + const std::shared_ptr internal_error_page(details::modded_request* mr, bool force_our = false) const; + const std::shared_ptr not_found_page(details::modded_request* mr) const; + + static void request_completed(void *cls, + struct MHD_Connection *connection, void **con_cls, + enum MHD_RequestTerminationCode toe + ); + + static MHD_Result answer_to_connection + ( + void* cls, MHD_Connection* connection, + const char* url, const char* method, + const char* version, const char* upload_data, + size_t* upload_data_size, void** con_cls + ); + static MHD_Result post_iterator + ( + void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *filename, + const char *content_type, + const char *transfer_encoding, + const char *data, uint64_t off, size_t size + ); + static void upgrade_handler + ( + void *cls, + struct MHD_Connection* connection, + void **con_cls, int upgrade_socket + ); + + MHD_Result requests_answer_first_step(MHD_Connection* connection, + struct details::modded_request* mr + ); + + MHD_Result requests_answer_second_step(MHD_Connection* connection, + const char* method, const char* version, const char* upload_data, + size_t* upload_data_size, struct details::modded_request* mr + ); + + MHD_Result finalize_answer(MHD_Connection* connection, + struct details::modded_request* mr, const char* method + ); + + MHD_Result complete_request(MHD_Connection* connection, + struct details::modded_request* mr, + const char* version, const char* method + ); + + friend MHD_Result policy_callback (void *cls, + const struct sockaddr* addr, socklen_t addrlen + ); + friend void error_log(void* cls, const char* fmt, va_list ap); + friend void access_log(webserver* cls, std::string uri); + friend void* uri_log(void* cls, const char* uri); + friend size_t unescaper_func(void * cls, + struct MHD_Connection *c, char *s + ); + friend class http_response; +}; + +}; +#endif //_FRAMEWORK_WEBSERVER_HPP__ diff --git a/3rd_party/libhttpserver-0.18.2/src/string_response.cpp b/3rd_party/libhttpserver-0.18.2/src/string_response.cpp new file mode 100644 index 00000000..75a557f8 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/string_response.cpp @@ -0,0 +1,42 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "httpserver/string_response.hpp" +#include +#include + +struct MHD_Response; + +using namespace std; + +namespace httpserver +{ + +MHD_Response* string_response::get_raw_response() +{ + size_t size = &(*content.end()) - &(*content.begin()); + return MHD_create_response_from_buffer( + size, + (void*) content.c_str(), + MHD_RESPMEM_PERSISTENT + ); +} + +} diff --git a/3rd_party/libhttpserver-0.18.2/src/string_utilities.cpp b/3rd_party/libhttpserver-0.18.2/src/string_utilities.cpp new file mode 100644 index 00000000..a3937f2e --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/string_utilities.cpp @@ -0,0 +1,85 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "httpserver/string_utilities.hpp" + +#include +#include +#include +#include +#include + +namespace httpserver +{ +namespace string_utilities +{ + +const std::string to_upper_copy(const std::string& str) +{ + std::string result = str; + std::transform(result.begin(), + result.end(), + result.begin(), + (int(*)(int)) std::toupper + ); + + return result; +} + +void to_upper(std::string& str) +{ + std::transform(str.begin(), + str.end(), + str.begin(), + (int(*)(int)) std::toupper + ); +} + +const std::string to_lower_copy(const std::string& str) +{ + std::string result = str; + std::transform(result.begin(), + result.end(), + result.begin(), + (int(*)(int)) std::tolower + ); + + return result; +} + +const std::vector string_split( + const std::string& s, + char sep, + bool collapse +) +{ + std::vector result; + + std::istringstream buf(s); + for(std::string token; getline(buf, token, sep); ) + { + if((collapse && token != "") || !collapse) + result.push_back(token); + } + return result; +} + +}; +}; diff --git a/3rd_party/libhttpserver-0.18.2/src/webserver.cpp b/3rd_party/libhttpserver-0.18.2/src/webserver.cpp new file mode 100644 index 00000000..64892b3b --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/src/webserver.cpp @@ -0,0 +1,650 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "httpserver/webserver.hpp" + +#if defined(_WIN32) && !defined(__CYGWIN__) +#include +#include +#ifndef _WINDOWS +#define _WINDOWS +#endif +#else +#if defined(__FreeBSD__) +#include +#endif +#if defined(__CYGWIN__) +#include +#endif +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gettext.h" +#include "strings.h" +#include "httpserver/create_webserver.hpp" +#include "httpserver/details/http_endpoint.hpp" +#include "httpserver/details/modded_request.hpp" +#include "httpserver/http_request.hpp" +#include "httpserver/http_resource.hpp" +#include "httpserver/http_response.hpp" +#include "httpserver/http_utils.hpp" +#include "httpserver/string_response.hpp" + +struct MHD_Connection; + +#define _REENTRANT 1 + +#ifndef SOCK_CLOEXEC +#define SOCK_CLOEXEC 02000000 +#endif + +#if MHD_VERSION < 0x00097002 +typedef int MHD_Result; +#endif + +using namespace std; + +namespace httpserver { + +using namespace http; + +MHD_Result policy_callback(void *, const struct sockaddr *, socklen_t); +void error_log(void *, const char *, va_list); +void *uri_log(void *, const char *); +void access_log(webserver *, string); +size_t unescaper_func(void *, struct MHD_Connection *, char *); + +struct compare_value { + bool operator()(const std::pair &left, const std::pair &right) const { + return left.second < right.second; + } +}; + +#if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) +static void catcher(int sig) {} +#endif + +static void ignore_sigpipe() { +// Mingw doesn't implement SIGPIPE +#if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) + struct sigaction oldsig; + struct sigaction sig; + + sig.sa_handler = &catcher; + sigemptyset(&sig.sa_mask); +#ifdef SA_INTERRUPT + sig.sa_flags = SA_INTERRUPT; /* SunOS */ +#else // SA_INTERRUPT + sig.sa_flags = SA_RESTART; +#endif // SA_INTERRUPTT + if (0 != sigaction(SIGPIPE, &sig, &oldsig)) + fprintf(stderr, gettext("Failed to install SIGPIPE handler: %s\n"), strerror(errno)); +#endif +} + +// WEBSERVER +webserver::webserver(const create_webserver ¶ms) + : port(params._port), + start_method(params._start_method), + max_threads(params._max_threads), + max_connections(params._max_connections), + memory_limit(params._memory_limit), + content_size_limit(params._content_size_limit), + connection_timeout(params._connection_timeout), + per_IP_connection_limit(params._per_IP_connection_limit), + log_access(params._log_access), + log_error(params._log_error), + validator(params._validator), + unescaper(params._unescaper), + bind_address(params._bind_address), + bind_socket(params._bind_socket), + max_thread_stack_size(params._max_thread_stack_size), + use_ssl(params._use_ssl), + use_ipv6(params._use_ipv6), + use_dual_stack(params._use_dual_stack), + debug(params._debug), + pedantic(params._pedantic), + https_mem_key(params._https_mem_key), + https_mem_cert(params._https_mem_cert), + https_mem_trust(params._https_mem_trust), + https_priorities(params._https_priorities), + cred_type(params._cred_type), + digest_auth_random(params._digest_auth_random), + nonce_nc_size(params._nonce_nc_size), + running(false), + default_policy(params._default_policy), + basic_auth_enabled(params._basic_auth_enabled), + digest_auth_enabled(params._digest_auth_enabled), + regex_checking(params._regex_checking), + ban_system_enabled(params._ban_system_enabled), + post_process_enabled(params._post_process_enabled), + deferred_enabled(params._deferred_enabled), + single_resource(params._single_resource), + tcp_nodelay(params._tcp_nodelay), + not_found_resource(params._not_found_resource), + method_not_allowed_resource(params._method_not_allowed_resource), + internal_error_resource(params._internal_error_resource) { + ignore_sigpipe(); + pthread_mutex_init(&mutexwait, NULL); + pthread_cond_init(&mutexcond, NULL); +} + +webserver::~webserver() { + stop(); + pthread_mutex_destroy(&mutexwait); + pthread_cond_destroy(&mutexcond); +} + +void webserver::sweet_kill() { stop(); } + +void webserver::request_completed(void *cls, struct MHD_Connection *connection, void **con_cls, + enum MHD_RequestTerminationCode toe) { + details::modded_request *mr = static_cast(*con_cls); + if (mr == 0x0) + return; + + delete mr; + mr = 0x0; +} + +bool webserver::register_resource(const std::string &resource, http_resource *hrm, bool family) { + if (single_resource && ((resource != "" && resource != "/") || !family)) { + throw std::invalid_argument("The resource should be '' or '/' and be marked as family when " + "using a single_resource server"); + } + + details::http_endpoint idx(resource, family, true, regex_checking); + + pair::iterator, bool> result = + registered_resources.insert( + map::value_type(idx, hrm)); + + if (result.second) { + registered_resources_str.insert( + pair(idx.get_url_complete(), result.first->second)); + } + + return result.second; +} + +bool webserver::start(bool blocking) { + + struct { + MHD_OptionItem operator()(enum MHD_OPTION opt, intptr_t val, void *ptr = 0) { + MHD_OptionItem x = {opt, val, ptr}; + return x; + } + } gen; + vector iov; + + iov.push_back(gen(MHD_OPTION_NOTIFY_COMPLETED, (intptr_t)&request_completed, NULL)); + iov.push_back(gen(MHD_OPTION_URI_LOG_CALLBACK, (intptr_t)&uri_log, this)); + iov.push_back(gen(MHD_OPTION_EXTERNAL_LOGGER, (intptr_t)&error_log, this)); + iov.push_back(gen(MHD_OPTION_UNESCAPE_CALLBACK, (intptr_t)&unescaper_func, this)); + iov.push_back(gen(MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout)); + if (bind_socket != 0) + iov.push_back(gen(MHD_OPTION_LISTEN_SOCKET, bind_socket)); + if (start_method == http_utils::THREAD_PER_CONNECTION && + (max_threads != 0 || max_thread_stack_size != 0)) { + throw std::invalid_argument( + "Cannot specify maximum number of threads when using a thread per connection"); + } + + if (max_threads != 0) + iov.push_back(gen(MHD_OPTION_THREAD_POOL_SIZE, max_threads)); + if (max_connections != 0) + iov.push_back(gen(MHD_OPTION_CONNECTION_LIMIT, max_connections)); + if (memory_limit != 0) + iov.push_back(gen(MHD_OPTION_CONNECTION_MEMORY_LIMIT, memory_limit)); + if (per_IP_connection_limit != 0) + iov.push_back(gen(MHD_OPTION_PER_IP_CONNECTION_LIMIT, per_IP_connection_limit)); + if (max_thread_stack_size != 0) + iov.push_back(gen(MHD_OPTION_THREAD_STACK_SIZE, max_thread_stack_size)); + if (nonce_nc_size != 0) + iov.push_back(gen(MHD_OPTION_NONCE_NC_SIZE, nonce_nc_size)); + if (use_ssl) + iov.push_back(gen(MHD_OPTION_HTTPS_MEM_KEY, 0, (void *)https_mem_key.c_str())); + if (use_ssl) + iov.push_back(gen(MHD_OPTION_HTTPS_MEM_CERT, 0, (void *)https_mem_cert.c_str())); + if (https_mem_trust != "" && use_ssl) + iov.push_back(gen(MHD_OPTION_HTTPS_MEM_TRUST, 0, (void *)https_mem_trust.c_str())); + if (https_priorities != "" && use_ssl) + iov.push_back(gen(MHD_OPTION_HTTPS_PRIORITIES, 0, (void *)https_priorities.c_str())); + if (digest_auth_random != "") + iov.push_back(gen(MHD_OPTION_DIGEST_AUTH_RANDOM, digest_auth_random.size(), + (char *)digest_auth_random.c_str())); +#ifdef HAVE_GNUTLS + if (cred_type != http_utils::NONE) + iov.push_back(gen(MHD_OPTION_HTTPS_CRED_TYPE, cred_type)); +#endif // HAVE_GNUTLS + + iov.push_back(gen(MHD_OPTION_END, 0, NULL)); + + int start_conf = start_method; + if (use_ssl) + start_conf |= MHD_USE_SSL; + if (use_ipv6) + start_conf |= MHD_USE_IPv6; + if (use_dual_stack) + start_conf |= MHD_USE_DUAL_STACK; + if (debug) + start_conf |= MHD_USE_DEBUG; + if (pedantic) + start_conf |= MHD_USE_PEDANTIC_CHECKS; + if (deferred_enabled) + start_conf |= MHD_USE_SUSPEND_RESUME; + +#ifdef USE_FASTOPEN + start_conf |= MHD_USE_TCP_FASTOPEN; +#endif + + daemon = NULL; + if (bind_address == 0x0) { + daemon = MHD_start_daemon(start_conf, port, &policy_callback, this, &answer_to_connection, this, + MHD_OPTION_ARRAY, &iov[0], MHD_OPTION_END); + } else { + daemon = MHD_start_daemon(start_conf, 1, &policy_callback, this, &answer_to_connection, this, + MHD_OPTION_ARRAY, &iov[0], MHD_OPTION_SOCK_ADDR, bind_address, + MHD_OPTION_END); + } + + if (daemon == NULL) { + throw std::invalid_argument("Unable to connect daemon to port: " + std::to_string(port)); + } + + bool value_onclose = false; + + running = true; + + if (blocking) { + pthread_mutex_lock(&mutexwait); + while (blocking && running) + pthread_cond_wait(&mutexcond, &mutexwait); + pthread_mutex_unlock(&mutexwait); + value_onclose = true; + } + return value_onclose; +} + +bool webserver::is_running() { return running; } + +bool webserver::stop() { + if (!running) + return false; + + pthread_mutex_lock(&mutexwait); + running = false; + pthread_cond_signal(&mutexcond); + pthread_mutex_unlock(&mutexwait); + + MHD_stop_daemon(daemon); + + shutdown(bind_socket, 2); + + return true; +} + +void webserver::unregister_resource(const string &resource) { + // family does not matter - it just checks the url_normalized anyhow + details::http_endpoint he(resource, false, true, regex_checking); + registered_resources.erase(he); + registered_resources.erase(he.get_url_complete()); + registered_resources_str.erase(he.get_url_complete()); +} + +void webserver::ban_ip(const string &ip) { + ip_representation t_ip(ip); + set::iterator it = bans.find(t_ip); + if (it != bans.end() && (t_ip.weight() < (*it).weight())) { + bans.erase(it); + bans.insert(t_ip); + } else + bans.insert(t_ip); +} + +void webserver::allow_ip(const string &ip) { + ip_representation t_ip(ip); + set::iterator it = allowances.find(t_ip); + if (it != allowances.end() && (t_ip.weight() < (*it).weight())) { + allowances.erase(it); + allowances.insert(t_ip); + } else + allowances.insert(t_ip); +} + +void webserver::unban_ip(const string &ip) { bans.erase(ip); } + +void webserver::disallow_ip(const string &ip) { allowances.erase(ip); } + +MHD_Result policy_callback(void *cls, const struct sockaddr *addr, socklen_t addrlen) { + if (!(static_cast(cls))->ban_system_enabled) + return MHD_YES; + + if ((((static_cast(cls))->default_policy == http_utils::ACCEPT) && + ((static_cast(cls))->bans.count(addr)) && + (!(static_cast(cls))->allowances.count(addr))) || + (((static_cast(cls))->default_policy == http_utils::REJECT) && + ((!(static_cast(cls))->allowances.count(addr)) || + ((static_cast(cls))->bans.count(addr))))) { + return MHD_NO; + } + + return MHD_YES; +} + +void *uri_log(void *cls, const char *uri) { + struct details::modded_request *mr = new details::modded_request(); + mr->complete_uri = new string(uri); + mr->second = false; + return ((void *)mr); +} + +void error_log(void *cls, const char *fmt, va_list ap) { + webserver *dws = static_cast(cls); + if (dws->log_error != 0x0) + dws->log_error(fmt); +} + +void access_log(webserver *dws, string uri) { + if (dws->log_access != 0x0) + dws->log_access(uri); +} + +size_t unescaper_func(void *cls, struct MHD_Connection *c, char *s) { + // THIS IS USED TO AVOID AN UNESCAPING OF URL BEFORE THE ANSWER. + // IT IS DUE TO A BOGUS ON libmicrohttpd (V0.99) THAT PRODUCING A + // STRING CONTAINING '\0' AFTER AN UNESCAPING, IS UNABLE TO PARSE + // ARGS WITH get_connection_values FUNC OR lookup FUNC. + return std::string(s).size(); +} + +MHD_Result webserver::post_iterator(void *cls, enum MHD_ValueKind kind, const char *key, + const char *filename, const char *content_type, + const char *transfer_encoding, const char *data, uint64_t off, + size_t size) { + struct details::modded_request *mr = (struct details::modded_request *)cls; + mr->dhr->set_arg(key, mr->dhr->get_arg(key) + std::string(data, size)); + return MHD_YES; +} + +void webserver::upgrade_handler(void *cls, struct MHD_Connection *connection, void **con_cls, + int upgrade_socket) {} + +const std::shared_ptr webserver::not_found_page(details::modded_request *mr) const { + if (not_found_resource != 0x0) { + return not_found_resource(*mr->dhr); + } else { + return std::shared_ptr( + new string_response(NOT_FOUND_ERROR, http_utils::http_not_found)); + } +} + +const std::shared_ptr +webserver::method_not_allowed_page(details::modded_request *mr) const { + if (method_not_allowed_resource != 0x0) { + return method_not_allowed_resource(*mr->dhr); + } else { + return std::shared_ptr( + new string_response(METHOD_ERROR, http_utils::http_method_not_allowed)); + } +} + +const std::shared_ptr webserver::internal_error_page(details::modded_request *mr, + bool force_our) const { + if (internal_error_resource != 0x0 && !force_our) { + return internal_error_resource(*mr->dhr); + } else { + return std::shared_ptr( + new string_response(GENERIC_ERROR, http_utils::http_internal_server_error, "text/plain")); + } +} + +MHD_Result webserver::requests_answer_first_step(MHD_Connection *connection, + struct details::modded_request *mr) { + mr->second = true; + mr->dhr = new http_request(connection, unescaper); + + if (!mr->has_body) { + return MHD_YES; + } + + mr->dhr->set_content_size_limit(content_size_limit); + const char *encoding = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, + http_utils::http_header_content_type.c_str()); + + if (post_process_enabled && + (0x0 != encoding && + ((0 == strncasecmp(http_utils::http_post_encoding_form_urlencoded.c_str(), encoding, + http_utils::http_post_encoding_form_urlencoded.size())) || + (0 == strncasecmp(http_utils::http_post_encoding_multipart_formdata.c_str(), encoding, + http_utils::http_post_encoding_multipart_formdata.size()))))) { + const size_t post_memory_limit(32 * 1024); // Same as #MHD_POOL_SIZE_DEFAULT + mr->pp = MHD_create_post_processor(connection, post_memory_limit, &post_iterator, mr); + } else { + mr->pp = NULL; + } + return MHD_YES; +} + +MHD_Result webserver::requests_answer_second_step(MHD_Connection *connection, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, + struct details::modded_request *mr) { + if (0 == *upload_data_size) + return complete_request(connection, mr, version, method); + + if (mr->has_body) { + +#ifdef DEBUG + cout << "Writing content: " << upload_data << endl; +#endif // DEBUG + mr->dhr->grow_content(upload_data, *upload_data_size); + + if (mr->pp != NULL) + MHD_post_process(mr->pp, upload_data, *upload_data_size); + } + + *upload_data_size = 0; + return MHD_YES; +} + +MHD_Result webserver::finalize_answer(MHD_Connection *connection, + struct details::modded_request *mr, const char *method) { + int to_ret = MHD_NO; + + map::iterator fe; + + http_resource *hrm; + + bool found = false; + struct MHD_Response *raw_response; + if (!single_resource) { + const char *st_url = mr->standardized_url->c_str(); + fe = registered_resources_str.find(st_url); + if (fe == registered_resources_str.end()) { + if (regex_checking) { + + map::iterator found_endpoint; + + details::http_endpoint endpoint(st_url, false, false, false); + + map::iterator it; + + size_t len = 0; + size_t tot_len = 0; + for (it = registered_resources.begin(); it != registered_resources.end(); ++it) { + size_t endpoint_pieces_len = (*it).first.get_url_pieces().size(); + size_t endpoint_tot_len = (*it).first.get_url_complete().size(); + if (!found || endpoint_pieces_len > len || + (endpoint_pieces_len == len && endpoint_tot_len > tot_len)) { + if ((*it).first.match(endpoint)) { + found = true; + len = endpoint_pieces_len; + tot_len = endpoint_tot_len; + found_endpoint = it; + } + } + } + if (found) { + vector url_pars = found_endpoint->first.get_url_pars(); + + vector url_pieces = endpoint.get_url_pieces(); + vector chunks = found_endpoint->first.get_chunk_positions(); + for (unsigned int i = 0; i < url_pars.size(); i++) { + mr->dhr->set_arg(url_pars[i], url_pieces[chunks[i]]); + } + + hrm = found_endpoint->second; + } + } + } else { + hrm = fe->second; + found = true; + } + } else { + hrm = registered_resources.begin()->second; + found = true; + } + + if (found) { + try { + if (hrm->is_allowed(method)) { + mr->dhrs = ((hrm)->*(mr->callback))(*mr->dhr); // copy in memory (move in case) + if (mr->dhrs->get_response_code() == -1) { + mr->dhrs = internal_error_page(mr); + } + } else { + mr->dhrs = method_not_allowed_page(mr); + } + } catch (const std::exception &e) { + mr->dhrs = internal_error_page(mr); + } catch (...) { + mr->dhrs = internal_error_page(mr); + } + } else { + mr->dhrs = not_found_page(mr); + } + + try { + try { + raw_response = mr->dhrs->get_raw_response(); + } catch (const std::invalid_argument &iae) { + mr->dhrs = not_found_page(mr); + raw_response = mr->dhrs->get_raw_response(); + } catch (const std::exception &e) { + mr->dhrs = internal_error_page(mr); + raw_response = mr->dhrs->get_raw_response(); + } catch (...) { + mr->dhrs = internal_error_page(mr); + raw_response = mr->dhrs->get_raw_response(); + } + } catch (...) // catches errors in internal error page + { + mr->dhrs = internal_error_page(mr, true); + raw_response = mr->dhrs->get_raw_response(); + } + mr->dhrs->decorate_response(raw_response); + to_ret = mr->dhrs->enqueue_response(connection, raw_response); + MHD_destroy_response(raw_response); + return (MHD_Result)to_ret; +} + +MHD_Result webserver::complete_request(MHD_Connection *connection, + struct details::modded_request *mr, const char *version, + const char *method) { + mr->ws = this; + + mr->dhr->set_path(mr->standardized_url->c_str()); + mr->dhr->set_method(method); + mr->dhr->set_version(version); + + return finalize_answer(connection, mr, method); +} + +MHD_Result webserver::answer_to_connection(void *cls, MHD_Connection *connection, const char *url, + const char *method, const char *version, + const char *upload_data, size_t *upload_data_size, + void **con_cls) { + struct details::modded_request *mr = static_cast(*con_cls); + + if (mr->second != false) { + return static_cast(cls)->requests_answer_second_step( + connection, method, version, upload_data, upload_data_size, mr); + } + + const MHD_ConnectionInfo *conninfo = + MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CONNECTION_FD); + + if (static_cast(cls)->tcp_nodelay) { + int yes = 1; + setsockopt(conninfo->connect_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&yes, sizeof(int)); + } + + std::string t_url = url; + + base_unescaper(t_url, static_cast(cls)->unescaper); + mr->standardized_url = new string(http_utils::standardize_url(t_url)); + + mr->has_body = false; + + access_log(static_cast(cls), *(mr->complete_uri) + " METHOD: " + method); + + if (0 == strcasecmp(method, http_utils::http_method_get.c_str())) { + mr->callback = &http_resource::render_GET; + } else if (0 == strcmp(method, http_utils::http_method_post.c_str())) { + mr->callback = &http_resource::render_POST; + mr->has_body = true; + } else if (0 == strcasecmp(method, http_utils::http_method_put.c_str())) { + mr->callback = &http_resource::render_PUT; + mr->has_body = true; + } else if (0 == strcasecmp(method, http_utils::http_method_delete.c_str())) { + mr->callback = &http_resource::render_DELETE; + mr->has_body = true; + } else if (0 == strcasecmp(method, http_utils::http_method_patch.c_str())) { + mr->callback = &http_resource::render_PATCH; + mr->has_body = true; + } else if (0 == strcasecmp(method, http_utils::http_method_head.c_str())) { + mr->callback = &http_resource::render_HEAD; + } else if (0 == strcasecmp(method, http_utils::http_method_connect.c_str())) { + mr->callback = &http_resource::render_CONNECT; + } else if (0 == strcasecmp(method, http_utils::http_method_trace.c_str())) { + mr->callback = &http_resource::render_TRACE; + } else if (0 == strcasecmp(method, http_utils::http_method_options.c_str())) { + mr->callback = &http_resource::render_OPTIONS; + } + + return static_cast(cls)->requests_answer_first_step(connection, mr); +} + +}; // namespace httpserver diff --git a/3rd_party/libhttpserver-0.18.2/test/Makefile.am b/3rd_party/libhttpserver-0.18.2/test/Makefile.am new file mode 100644 index 00000000..e6c91b02 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/Makefile.am @@ -0,0 +1,50 @@ +# +# This file is part of libhttpserver +# Copyright (C) 2011-2019 Sebastiano Merlino +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +LDADD = $(top_builddir)/src/libhttpserver.la +AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ +METASOURCES = AUTO +check_PROGRAMS = basic http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred + +MOSTLYCLEANFILES = *.gcda *.gcno *.gcov + +basic_SOURCES = integ/basic.cpp +threaded_SOURCES = integ/threaded.cpp +ban_system_SOURCES = integ/ban_system.cpp +ws_start_stop_SOURCES = integ/ws_start_stop.cpp +authentication_SOURCES = integ/authentication.cpp +deferred_SOURCES = integ/deferred.cpp +http_utils_SOURCES = unit/http_utils_test.cpp +string_utilities_SOURCES = unit/string_utilities_test.cpp +http_endpoint_SOURCES = unit/http_endpoint_test.cpp +nodelay_SOURCES = integ/nodelay.cpp + +noinst_HEADERS = littletest.hpp +AM_CXXFLAGS += -lcurl -Wall -fPIC + +if COND_GCOV +AM_CFLAGS += -O0 --coverage --no-inline +AM_CXXFLAGS += -O0 --coverage --no-inline +AM_LDFLAGS += -O0 --coverage -lgcov --no-inline +endif + +TESTS = $(check_PROGRAMS) + +@VALGRIND_CHECK_RULES@ +VALGRIND_SUPPRESSIONS_FILES = libhttpserver.supp +EXTRA_DIST = libhttpserver.supp diff --git a/3rd_party/libhttpserver-0.18.2/test/cert.pem b/3rd_party/libhttpserver-0.18.2/test/cert.pem new file mode 100644 index 00000000..2c766dff --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICpjCCAZCgAwIBAgIESEPtjjALBgkqhkiG9w0BAQUwADAeFw0wODA2MDIxMjU0 +MzhaFw0wOTA2MDIxMjU0NDZaMAAwggEfMAsGCSqGSIb3DQEBAQOCAQ4AMIIBCQKC +AQC03TyUvK5HmUAirRp067taIEO4bibh5nqolUoUdo/LeblMQV+qnrv/RNAMTx5X +fNLZ45/kbM9geF8qY0vsPyQvP4jumzK0LOJYuIwmHaUm9vbXnYieILiwCuTgjaud +3VkZDoQ9fteIo+6we9UTpVqZpxpbLulBMh/VsvX0cPJ1VFC7rT59o9hAUlFf9jX/ +GmKdYI79MtgVx0OPBjmmSD6kicBBfmfgkO7bIGwlRtsIyMznxbHu6VuoX/eVxrTv +rmCwgEXLWRZ6ru8MQl5YfqeGXXRVwMeXU961KefbuvmEPccgCxm8FZ1C1cnDHFXh +siSgAzMBjC/b6KVhNQ4KnUdZAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0O +BBYEFJcUvpjvE5fF/yzUshkWDpdYiQh/MAsGCSqGSIb3DQEBBQOCAQEARP7eKSB2 +RNd6XjEjK0SrxtoTnxS3nw9sfcS7/qD1+XHdObtDFqGNSjGYFB3Gpx8fpQhCXdoN +8QUs3/5ZVa5yjZMQewWBgz8kNbnbH40F2y81MHITxxCe1Y+qqHWwVaYLsiOTqj2/ +0S3QjEJ9tvklmg7JX09HC4m5QRYfWBeQLD1u8ZjA1Sf1xJriomFVyRLI2VPO2bNe +JDMXWuP+8kMC7gEvUnJ7A92Y2yrhu3QI3bjPk8uSpHea19Q77tul1UVBJ5g+zpH3 +OsF5p0MyaVf09GTzcLds5nE/osTdXGUyHJapWReVmPm3Zn6gqYlnzD99z+DPIgIV +RhZvQx74NQnS6g== +-----END CERTIFICATE----- diff --git a/3rd_party/libhttpserver-0.18.2/test/integ/authentication.cpp b/3rd_party/libhttpserver-0.18.2/test/integ/authentication.cpp new file mode 100644 index 00000000..da89f0a2 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/integ/authentication.cpp @@ -0,0 +1,231 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if defined(_WIN32) && ! defined(__CYGWIN__) +#define _WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#include +#include +#else +#include +#include +#include +#endif + +#include + +#include "httpserver.hpp" +#include "littletest.hpp" + +#define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" + +using namespace std; +using namespace httpserver; + +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) +{ + s->append((char*) ptr, size*nmemb); + return size*nmemb; +} + +class user_pass_resource : public httpserver::http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + if (req.get_user() != "myuser" || req.get_pass() != "mypass") + { + return shared_ptr(new basic_auth_fail_response("FAIL", "examplerealm")); + } + return shared_ptr(new string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); + } +}; + +class digest_resource : public httpserver::http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + if (req.get_digested_user() == "") { + return shared_ptr(new digest_auth_fail_response("FAIL", "examplerealm", MY_OPAQUE, true)); + } + else + { + bool reload_nonce = false; + if(!req.check_digest_auth("examplerealm", "mypass", 300, reload_nonce)) + { + return shared_ptr(new digest_auth_fail_response("FAIL", "examplerealm", MY_OPAQUE, reload_nonce)); + } + } + return shared_ptr(new string_response("SUCCESS", 200, "text/plain")); + } +}; + +LT_BEGIN_SUITE(authentication_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(authentication_suite) + +LT_BEGIN_AUTO_TEST(authentication_suite, base_auth) + webserver ws = create_webserver(8080); + + user_pass_resource user_pass; + ws.register_resource("base", &user_pass); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_USERNAME, "myuser"); + curl_easy_setopt(curl, CURLOPT_PASSWORD, "mypass"); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "myuser mypass"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(base_auth) + +LT_BEGIN_AUTO_TEST(authentication_suite, base_auth_fail) + webserver ws = create_webserver(8080); + + user_pass_resource user_pass; + ws.register_resource("base", &user_pass); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_USERNAME, "myuser"); + curl_easy_setopt(curl, CURLOPT_PASSWORD, "wrongpass"); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "FAIL"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(base_auth_fail) + +// do not run the digest auth tests on windows as curl +// appears to have problems with it. +// Will fix this separately +#ifndef _WINDOWS + +LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth) + webserver ws = create_webserver(8080) + .digest_auth_random("myrandom") + .nonce_nc_size(300); + + digest_resource digest; + ws.register_resource("base", &digest); + ws.start(false); + +#if defined(_WINDOWS) + curl_global_init(CURL_GLOBAL_WIN32 ); +#else + curl_global_init(CURL_GLOBAL_ALL); +#endif + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); +#if defined(_WINDOWS) + curl_easy_setopt(curl, CURLOPT_USERPWD, "examplerealm/myuser:mypass"); +#else + curl_easy_setopt(curl, CURLOPT_USERPWD, "myuser:mypass"); +#endif + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 150L); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "SUCCESS"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(digest_auth) + +LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_wrong_pass) + webserver ws = create_webserver(8080) + .digest_auth_random("myrandom") + .nonce_nc_size(300); + + digest_resource digest; + ws.register_resource("base", &digest); + ws.start(false); + +#if defined(_WINDOWS) + curl_global_init(CURL_GLOBAL_WIN32 ); +#else + curl_global_init(CURL_GLOBAL_ALL); +#endif + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); +#if defined(_WINDOWS) + curl_easy_setopt(curl, CURLOPT_USERPWD, "examplerealm/myuser:wrongpass"); +#else + curl_easy_setopt(curl, CURLOPT_USERPWD, "myuser:wrongpass"); +#endif + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 150L); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "FAIL"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(digest_auth_wrong_pass) + +#endif + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() diff --git a/3rd_party/libhttpserver-0.18.2/test/integ/ban_system.cpp b/3rd_party/libhttpserver-0.18.2/test/integ/ban_system.cpp new file mode 100644 index 00000000..07f3e8d8 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/integ/ban_system.cpp @@ -0,0 +1,166 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include +#include + +#include "httpserver.hpp" +#include "httpserver/http_utils.hpp" +#include "littletest.hpp" + +using namespace httpserver; +using namespace std; +using namespace http; + +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) +{ + s->append((char*) ptr, size*nmemb); + return size*nmemb; +} + +class ok_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } +}; + +LT_BEGIN_SUITE(ban_system_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(ban_system_suite) + +LT_BEGIN_AUTO_TEST(ban_system_suite, accept_default_ban_blocks) + webserver ws = create_webserver(8080).default_policy(http_utils::ACCEPT); + ws.start(false); + + ok_resource resource; + ws.register_resource("base", &resource); + + curl_global_init(CURL_GLOBAL_ALL); + + { + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + { + ws.ban_ip("127.0.0.1"); + + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + res = curl_easy_perform(curl); + LT_ASSERT_NEQ(res, 0); + curl_easy_cleanup(curl); + } + + { + ws.unban_ip("127.0.0.1"); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + curl_global_cleanup(); + ws.stop(); +LT_END_AUTO_TEST(accept_default_ban_blocks) + +LT_BEGIN_AUTO_TEST(ban_system_suite, reject_default_allow_passes) + webserver ws = create_webserver(8080).default_policy(http_utils::REJECT); + ws.start(false); + + ok_resource resource; + ws.register_resource("base", &resource); + + curl_global_init(CURL_GLOBAL_ALL); + + { + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + res = curl_easy_perform(curl); + LT_ASSERT_NEQ(res, 0); + curl_easy_cleanup(curl); + } + + { + ws.allow_ip("127.0.0.1"); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + { + ws.disallow_ip("127.0.0.1"); + + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + res = curl_easy_perform(curl); + LT_ASSERT_NEQ(res, 0); + curl_easy_cleanup(curl); + } + + curl_global_cleanup(); + ws.stop(); +LT_END_AUTO_TEST(reject_default_allow_passes) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() diff --git a/3rd_party/libhttpserver-0.18.2/test/integ/basic.cpp b/3rd_party/libhttpserver-0.18.2/test/integ/basic.cpp new file mode 100644 index 00000000..fdb503de --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/integ/basic.cpp @@ -0,0 +1,1016 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include +#include +#include + +#include "httpserver.hpp" +#include "httpserver/string_utilities.hpp" +#include "littletest.hpp" + +using namespace httpserver; +using namespace std; + +std::string lorem_ipsum(" , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat."); + +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) +{ + s->append((char*) ptr, size*nmemb); + return size*nmemb; +} + +size_t headerfunc(void *ptr, size_t size, size_t nmemb, map* ss) +{ + string s_ptr((char*)ptr, size*nmemb); + size_t pos = s_ptr.find(":"); + if(pos != string::npos) + (*ss)[s_ptr.substr(0, pos)] = + s_ptr.substr(pos + 2, s_ptr.size() - pos - 4); + return size*nmemb; +} + +class simple_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + const shared_ptr render_POST(const http_request& req) + { + return shared_ptr(new string_response(req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain")); + } +}; + +class args_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response(req.get_arg("arg") + req.get_arg("arg2"), 200, "text/plain")); + } +}; + +class long_content_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response(lorem_ipsum, 200, "text/plain")); + } +}; + +class header_set_test_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + shared_ptr hrb(new string_response("OK", 200, "text/plain")); + hrb->with_header("KEY", "VALUE"); + return hrb; + } +}; + +class cookie_set_test_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + shared_ptr hrb(new string_response("OK", 200, "text/plain")); + hrb->with_cookie("MyCookie", "CookieValue"); + return hrb; + } +}; + +class cookie_reading_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response(req.get_cookie("name"), 200, "text/plain")); + } +}; + +class header_reading_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response(req.get_header("MyHeader"), 200, "text/plain")); + } +}; + +class full_args_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response(req.get_args().at("arg"), 200, "text/plain")); + } +}; + +class querystring_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response(req.get_querystring(), 200, "text/plain")); + } +}; + +class path_pieces_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + std::stringstream ss; + for (unsigned int i = 0; i < req.get_path_pieces().size(); i++) + { + ss << req.get_path_piece(i) << ","; + } + return shared_ptr(new string_response(ss.str(), 200, "text/plain")); + } +}; + +class complete_test_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + const shared_ptr render_POST(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + const shared_ptr render_PUT(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + const shared_ptr render_DELETE(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + const shared_ptr render_CONNECT(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + const shared_ptr render_PATCH(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } +}; + +class only_render_resource : public http_resource +{ + public: + const shared_ptr render(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } +}; + +class ok_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } +}; + +class nok_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response("NOK", 200, "text/plain")); + } +}; + +class no_response_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new http_response()); + } +}; + +class file_response_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new file_response("test_content", 200, "text/plain")); + } +}; + +class exception_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + throw std::domain_error("invalid"); + } +}; + +class error_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + throw "invalid"; + } +}; + +class print_request_resource : public http_resource +{ + public: + print_request_resource(std::stringstream* ss) + { + this->ss = ss; + } + + const shared_ptr render_GET(const http_request& req) + { + (*ss) << req; + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + + private: + std::stringstream* ss; +}; + +class print_response_resource : public http_resource +{ + public: + print_response_resource(std::stringstream* ss) + { + this->ss = ss; + } + + const shared_ptr render_GET(const http_request& req) + { + shared_ptr hresp(new string_response("OK", 200, "text/plain")); + + hresp->with_header("MyResponseHeader", "MyResponseHeaderValue"); + hresp->with_footer("MyResponseFooter", "MyResponseFooterValue"); + hresp->with_cookie("MyResponseCookie", "MyResponseCookieValue"); + + (*ss) << *hresp; + + return hresp; + } + + private: + std::stringstream* ss; +}; + +LT_BEGIN_SUITE(basic_suite) + + webserver* ws; + + void set_up() + { + ws = new webserver(create_webserver(8080)); + ws->start(false); + } + + void tear_down() + { + ws->stop(); + delete ws; + } +LT_END_SUITE(basic_suite) + +LT_BEGIN_AUTO_TEST(basic_suite, server_runs) + LT_CHECK_EQ(ws->is_running(), true); +LT_END_AUTO_TEST(server_runs) + +LT_BEGIN_AUTO_TEST(basic_suite, two_endpoints) + ok_resource ok; + ws->register_resource("OK", &ok); + nok_resource nok; + ws->register_resource("NOK", &nok); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + { + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/OK"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + std::string t; + { + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/NOK"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &t); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(t, "NOK"); + curl_easy_cleanup(curl); + } +LT_END_AUTO_TEST(two_endpoints) + +LT_BEGIN_AUTO_TEST(basic_suite, read_body) + simple_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(read_body) + +LT_BEGIN_AUTO_TEST(basic_suite, read_long_body) + long_content_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s.size(), lorem_ipsum.size()); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(read_long_body) + +LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_header) + header_set_test_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + map ss; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerfunc); + curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &ss); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + LT_CHECK_EQ(ss["KEY"], "VALUE"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(resource_setting_header) + +LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_cookie) + cookie_set_test_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); + curl_easy_setopt(curl, CURLOPT_COOKIEJAR, ""); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + + struct curl_slist *cookies; + curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies); + + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + std::string read_cookie = ""; + + read_cookie = cookies->data; + curl_slist_free_all(cookies); + + std::vector cookie_parts = string_utilities::string_split(read_cookie, '\t', false); + LT_CHECK_EQ(cookie_parts[5], "MyCookie"); + LT_CHECK_EQ(cookie_parts[6], "CookieValue"); + + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(resource_setting_cookie) + +LT_BEGIN_AUTO_TEST(basic_suite, request_with_header) + header_reading_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + + struct curl_slist *list = NULL; + list = curl_slist_append(list, "MyHeader: MyValue"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "MyValue"); + curl_slist_free_all(list); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(request_with_header) + +LT_BEGIN_AUTO_TEST(basic_suite, request_with_cookie) + cookie_reading_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_setopt(curl, CURLOPT_COOKIE, "name=myname; present=yes;"); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "myname"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(request_with_cookie) + +LT_BEGIN_AUTO_TEST(basic_suite, complete) + complete_test_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + { + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + CURLcode res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); + } + + { + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + CURLcode res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); + } + + { + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + CURLcode res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); + } + + { + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); + CURLcode res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); + } +/* + { + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "CONNECT"); + CURLcode res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); + } +*/ + + { + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0); + CURLcode res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); + } +LT_END_AUTO_TEST(complete) + +LT_BEGIN_AUTO_TEST(basic_suite, only_render) + only_render_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL* curl; + CURLcode res; + + s = ""; + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + s = ""; + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + s = ""; + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + +/* + s = ""; + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "CONNECT"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); +*/ + + s = ""; + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "NOT_EXISTENT"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Method not Allowed"); + curl_easy_cleanup(curl); + + s = ""; + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(only_render) + +LT_BEGIN_AUTO_TEST(basic_suite, postprocessor) + simple_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "arg1=lib&arg2=httpserver"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "libhttpserver"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(postprocessor) + +LT_BEGIN_AUTO_TEST(basic_suite, empty_arg) + simple_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "arg1"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(empty_arg) + +LT_BEGIN_AUTO_TEST(basic_suite, no_response) + no_response_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + CURLcode res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 500); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(no_response) + +LT_BEGIN_AUTO_TEST(basic_suite, regex_matching) + simple_resource resource; + ws->register_resource("regex/matching/number/[0-9]+", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/regex/matching/number/10"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(regex_matching) + +LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg) + args_resource resource; + ws->register_resource("this/captures/{arg}/passed/in/input", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/whatever/passed/in/input"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "whatever"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(regex_matching_arg) + +LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg_custom) + args_resource resource; + ws->register_resource("this/captures/numeric/{arg|([0-9]+)}/passed/in/input", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + { + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/numeric/11/passed/in/input"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "11"); + curl_easy_cleanup(curl); + } + + { + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/numeric/text/passed/in/input"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Not Found"); + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 404); + curl_easy_cleanup(curl); + } +LT_END_AUTO_TEST(regex_matching_arg_custom) + +LT_BEGIN_AUTO_TEST(basic_suite, querystring_processing) + args_resource resource; + ws->register_resource("this/captures/args/passed/in/the/querystring", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/args/passed/in/the/querystring?arg=first&arg2=second"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "firstsecond"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(querystring_processing) + +LT_BEGIN_AUTO_TEST(basic_suite, full_arguments_processing) + full_args_resource resource; + ws->register_resource("this/captures/args/passed/in/the/querystring", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/args/passed/in/the/querystring?arg=argument"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "argument"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(full_arguments_processing) + +LT_BEGIN_AUTO_TEST(basic_suite, querystring_query_processing) + querystring_resource resource; + ws->register_resource("this/captures/args/passed/in/the/querystring", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/args/passed/in/the/querystring?arg1=value1&arg2=value2&arg3=value3"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "?arg1=value1&arg2=value2&arg3=value3"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(querystring_query_processing) + +LT_BEGIN_AUTO_TEST(basic_suite, register_unregister) + simple_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + { + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + ws->unregister_resource("base"); + { + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 404); + + LT_CHECK_EQ(s, "Not Found"); + + curl_easy_cleanup(curl); + } + + ws->register_resource("base", &resource); + { + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } +LT_END_AUTO_TEST(register_unregister) + +LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource) + file_response_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "test content of file\n"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(file_serving_resource) + +LT_BEGIN_AUTO_TEST(basic_suite, exception_forces_500) + exception_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Internal Error"); + + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 500); + + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(exception_forces_500) + +LT_BEGIN_AUTO_TEST(basic_suite, untyped_error_forces_500) + error_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Internal Error"); + + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 500); + + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(untyped_error_forces_500) + +LT_BEGIN_AUTO_TEST(basic_suite, request_is_printable) + std::stringstream ss; + print_request_resource resource(&ss); + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + + struct curl_slist *list = NULL; + list = curl_slist_append(NULL, "MyHeader: MyValue"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + + res = curl_easy_perform(curl); + curl_slist_free_all(list); + + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + + std::string actual = ss.str(); + LT_CHECK_EQ(actual.find("GET Request") != string::npos, true); + LT_CHECK_EQ(actual.find("Headers [") != string::npos, true); + LT_CHECK_EQ(actual.find("Host") != string::npos, true); + LT_CHECK_EQ(actual.find("Accept:\"*/*\"") != string::npos, true); + LT_CHECK_EQ(actual.find("MyHeader:\"MyValue\"") != string::npos, true); + LT_CHECK_EQ(actual.find("Version [ HTTP/1.1 ]") != string::npos, true); + + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(request_is_printable) + +LT_BEGIN_AUTO_TEST(basic_suite, response_is_printable) + std::stringstream ss; + print_response_resource resource(&ss); + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + + struct curl_slist *list = NULL; + list = curl_slist_append(NULL, "MyHeader: MyValue"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + + res = curl_easy_perform(curl); + curl_slist_free_all(list); + + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + + std::string actual = ss.str(); + LT_CHECK_EQ(actual.find("Response [response_code:200]") != string::npos, true); + LT_CHECK_EQ(actual.find("Headers [Content-Type:\"text/plain\" MyResponseHeader:\"MyResponseHeaderValue\" ]") != string::npos, true); + LT_CHECK_EQ(actual.find("Footers [MyResponseFooter:\"MyResponseFooterValue\" ]") != string::npos, true); + LT_CHECK_EQ(actual.find("Cookies [MyResponseCookie:\"MyResponseCookieValue\" ]") != string::npos, true); + + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(response_is_printable) + +LT_BEGIN_AUTO_TEST(basic_suite, long_path_pieces) + path_pieces_resource resource; + ws->register_resource("/settings", &resource, true); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/settings/somestringthatisreallylong/with_really_a_lot_of_content/and_underscores_and_looooooooooooooooooong_stuff"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "settings,somestringthatisreallylong,with_really_a_lot_of_content,and_underscores_and_looooooooooooooooooong_stuff,"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(long_path_pieces) + +LT_BEGIN_AUTO_TEST(basic_suite, url_with_regex_like_pieces) + path_pieces_resource resource; + ws->register_resource("/settings", &resource, true); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/settings/{}"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "settings,{},"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(url_with_regex_like_pieces) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() diff --git a/3rd_party/libhttpserver-0.18.2/test/integ/deferred.cpp b/3rd_party/libhttpserver-0.18.2/test/integ/deferred.cpp new file mode 100644 index 00000000..571c36a5 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/integ/deferred.cpp @@ -0,0 +1,165 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if defined(_WIN32) && ! defined(__CYGWIN__) +#define _WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#include +#include +#else +#include +#include +#include +#endif + +#include +#include +#include + +#include "httpserver.hpp" +#include "littletest.hpp" + +using namespace std; +using namespace httpserver; + +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) +{ + s->append((char*) ptr, size*nmemb); + return size*nmemb; +} + +static int counter = 0; + +struct test_data +{ + int value; +}; + +ssize_t test_callback(std::shared_ptr closure_data, char* buf, size_t max) +{ + if (counter == 2) + { + return -1; + } + else + { + memset(buf, 0, max); + strcat(buf, "test"); + counter++; + return std::string(buf).size(); + } +} + +ssize_t test_callback_with_data(std::shared_ptr closure_data, char* buf, size_t max) +{ + if (counter == 2) + { + return -1; + } + else + { + memset(buf, 0, max); + strcat(buf, ("test" + std::to_string(closure_data->value)).c_str()); + + closure_data->value = 84; + + counter++; + return std::string(buf).size(); + } +} + +class deferred_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr>(new deferred_response(test_callback, nullptr, "cycle callback response")); + } +}; + +class deferred_resource_with_data : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + std::shared_ptr internal_info(new test_data); + internal_info->value = 42; + return shared_ptr>(new deferred_response(test_callback_with_data, internal_info, "cycle callback response")); + } +}; + +LT_BEGIN_SUITE(deferred_suite) + webserver* ws; + + void set_up() + { + ws = new webserver(create_webserver(8080)); + ws->start(false); + } + + void tear_down() + { + counter = 0; + + ws->stop(); + delete ws; + } +LT_END_SUITE(deferred_suite) + +LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response) + deferred_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "testtest"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(deferred_response) + +LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response_with_data) + deferred_resource_with_data resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "test42test84"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(deferred_response_with_data) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() diff --git a/3rd_party/libhttpserver-0.18.2/test/integ/nodelay.cpp b/3rd_party/libhttpserver-0.18.2/test/integ/nodelay.cpp new file mode 100644 index 00000000..0284a3f9 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/integ/nodelay.cpp @@ -0,0 +1,75 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include +#include + +#include "httpserver.hpp" +#include "littletest.hpp" + +using namespace httpserver; +using namespace std; + +class ok_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } +}; + +LT_BEGIN_SUITE(threaded_suite) + + webserver* ws; + + void set_up() + { + ws = new webserver(create_webserver(8080).tcp_nodelay()); + ws->start(false); + } + + void tear_down() + { + ws->stop(); + delete ws; + } +LT_END_SUITE(threaded_suite) + +LT_BEGIN_AUTO_TEST(threaded_suite, base) + ok_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL* curl; + CURLcode res; + + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(base) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() diff --git a/3rd_party/libhttpserver-0.18.2/test/integ/threaded.cpp b/3rd_party/libhttpserver-0.18.2/test/integ/threaded.cpp new file mode 100644 index 00000000..1b29d512 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/integ/threaded.cpp @@ -0,0 +1,75 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include +#include + +#include "httpserver.hpp" +#include "littletest.hpp" + +using namespace httpserver; +using namespace std; + +class ok_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } +}; + +LT_BEGIN_SUITE(threaded_suite) + + webserver* ws; + + void set_up() + { + ws = new webserver(create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT).max_threads(5)); + ws->start(false); + } + + void tear_down() + { + ws->stop(); + delete ws; + } +LT_END_SUITE(threaded_suite) + +LT_BEGIN_AUTO_TEST(threaded_suite, base) + ok_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL* curl; + CURLcode res; + + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(base) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() diff --git a/3rd_party/libhttpserver-0.18.2/test/integ/ws_start_stop.cpp b/3rd_party/libhttpserver-0.18.2/test/integ/ws_start_stop.cpp new file mode 100644 index 00000000..7aa185e8 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/integ/ws_start_stop.cpp @@ -0,0 +1,607 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if defined(_WIN32) && ! defined(__CYGWIN__) +#define _WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#include +#include +#else +#include +#include +#include +#endif + +#include +#include +#include + +#include "httpserver.hpp" +#include "littletest.hpp" + +using namespace std; +using namespace httpserver; + +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) +{ + s->append((char*) ptr, size*nmemb); + return size*nmemb; +} + +class ok_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } +}; + +const shared_ptr not_found_custom(const http_request& req) +{ + return shared_ptr(new string_response("Not found custom", 404, "text/plain")); +} + +const shared_ptr not_allowed_custom(const http_request& req) +{ + return shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); +} + +LT_BEGIN_SUITE(ws_start_stop_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(ws_start_stop_suite) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, start_stop) + { + webserver ws = create_webserver(8080); + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); + } + + { + webserver ws = create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT); + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); + } + + { + webserver ws = create_webserver(8080).start_method(http::http_utils::THREAD_PER_CONNECTION); + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); + } +LT_END_AUTO_TEST(start_stop) + +#if defined(IPV6_TESTS_ENABLED) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ipv6) + { + webserver ws = create_webserver(8080).use_ipv6(); + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); + } +LT_END_AUTO_TEST(ipv6) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, dual_stack) + { + webserver ws = create_webserver(8080).use_dual_stack(); + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); + } +LT_END_AUTO_TEST(dual_stack) + +#endif + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, sweet_kill) + webserver ws = create_webserver(8080); + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + { + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + ws.sweet_kill(); + + { + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 7); + curl_easy_cleanup(curl); + } +LT_END_AUTO_TEST(sweet_kill) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, disable_options) + webserver ws = create_webserver(8080) + .no_ssl() + .no_ipv6() + .no_debug() + .no_pedantic() + .no_basic_auth() + .no_digest_auth() + .no_deferred() + .no_regex_checking() + .no_ban_system() + .no_post_process(); + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(disable_options) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, enable_options) + webserver ws = create_webserver(8080) + .debug() + .pedantic() + .deferred() + .regex_checking() + .ban_system() + .post_process(); + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(enable_options) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) +#ifndef DARWIN + int fd = socket(AF_INET, SOCK_STREAM, 0); + + struct sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr("127.0.0.1"); + address.sin_port = htons(8181); + bind(fd, (struct sockaddr*) &address, sizeof(address)); + listen(fd, 10000); + + webserver ws = create_webserver(-1).bind_socket(fd); //whatever port here doesn't matter + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8181/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +#endif +LT_END_AUTO_TEST(custom_socket) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource) + webserver ws = create_webserver(8080).single_resource(); + ok_resource ok; + ws.register_resource("/", &ok, true); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/any/url/works"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(single_resource) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource_not_default_resource) + webserver ws = create_webserver(8080).single_resource(); + ok_resource ok; + LT_CHECK_THROW(ws.register_resource("/other", &ok, true)); + LT_CHECK_THROW(ws.register_resource("/", &ok, false)); + ws.start(false); + + ws.stop(); +LT_END_AUTO_TEST(single_resource_not_default_resource) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, thread_per_connection_fails_with_max_threads) + { + webserver ws = create_webserver(8080) + .start_method(http::http_utils::THREAD_PER_CONNECTION) + .max_threads(5); + LT_CHECK_THROW(ws.start(false)); + } +LT_END_AUTO_TEST(thread_per_connection_fails_with_max_threads) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, thread_per_connection_fails_with_max_threads_stack_size) + { + webserver ws = create_webserver(8080) + .start_method(http::http_utils::THREAD_PER_CONNECTION) + .max_thread_stack_size(4*1024*1024); + LT_CHECK_THROW(ws.start(false)); + } +LT_END_AUTO_TEST(thread_per_connection_fails_with_max_threads_stack_size) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, tuning_options) + webserver ws = create_webserver(8080) + .max_connections(10) + .max_threads(10) + .memory_limit(10000) + .per_IP_connection_limit(10) + .max_thread_stack_size(4*1024*1024) + .nonce_nc_size(10); + ; + + ok_resource ok; + ws.register_resource("base", &ok); + LT_CHECK_NOTHROW(ws.start(false)); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(tuning_options) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_base) + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem"); + + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + curl_global_cleanup(); + + ws.stop(); +LT_END_AUTO_TEST(ssl_base) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_protocol_priorities) + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem") + .https_priorities("NONE:+VERS-TLS1.0:+AES-128-CBC:+SHA1:+RSA:+COMP-NULL"); + + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(ssl_with_protocol_priorities) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_trust) + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem") + .https_mem_trust("test_root_ca.pem"); + + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(ssl_with_trust) + +void* start_ws_blocking(void* par) +{ + webserver* ws = (webserver*) par; + ok_resource ok; + ws->register_resource("base", &ok); + ws->start(true); + + return 0x0; +} + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, blocking_server) + webserver ws = create_webserver(8080); + + pthread_t tid; + pthread_create(&tid, NULL, start_ws_blocking, (void *) &ws); + + sleep(1); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); + + char* b; + pthread_join(tid,(void**) &b); + free(b); +LT_END_AUTO_TEST(blocking_server) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_error_resources) + webserver ws = create_webserver(8080) + .not_found_resource(not_found_custom) + .method_not_allowed_resource(not_allowed_custom); + + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + { + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + { + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/not_registered"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Not found custom"); + + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 404); + + curl_easy_cleanup(curl); + } + + { + ok.set_allowing("PUT", false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Not allowed custom"); + + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 405); + + curl_easy_cleanup(curl); + } + + ws.stop(); +LT_END_AUTO_TEST(custom_error_resources) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() diff --git a/3rd_party/libhttpserver-0.18.2/test/key.pem b/3rd_party/libhttpserver-0.18.2/test/key.pem new file mode 100644 index 00000000..a5848eed --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAtN08lLyuR5lAIq0adOu7WiBDuG4m4eZ6qJVKFHaPy3m5TEFf +qp67/0TQDE8eV3zS2eOf5GzPYHhfKmNL7D8kLz+I7psytCziWLiMJh2lJvb2152I +niC4sArk4I2rnd1ZGQ6EPX7XiKPusHvVE6VamacaWy7pQTIf1bL19HDydVRQu60+ +faPYQFJRX/Y1/xpinWCO/TLYFcdDjwY5pkg+pInAQX5n4JDu2yBsJUbbCMjM58Wx +7ulbqF/3lca0765gsIBFy1kWeq7vDEJeWH6nhl10VcDHl1PetSnn27r5hD3HIAsZ +vBWdQtXJwxxV4bIkoAMzAYwv2+ilYTUOCp1HWQIDAQABAoIBAArOQv3R7gmqDspj +lDaTFOz0C4e70QfjGMX0sWnakYnDGn6DU19iv3GnX1S072ejtgc9kcJ4e8VUO79R +EmqpdRR7k8dJr3RTUCyjzf/C+qiCzcmhCFYGN3KRHA6MeEnkvRuBogX4i5EG1k5l +/5t+YBTZBnqXKWlzQLKoUAiMLPg0eRWh+6q7H4N7kdWWBmTpako7TEqpIwuEnPGx +u3EPuTR+LN6lF55WBePbCHccUHUQaXuav18NuDkcJmCiMArK9SKb+h0RqLD6oMI/ +dKD6n8cZXeMBkK+C8U/K0sN2hFHACsu30b9XfdnljgP9v+BP8GhnB0nCB6tNBCPo +32srOwECgYEAxWh3iBT4lWqL6bZavVbnhmvtif4nHv2t2/hOs/CAq8iLAw0oWGZc ++JEZTUDMvFRlulr0kcaWra+4fN3OmJnjeuFXZq52lfMgXBIKBmoSaZpIh2aDY1Rd +RbEse7nQl9hTEPmYspiXLGtnAXW7HuWqVfFFP3ya8rUS3t4d07Hig8ECgYEA6ou6 +OHiBRTbtDqLIv8NghARc/AqwNWgEc9PelCPe5bdCOLBEyFjqKiT2MttnSSUc2Zob +XhYkHC6zN1Mlq30N0e3Q61YK9LxMdU1vsluXxNq2rfK1Scb1oOlOOtlbV3zA3VRF +hV3t1nOA9tFmUrwZi0CUMWJE/zbPAyhwWotKyZkCgYEAh0kFicPdbABdrCglXVae +SnfSjVwYkVuGd5Ze0WADvjYsVkYBHTvhgRNnRJMg+/vWz3Sf4Ps4rgUbqK8Vc20b +AU5G6H6tlCvPRGm0ZxrwTWDHTcuKRVs+pJE8C/qWoklE/AAhjluWVoGwUMbPGuiH +6Gf1bgHF6oj/Sq7rv/VLZ8ECgYBeq7ml05YyLuJutuwa4yzQ/MXfghzv4aVyb0F3 +QCdXR6o2IYgR6jnSewrZKlA9aPqFJrwHNR6sNXlnSmt5Fcf/RWO/qgJQGLUv3+rG +7kuLTNDR05azSdiZc7J89ID3Bkb+z2YkV+6JUiPq/Ei1+nDBEXb/m+/HqALU/nyj +P3gXeQKBgBusb8Rbd+KgxSA0hwY6aoRTPRt8LNvXdsB9vRcKKHUFQvxUWiUSS+L9 +/Qu1sJbrUquKOHqksV5wCnWnAKyJNJlhHuBToqQTgKXjuNmVdYSe631saiI7PHyC +eRJ6DxULPxABytJrYCRrNqmXi5TCiqR2mtfalEMOPxz8rUU8dYyx +-----END RSA PRIVATE KEY----- diff --git a/3rd_party/libhttpserver-0.18.2/test/libhttpserver.supp b/3rd_party/libhttpserver-0.18.2/test/libhttpserver.supp new file mode 100644 index 00000000..6da3d3c8 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/libhttpserver.supp @@ -0,0 +1,5 @@ +{ + gnutls_session_get_data + Memcheck:Cond + fun:gnutls_session_get_data +} diff --git a/3rd_party/libhttpserver-0.18.2/test/littletest.hpp b/3rd_party/libhttpserver-0.18.2/test/littletest.hpp new file mode 100644 index 00000000..bf9539fd --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/littletest.hpp @@ -0,0 +1,591 @@ +/* + This file is part of liblittletest + Copyright (C) 2012 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +//TODO: personalized messages + +#ifndef _LITTLETEST_HPP_ +#define _LITTLETEST_HPP_ + +#include +#include +#include +#include +#include +#include + +#define LT_VERSION 1.0 + +#define WARN 0 +#define CHECK 1 +#define ASSERT 2 + +#define LT_BEGIN_TEST_ENV() int main() { + +#define LT_END_TEST_ENV() } + +#define LT_BEGIN_AUTO_TEST_ENV() LT_BEGIN_TEST_ENV() + +#define LT_END_AUTO_TEST_ENV() \ + return (__lt_result__); \ + } + +#define AUTORUN_TESTS() \ + std::vector::iterator __lt_autorun_it__; \ + for(__lt_autorun_it__ = littletest::auto_test_vector.begin(); __lt_autorun_it__ != littletest::auto_test_vector.end(); ++__lt_autorun_it__) \ + littletest::auto_test_runner((*__lt_autorun_it__)); \ + int __lt_result__ = littletest::auto_test_runner(); + +#define LT_CREATE_RUNNER(__lt_suite_name__, __lt_runner_name__) \ + std::cout << "** Initializing Runner \"" << #__lt_runner_name__ << "\" **" << std::endl; \ + littletest::test_runner __lt_runner_name__ + +#define LT_RUNNER(__lt_runner_name__) __lt_runner_name__ + +#define LT_BEGIN_SUITE(__lt_name__) \ + struct __lt_name__ : public littletest::suite<__lt_name__> \ + { + +#define LT_END_SUITE(__lt_name__) \ + }; + +#define LT_CHECKPOINT() __lt_tr__->set_checkpoint(__FILE__, __LINE__) + +#define LT_BEGIN_TEST(__lt_suite_name__, __lt_test_name__) \ + struct __lt_test_name__ ## _class: public __lt_suite_name__, littletest::test<__lt_test_name__ ## _class> \ + { \ + __lt_test_name__ ## _class() \ + { \ + __lt_name__ = #__lt_test_name__; \ + littletest::auto_test_vector.push_back(this); \ + } \ + void operator()(littletest::test_runner* __lt_tr__) \ + { + +#define LT_END_TEST(__lt_test_name__) \ + } \ + }; \ + __lt_test_name__ ## _class __lt_test_name__; \ + +#define LT_BEGIN_AUTO_TEST(__lt_suite_name__, __lt_test_name__) LT_BEGIN_TEST(__lt_suite_name__, __lt_test_name__) + +#define LT_END_AUTO_TEST(__lt_test_name__) \ + LT_END_TEST(__lt_test_name__) \ + +#define LT_SWITCH_MODE(__lt_mode__) \ + switch(__lt_mode__) \ + { \ + case(WARN): \ + throw littletest::warn_unattended(__lt_ss__.str()); \ + case(CHECK): \ + throw littletest::check_unattended(__lt_ss__.str()); \ + case(ASSERT): \ + throw littletest::assert_unattended(__lt_ss__.str()); \ + } + +#define LT_SIMPLE_OP(__lt_name__, __lt_val__, __lt_file__, __lt_line__, __lt_mode__) \ + if(!((__lt_val__))) \ + { \ + std::stringstream __lt_ss__; \ + __lt_ss__ << "(" << __lt_file__ << ":" << __lt_line__ << ") - error in " << "\"" << __lt_name__ << "\""; \ + LT_SWITCH_MODE(__lt_mode__) \ + } + +#define LT_THROW_OP(__lt_name__, __lt_operation__, __lt_file__, __lt_line__, __lt_mode__) \ + bool __lt_thrown__ = false; \ + std::stringstream __lt_ss__; \ + try \ + { \ + (__lt_operation__) ;\ + __lt_ss__ << "(" << __lt_file__ << ":" << __lt_line__ << ") - error in " << "\"" << __lt_name__ << "\": no exceptions thown by " << #__lt_operation__; \ + __lt_thrown__ = true; \ + } \ + catch(...) { } \ + if(__lt_thrown__) \ + LT_SWITCH_MODE(__lt_mode__) + +#define LT_NOTHROW_OP(__lt_name__, __lt_operation__, __lt_file__, __lt_line__, __lt_mode__) \ + try \ + { \ + (__lt_operation__) ;\ + } \ + catch(...) \ + { \ + std::stringstream __lt_ss__; \ + __lt_ss__ << "(" << __lt_file__ << ":" << __lt_line__ << ") - error in " << "\"" << __lt_name__ << "\": exceptions thown by " << #__lt_operation__; \ + LT_SWITCH_MODE(__lt_mode__) \ + } + +#define LT_COLLEQ_OP(__lt_name__, __lt_first_begin__, __lt_first_end__, __lt_second_begin__, __lt_file__, __lt_line__, __lt_mode__) \ + if(! std::equal((__lt_first_begin__), (__lt_first_end__), (__lt_second_begin__))) \ + { \ + std::stringstream __lt_ss__; \ + __lt_ss__ << "(" << __lt_file__ << ":" << __lt_line__ << ") - error in " << "\"" << __lt_name__ << "\": collections are different"; \ + LT_SWITCH_MODE(__lt_mode__) \ + } + +#define LT_COLLNEQ_OP(__lt_name__, __lt_first_begin__, __lt_first_end__, __lt_second_begin__, __lt_file__, __lt_line__, __lt_mode__) \ + if(std::equal((__lt_first_begin__), (__lt_first_end__), (__lt_second_begin__))) \ + { \ + std::stringstream __lt_ss__; \ + __lt_ss__ << "(" << __lt_file__ << ":" << __lt_line__ << ") - error in " << "\"" << __lt_name__ << "\": collections are equal"; \ + LT_SWITCH_MODE(__lt_mode__) \ + } + +#define LT_OP(__lt_name__, __lt_a__, __lt_b__, __lt_file__, __lt_line__, __lt_op__, __lt_mode__) \ + if((__lt_a__) __lt_op__ (__lt_b__)) \ + { \ + std::stringstream __lt_ss__; \ + __lt_ss__ << "(" << __lt_file__ << ":" << __lt_line__ << ") - error in " << "\"" << __lt_name__ << "\": " << (__lt_a__) << #__lt_op__ << (__lt_b__); \ + LT_SWITCH_MODE(__lt_mode__) \ + } + +#define LT_CATCH_ERRORS \ + catch(littletest::check_unattended& __lt_cu__) \ + { \ + std::cout << "[CHECK FAILURE] " << __lt_cu__.what() << std::endl; \ + __lt_tr__->add_failure(); \ + } \ + catch(littletest::assert_unattended& __lt_au__) \ + { \ + std::cout << "[ASSERT FAILURE] " << __lt_au__.what() << std::endl; \ + __lt_tr__->add_failure(); \ + throw __lt_au__; \ + } \ + catch(littletest::warn_unattended& __lt_wu__) \ + { \ + std::cout << "[WARN] " << __lt_wu__.what() << std::endl; \ + } + +#define LT_ADD_SUCCESS(__lt_mode__) \ + if(__lt_mode__) \ + __lt_tr__->add_success(); + +#define LT_EV(__lt_a__, __lt_b__, __lt_op__, __lt_mode__) \ + try \ + { \ + LT_OP(__lt_name__, (__lt_a__), (__lt_b__), __FILE__, __LINE__, __lt_op__, __lt_mode__); \ + LT_ADD_SUCCESS(__lt_mode__) \ + } \ + LT_CATCH_ERRORS + +#define LT_SIMPLE_EV(__lt_val__, __lt_mode__) \ + try \ + { \ + LT_SIMPLE_OP(__lt_name__, (__lt_val__), __FILE__, __LINE__, __lt_mode__); \ + LT_ADD_SUCCESS(__lt_mode__) \ + } \ + LT_CATCH_ERRORS + +#define LT_THROW_EV(__lt_operation__, __lt_mode__) \ + try \ + { \ + LT_THROW_OP(__lt_name__, (__lt_operation__), __FILE__, __LINE__, __lt_mode__); \ + LT_ADD_SUCCESS(__lt_mode__) \ + } \ + LT_CATCH_ERRORS + +#define LT_NOTHROW_EV(__lt_operation__, __lt_mode__) \ + try \ + { \ + LT_NOTHROW_OP(__lt_name__, (__lt_operation__), __FILE__, __LINE__, __lt_mode__); \ + LT_ADD_SUCCESS(__lt_mode__) \ + } \ + LT_CATCH_ERRORS + +#define LT_COLLEQ_EV(__lt_first_begin__, __lt_first_end__, __lt_second_begin__, __lt_mode__) \ + try \ + { \ + LT_COLLEQ_OP(__lt_name__, (__lt_first_begin__), (__lt_first_end__), (__lt_second_begin__), __FILE__, __LINE__, __lt_mode__); \ + LT_ADD_SUCCESS(__lt_mode__) \ + } \ + LT_CATCH_ERRORS + +#define LT_COLLNEQ_EV(__lt_first_begin__, __lt_first_end__, __lt_second_begin__, __lt_mode__) \ + try \ + { \ + LT_COLLNEQ_OP(__lt_name__, (__lt_first_begin__), (__lt_first_end__), (__lt_second_begin__), __FILE__, __LINE__, __lt_mode__); \ + LT_ADD_SUCCESS(__lt_mode__) \ + } \ + LT_CATCH_ERRORS + +#define LT_WARN(__lt_val__) LT_SIMPLE_EV((__lt_val__), WARN) +#define LT_WARN_EQ(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), !=, WARN) +#define LT_WARN_NEQ(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), ==, WARN) +#define LT_WARN_GT(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), <=, WARN) +#define LT_WARN_GTE(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), <, WARN) +#define LT_WARN_LT(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), >=, WARN) +#define LT_WARN_LTE(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), >, WARN) +#define LT_WARN_THROW(__lt_operation__) LT_THROW_EV((__lt_operation__), WARN) +#define LT_WARN_NOTHROW(__lt_operation__) LT_NOTHROW_EV((__lt_operation__), WARN) +#define LT_WARN_COLLECTIONS_EQ(__lt_first_begin__, __lt_first_end__, __lt_second_begin__) LT_COLLEQ_EV((__lt_first_begin__), (__lt_first_end__), (__lt_second_begin__), WARN) +#define LT_WARN_COLLECTIONS_NEQ(__lt_first_begin__, __lt_first_end__, __lt_second_begin__) LT_COLLNEQ_EV((__lt_first_begin__), (__lt_first_end__), (__lt_second_begin__), WARN) + +#define LT_CHECK(__lt_val__) LT_SIMPLE_EV((__lt_val__), CHECK) +#define LT_CHECK_EQ(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), !=, CHECK) +#define LT_CHECK_NEQ(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), ==, CHECK) +#define LT_CHECK_GT(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), <=, CHECK) +#define LT_CHECK_GTE(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), <, CHECK) +#define LT_CHECK_LT(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), >=, CHECK) +#define LT_CHECK_LTE(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), >, CHECK) +#define LT_CHECK_THROW(__lt_operation__) LT_THROW_EV((__lt_operation__), CHECK) +#define LT_CHECK_NOTHROW(__lt_operation__) LT_NOTHROW_EV((__lt_operation__), CHECK) +#define LT_CHECK_COLLECTIONS_EQ(__lt_first_begin__, __lt_first_end__, __lt_second_begin__) LT_COLLEQ_EV((__lt_first_begin__), (__lt_first_end__), (__lt_second_begin__), CHECK) +#define LT_CHECK_COLLECTIONS_NEQ(__lt_first_begin__, __lt_first_end__, __lt_second_begin__) LT_COLLNEQ_EV((__lt_first_begin__), (__lt_first_end__), (__lt_second_begin__), CHECK) + +#define LT_ASSERT(__lt_val__) LT_SIMPLE_EV((__lt_val__), ASSERT) +#define LT_ASSERT_EQ(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), !=, ASSERT) +#define LT_ASSERT_NEQ(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), ==, ASSERT) +#define LT_ASSERT_GT(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), <=, ASSERT) +#define LT_ASSERT_GTE(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), <, ASSERT) +#define LT_ASSERT_LT(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), >=, ASSERT) +#define LT_ASSERT_LTE(__lt_a__, __lt_b__) LT_EV((__lt_a__), (__lt_b__), >, ASSERT) +#define LT_ASSERT_THROW(__lt_operation__) LT_THROW_EV((__lt_operation__), ASSERT) +#define LT_ASSERT_NOTHROW(__lt_operation__) LT_NOTHROW_EV((__lt_operation__), ASSERT) +#define LT_ASSERT_COLLECTIONS_EQ(__lt_first_begin__, __lt_first_end__, __lt_second_begin__) LT_COLLEQ_EV((__lt_first_begin__), (__lt_first_end__), (__lt_second_begin__), ASSERT) +#define LT_ASSERT_COLLECTIONS_NEQ(__lt_first_begin__, __lt_first_end__, __lt_second_begin__) LT_COLLNEQ_EV((__lt_first_begin__), (__lt_first_end__), (__lt_second_begin__), ASSERT) + +#define LT_FAIL(__lt_message__) \ + std::cout << "[ASSERT FAILURE] (" << __FILE__ << ":" << __LINE__ << ") - error in " << "\"" << (__lt_name__) << "\": " << (__lt_message__) << std::endl; \ + __lt_tr__->add_failure(); \ + throw littletest::assert_unattended(""); + +namespace littletest +{ + +struct check_unattended : public std::exception +{ + check_unattended(const std::string& message): + message(message) + { + } + ~check_unattended() throw() { } + + virtual const char* what() const throw() + { + return message.c_str(); + } + + private: + std::string message; +}; + +struct assert_unattended : public std::exception +{ + assert_unattended(const std::string& message): + message(message) + { + } + ~assert_unattended() throw() { } + virtual const char* what() const throw() + { + return message.c_str(); + } + + private: + std::string message; +}; + +struct warn_unattended : public std::exception +{ + warn_unattended(const std::string& message): + message(message) + { + } + ~warn_unattended() throw() { } + virtual const char* what() const throw() + { + return message.c_str(); + } + + private: + std::string message; +}; + +template +class suite +{ + public: + void suite_set_up() + { + static_cast(this)->set_up(); + } + + void suite_tear_down() + { + static_cast(this)->tear_down(); + } + + suite() { } + suite(const suite& s) { } +}; + +double calculate_duration(timeval* before, timeval* after) +{ + return ((after->tv_sec * 1000 + (after->tv_usec / 1000.0)) - + (before->tv_sec * 1000 + (before->tv_usec / 1000.0))); +} + +class test_base; + +std::vector auto_test_vector; + +class test_runner +{ + public: + test_runner() : + last_checkpoint_file(""), + last_checkpoint_line(-1), + test_counter(1), + success_counter(0), + failures_counter(0), + good_time_total(0.0), + total_set_up_time(0.0), + total_tear_down_time(0.0), + total_time(0.0) + { + } + + template + test_runner& run(test_impl* t) + { + std::cout << "Running test (" << + test_counter << "): " << + t->__lt_name__ << std::endl; + + t->run_test(this); + + test_counter++; + return *this; + } + + template + test_runner& operator()(test_impl& t) + { + return run(&t); + } + + template + test_runner& operator()(test_impl* t) + { + return run(t); + } + + void clear_runner() + { + last_checkpoint_file = ""; + last_checkpoint_line = -1; + test_counter = 1; + success_counter = 0; + failures_counter = 0; + good_time_total = 0.0, + total_set_up_time = 0.0; + total_tear_down_time = 0.0; + total_time = 0.0; + } + + int operator()() + { + std::cout << "** Runner terminated! **" << std::endl; + std::cout << (test_counter - 1) << " tests executed" << std::endl; + std::cout << (failures_counter + success_counter) << " checks" << std::endl; + std::cout << "-> " << success_counter << " successes" << std::endl; + std::cout << "-> " << failures_counter << " failures" << std::endl; + std::cout << "Total run time: " << total_time << " ms"<< std::endl; + std::cout << "Total time spent in tests: " << good_time_total << " ms" << std::endl; + std::cout << "Average set up time: " << (total_set_up_time / test_counter) << " ms" << std::endl; + std::cout << "Average tear down time: " << (total_tear_down_time / test_counter) << " ms" << std::endl; + int to_ret = failures_counter; + clear_runner(); + return to_ret; + } + + void add_failure() + { + failures_counter++; + } + + void add_success() + { + success_counter++; + } + + void set_checkpoint(const char* file, int line) + { + last_checkpoint_file = file; + last_checkpoint_line = line; + } + + void add_good_time(double t) + { + good_time_total += t; + } + + void add_set_up_time(double t) + { + total_set_up_time += t; + } + + void add_tear_down_time(double t) + { + total_tear_down_time += t; + } + + void add_total_time(double t) + { + total_time += t; + } + + operator int() + { + return failures_counter; + } + + int get_test_number() + { + return test_counter; + } + + int get_successes() + { + return success_counter; + } + + int get_failures() + { + return failures_counter; + } + + double get_test_time() + { + return good_time_total; + } + + double get_total_time() + { + return total_time; + } + + std::string last_checkpoint_file; + int last_checkpoint_line; + + private: + int test_counter; + int success_counter; + int failures_counter; + double good_time_total; + double total_set_up_time; + double total_tear_down_time; + double total_time; +}; + +class test_base +{ + public: + const char* __lt_name__; + virtual void run_test(test_runner* tr) { } + virtual void operator()() { } +}; + +test_runner auto_test_runner; + +template +class test : public test_base +{ + virtual void run_test(test_runner* tr) + { + double set_up_duration = 0.0, tear_down_duration = 0.0, test_duration = 0.0; + timeval before, after; + try + { + gettimeofday(&before, NULL); + static_cast(this)->suite_set_up(); + gettimeofday(&after, NULL); + set_up_duration = calculate_duration(&before, &after); + tr->add_set_up_time(set_up_duration); + } + catch(std::exception& e) + { + std::cout << "Exception during " << static_cast(this)->__lt_name__ << " set up" << std::endl; + std::cout << e.what() << std::endl; + } + catch(...) + { + std::cout << "Exception during " << static_cast(this)->__lt_name__ << " set up" << std::endl; + } + try + { + gettimeofday(&before, NULL); + (*static_cast(this))(tr); + } + catch(assert_unattended& au) + { + ; + } + catch(std::exception& e) + { + std::cout << "Exception during " << static_cast(this)->__lt_name__ << " run" << std::endl; + std::cout << e.what() << std::endl; + if(tr->last_checkpoint_line != -1) + std::cout << "Last checkpoint in " << tr->last_checkpoint_file << ":" << tr->last_checkpoint_line << std::endl; + } + catch(...) + { + std::cout << "Exception during " << static_cast(this)->__lt_name__ << " run" << std::endl; + if(tr->last_checkpoint_line != -1) + std::cout << "Last checkpoint in " << tr->last_checkpoint_file << ":" << tr->last_checkpoint_line << std::endl; + } + gettimeofday(&after, NULL); + + test_duration = calculate_duration(&before, &after); + + tr->add_good_time(test_duration); + + std::cout << "- Time spent during \"" << static_cast(this)->__lt_name__ << "\": " << test_duration << " ms"<< std::endl; + + try + { + gettimeofday(&before, NULL); + static_cast(this)->suite_tear_down(); + gettimeofday(&after, NULL); + tear_down_duration = calculate_duration(&before, &after); + tr->add_tear_down_time(tear_down_duration); + } + catch(std::exception& e) + { + std::cout << "Exception during " << static_cast(this)->__lt_name__ << " tear down" << std::endl; + std::cout << e.what() << std::endl; + } + catch(...) + { + std::cout << "Exception during " << static_cast(this)->__lt_name__ << " tear down" << std::endl; + } + double total = set_up_duration + test_duration + tear_down_duration; + tr->add_total_time(total); + } + protected: + test() { } + test(const test& t) { } + + friend class test_runner; +}; + +}; + +#endif //_LITTLETEST_HPP_ diff --git a/3rd_party/libhttpserver-0.18.2/test/test_content b/3rd_party/libhttpserver-0.18.2/test/test_content new file mode 100644 index 00000000..5f643138 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/test_content @@ -0,0 +1 @@ +test content of file diff --git a/3rd_party/libhttpserver-0.18.2/test/test_root_ca.pem b/3rd_party/libhttpserver-0.18.2/test/test_root_ca.pem new file mode 100644 index 00000000..998460f1 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/test_root_ca.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- diff --git a/3rd_party/libhttpserver-0.18.2/test/unit/http_endpoint_test.cpp b/3rd_party/libhttpserver-0.18.2/test/unit/http_endpoint_test.cpp new file mode 100644 index 00000000..677b0fa4 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/unit/http_endpoint_test.cpp @@ -0,0 +1,353 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "httpserver/details/http_endpoint.hpp" + +#include "littletest.hpp" + +using namespace httpserver; +using namespace std; +using namespace details; + +LT_BEGIN_SUITE(http_endpoint_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(http_endpoint_suite) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_default) + http_endpoint test_endpoint; + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "/"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + LT_CHECK_EQ(test_endpoint.get_url_pieces().size(), 0); + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), false); +LT_END_AUTO_TEST(http_endpoint_default) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_registration) + http_endpoint test_endpoint("/path/to/resource", false, true, true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_from_string_registration) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_not_beginning_with_slash) + http_endpoint test_endpoint("path/to/resource", false, true, true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_from_string_not_beginning_with_slash) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_ending_with_slash) + http_endpoint test_endpoint("path/to/resource/", false, true, true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_from_string_ending_with_slash) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_family) + http_endpoint test_endpoint("/path/to/resource", true, true, true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), true); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_from_string_family) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_default_no_regex) + http_endpoint test_endpoint("/path/to/resource"); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "/path/to/resource"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), false); +LT_END_AUTO_TEST(http_endpoint_default_no_regex) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_no_regex) + http_endpoint test_endpoint("/path/to/resource", false, false, false); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "/path/to/resource"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), false); +LT_END_AUTO_TEST(http_endpoint_from_string_no_regex) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration) + http_endpoint test_endpoint("/path/to/resource", false, true, true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_registration) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_nested_regex) + http_endpoint test_endpoint("/path/to/resource/with/[0-9]+/to/fetch", false, true, true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource/with/[0-9]+/to/fetch"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource/with/[0-9]+/to/fetch$"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource", "with", "[0-9]+", "to", "fetch" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_registration_nested_regex) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_arg) + http_endpoint test_endpoint("/path/to/resource/with/{arg}/to/fetch", false, true, true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource/with/{arg}/to/fetch"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource/with/([^\\/]+)/to/fetch$"); + + string expected_pars_arr[] = { "arg" }; + vector expected_pars(expected_pars_arr, expected_pars_arr + sizeof(expected_pars_arr) / sizeof(expected_pars_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pars().begin(), test_endpoint.get_url_pars().end(), expected_pars.begin()); + + string expected_arr[] = { "path", "to", "resource", "with", "{arg}", "to", "fetch" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + int expected_chunk_positions_arr[] = { 4 }; + vector expected_chunk_positions(expected_chunk_positions_arr, expected_chunk_positions_arr + sizeof(expected_chunk_positions_arr) / sizeof(expected_chunk_positions_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_chunk_positions().begin(), test_endpoint.get_chunk_positions().end(), expected_chunk_positions.begin()); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_registration_arg) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_arg_custom_regex) + http_endpoint test_endpoint("/path/to/resource/with/{arg|([0-9]+)}/to/fetch", false, true, true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource/with/{arg|([0-9]+)}/to/fetch"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource/with/([0-9]+)/to/fetch$"); + + string expected_pars_arr[] = { "arg" }; + vector expected_pars(expected_pars_arr, expected_pars_arr + sizeof(expected_pars_arr) / sizeof(expected_pars_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pars().begin(), test_endpoint.get_url_pars().end(), expected_pars.begin()); + + string expected_arr[] = { "path", "to", "resource", "with", "{arg|([0-9]+)}", "to", "fetch" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + int expected_chunk_positions_arr[] = { 4 }; + vector expected_chunk_positions(expected_chunk_positions_arr, expected_chunk_positions_arr + sizeof(expected_chunk_positions_arr) / sizeof(expected_chunk_positions_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_chunk_positions().begin(), test_endpoint.get_chunk_positions().end(), expected_chunk_positions.begin()); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_registration_arg_custom_regex) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_invalid_arg) + LT_CHECK_THROW(http_endpoint("/path/to/resource/with/{}/to/fetch", false, true)); +LT_END_AUTO_TEST(http_endpoint_registration_invalid_arg) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_copy_constructor) + http_endpoint a("/path/to/resource/with/{arg|([0-9]+)}/to/fetch", false, true, true); + http_endpoint b(a); + + LT_CHECK_EQ(a.get_url_complete(), b.get_url_complete()); + LT_CHECK_EQ(a.get_url_normalized(), b.get_url_normalized()); + LT_CHECK_COLLECTIONS_EQ(a.get_url_pars().begin(), a.get_url_pars().end(), b.get_url_pars().begin()); + LT_CHECK_COLLECTIONS_EQ(a.get_url_pieces().begin(), a.get_url_pieces().end(), b.get_url_pieces().begin()); + LT_CHECK_COLLECTIONS_EQ(a.get_chunk_positions().begin(), a.get_chunk_positions().end(), b.get_chunk_positions().begin()); + LT_CHECK_EQ(a.is_family_url(), b.is_family_url()); + LT_CHECK_EQ(a.is_regex_compiled(), b.is_regex_compiled()); + + LT_CHECK_EQ(a.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); + LT_CHECK_EQ(b.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); +LT_END_AUTO_TEST(http_endpoint_copy_constructor) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_assignment) + http_endpoint a("/path/to/resource/with/{arg|([0-9]+)}/to/fetch", false, true, true); + http_endpoint b("whatever/initial/value"); + + LT_CHECK_NEQ(a.get_url_complete(), b.get_url_complete()); + + std::cout << "before assigning" << std::endl; + b = a; + std::cout << "after assigning" << std::endl; + + LT_CHECK_EQ(a.get_url_complete(), b.get_url_complete()); + LT_CHECK_EQ(a.get_url_normalized(), b.get_url_normalized()); + LT_CHECK_COLLECTIONS_EQ(a.get_url_pars().begin(), a.get_url_pars().end(), b.get_url_pars().begin()); + LT_CHECK_COLLECTIONS_EQ(a.get_url_pieces().begin(), a.get_url_pieces().end(), b.get_url_pieces().begin()); + LT_CHECK_COLLECTIONS_EQ(a.get_chunk_positions().begin(), a.get_chunk_positions().end(), b.get_chunk_positions().begin()); + LT_CHECK_EQ(a.is_family_url(), b.is_family_url()); + LT_CHECK_EQ(a.is_regex_compiled(), b.is_regex_compiled()); + + LT_CHECK_EQ(a.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); + LT_CHECK_EQ(b.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); +LT_END_AUTO_TEST(http_endpoint_assignment) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex) + http_endpoint test_endpoint("/path/to/resource/", false, true, true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to2/resource")), false); +LT_END_AUTO_TEST(http_endpoint_match_regex) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_nested) + http_endpoint test_endpoint("/path/to/resource/with/[0-9]+/to/fetch", false, true, true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/0/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to/resource/with/1/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/1/to/fetch/")), true); +LT_END_AUTO_TEST(http_endpoint_match_regex_nested) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_nested_capture) + http_endpoint test_endpoint("/path/to/resource/with/([0-9]+)/to/fetch", false, true, true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/0/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to/resource/with/1/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/1/to/fetch/")), true); +LT_END_AUTO_TEST(http_endpoint_match_regex_nested_capture) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_nested_arg) + http_endpoint test_endpoint("/path/to/resource/with/{arg}/to/fetch", false, true, true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/0/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to/resource/with/1/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/1/to/fetch/")), true); +LT_END_AUTO_TEST(http_endpoint_match_regex_nested_arg) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_nested_custom_arg) + http_endpoint test_endpoint("/path/to/resource/with/{arg|([0-9]+)}/to/fetch", false, true, true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/0/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to/resource/with/1/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/1/to/fetch/")), true); +LT_END_AUTO_TEST(http_endpoint_match_regex_nested_custom_arg) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_family) + http_endpoint test_endpoint("/path/to/resource", true, true, true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to/resource")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to/resource/")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/followed/by/anything")), true); + + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to2/resource")), false); +LT_END_AUTO_TEST(http_endpoint_match_regex_family) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_disabled) + http_endpoint test_endpoint("/path/to/resource", false, true, false); + LT_CHECK_THROW(test_endpoint.match(http_endpoint("/path/to/resource"))); +LT_END_AUTO_TEST(http_endpoint_match_regex_disabled) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_cannot_use_regex_if_not_registering) + LT_CHECK_THROW(http_endpoint("/path/to/resource", false, false, true)); +LT_END_AUTO_TEST(http_endpoint_cannot_use_regex_if_not_registering) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, comparator) + LT_CHECK_EQ(http_endpoint("/a/b") < http_endpoint("/a/c"), true); + LT_CHECK_EQ(http_endpoint("/a/c") < http_endpoint("/a/b"), false); + + LT_CHECK_EQ(http_endpoint("/a/b") < http_endpoint("/a/b/c"), true); + LT_CHECK_EQ(http_endpoint("/a/b/c") < http_endpoint("/a/b"), false); +LT_END_AUTO_TEST(comparator) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() diff --git a/3rd_party/libhttpserver-0.18.2/test/unit/http_utils_test.cpp b/3rd_party/libhttpserver-0.18.2/test/unit/http_utils_test.cpp new file mode 100644 index 00000000..6ef9cb99 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/unit/http_utils_test.cpp @@ -0,0 +1,608 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "httpserver/http_utils.hpp" + +#if defined(_WIN32) && ! defined(__CYGWIN__) +#define _WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#include +#include +#else +#include +#endif + +#include + +#include "littletest.hpp" + +using namespace httpserver; +using namespace std; + +LT_BEGIN_SUITE(http_utils_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(http_utils_suite) + +LT_BEGIN_AUTO_TEST(http_utils_suite, unescape) + char* with_plus = (char*) malloc(6 * sizeof(char)); + sprintf(with_plus, "%s", "A%20B"); + std::string string_with_plus = with_plus; + int expected_size = http::http_unescape(string_with_plus); + + char* expected = (char*) malloc(4 * sizeof(char)); + sprintf(expected, "%s", "A B"); + + std::cout << "|||||" << string_with_plus << "||||" << std::endl; + std::cout << expected << std::endl; + + LT_CHECK_EQ(string_with_plus, string(expected)); + LT_CHECK_EQ(expected_size, 3); + + free(with_plus); + free(expected); +LT_END_AUTO_TEST(unescape) + +LT_BEGIN_AUTO_TEST(http_utils_suite, unescape_plus) + char* with_plus = (char*) malloc(6 * sizeof(char)); + sprintf(with_plus, "%s", "A+B"); + std::string string_with_plus = with_plus; + int expected_size = http::http_unescape(string_with_plus); + + char* expected = (char*) malloc(4 * sizeof(char)); + sprintf(expected, "%s", "A B"); + + LT_CHECK_EQ(string_with_plus, string(expected)); + LT_CHECK_EQ(expected_size, 3); + + free(with_plus); + free(expected); +LT_END_AUTO_TEST(unescape_plus) + +LT_BEGIN_AUTO_TEST(http_utils_suite, tokenize_url) + string value = "test/this/url/here"; + string expected_arr[] = { "test", "this", "url", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = http::http_utils::tokenize_url(value, '/'); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(tokenize_url) + +LT_BEGIN_AUTO_TEST(http_utils_suite, tokenize_url_multiple_spaces) + string value = "test//this//url//here"; + string expected_arr[] = { "test", "this", "url", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = http::http_utils::tokenize_url(value, '/'); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(tokenize_url_multiple_spaces) + +LT_BEGIN_AUTO_TEST(http_utils_suite, tokenize_url_end_slash) + string value = "test/this/url/here/"; + string expected_arr[] = { "test", "this", "url", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = http::http_utils::tokenize_url(value, '/'); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(tokenize_url_end_slash) + +LT_BEGIN_AUTO_TEST(http_utils_suite, standardize_url) + string url = "/", result; + result = http::http_utils::standardize_url(url); + LT_CHECK_EQ(result, "/"); + + url = "/abc/", result = ""; + result = http::http_utils::standardize_url(url); + LT_CHECK_EQ(result, "/abc"); + + url = "/abc", result = ""; + result = http::http_utils::standardize_url(url); + LT_CHECK_EQ(result, "/abc"); + + url = "/abc/pqr/", result = ""; + result = http::http_utils::standardize_url(url); + LT_CHECK_EQ(result, "/abc/pqr"); + + url = "/abc/pqr", result = ""; + result = http::http_utils::standardize_url(url); + LT_CHECK_EQ(result, "/abc/pqr"); + + url = "/abc//pqr", result = ""; + result = http::http_utils::standardize_url(url); + LT_CHECK_EQ(result, "/abc/pqr"); +LT_END_AUTO_TEST(standardize_url) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str) + struct sockaddr_in ip4addr; + + ip4addr.sin_family = AF_INET; + ip4addr.sin_port = htons(3490); + ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + string result = http::get_ip_str((struct sockaddr*) &ip4addr); + unsigned short port = http::get_port((struct sockaddr*) &ip4addr); + + LT_CHECK_EQ(result, "127.0.0.1"); + LT_CHECK_EQ(port, htons(3490)); +LT_END_AUTO_TEST(ip_to_str) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str6) + struct sockaddr_in6 ip6addr; + + ip6addr.sin6_family = AF_INET6; + ip6addr.sin6_port = htons(3490); + inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &(ip6addr.sin6_addr)); + + string result = http::get_ip_str((struct sockaddr *) &ip6addr); + unsigned short port = http::get_port((struct sockaddr*) &ip6addr); + + LT_CHECK_EQ(result, "2001:db8:8714:3a90::12"); + LT_CHECK_EQ(port, htons(3490)); +LT_END_AUTO_TEST(ip_to_str6) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str_invalid_family) + struct sockaddr_in ip4addr; + + ip4addr.sin_family = 55; + ip4addr.sin_port = htons(3490); + ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + LT_CHECK_THROW(http::get_ip_str((struct sockaddr*) &ip4addr)); +LT_END_AUTO_TEST(ip_to_str_invalid_family) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str_null) + LT_CHECK_THROW(http::get_ip_str((struct sockaddr*) 0x0)); +LT_END_AUTO_TEST(ip_to_str_null) + +LT_BEGIN_AUTO_TEST(http_utils_suite, get_port_invalid_family) + struct sockaddr_in ip4addr; + + ip4addr.sin_family = 55; + ip4addr.sin_port = htons(3490); + ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + LT_CHECK_THROW(http::get_port((struct sockaddr*) &ip4addr)); +LT_END_AUTO_TEST(get_port_invalid_family) + +LT_BEGIN_AUTO_TEST(http_utils_suite, get_port_null) + LT_CHECK_THROW(http::get_port((struct sockaddr*) 0x0)); +LT_END_AUTO_TEST(get_port_null) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str) + http::ip_representation test_ip("192.168.5.5"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV4); + + for (int i = 0; i < 12; i++) { + LT_CHECK_EQ(test_ip.pieces[i], 0); + } + + LT_CHECK_EQ(test_ip.pieces[12], 192); + LT_CHECK_EQ(test_ip.pieces[13], 168); + LT_CHECK_EQ(test_ip.pieces[14], 5); + LT_CHECK_EQ(test_ip.pieces[15], 5); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation4_str) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str_mask) + http::ip_representation test_ip("192.168.*.*"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV4); + + for (int i = 0; i < 12; i++) { + LT_CHECK_EQ(test_ip.pieces[i], 0); + } + + LT_CHECK_EQ(test_ip.pieces[12], 192); + LT_CHECK_EQ(test_ip.pieces[13], 168); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 0); + + LT_CHECK_EQ(test_ip.mask, 0x3FFF); +LT_END_AUTO_TEST(ip_representation4_str_mask) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str_invalid) + LT_CHECK_THROW(http::ip_representation("192.168.5.5.5")); +LT_END_AUTO_TEST(ip_representation4_str_invalid) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str_beyond255) + LT_CHECK_THROW(http::ip_representation("192.168.256.5")); +LT_END_AUTO_TEST(ip_representation4_str_beyond255) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str) + http::ip_representation test_ip("2001:db8:8714:3a90::12"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 32); + LT_CHECK_EQ(test_ip.pieces[1], 1); + LT_CHECK_EQ(test_ip.pieces[2], 13); + LT_CHECK_EQ(test_ip.pieces[3], 184); + LT_CHECK_EQ(test_ip.pieces[4], 135); + LT_CHECK_EQ(test_ip.pieces[5], 20); + LT_CHECK_EQ(test_ip.pieces[6], 58); + LT_CHECK_EQ(test_ip.pieces[7], 144); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 0); + LT_CHECK_EQ(test_ip.pieces[11], 0); + LT_CHECK_EQ(test_ip.pieces[12], 0); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 18); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation6_str) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_mask) + http::ip_representation test_ip("2001:db8:8714:3a90:*:*"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 32); + LT_CHECK_EQ(test_ip.pieces[1], 1); + LT_CHECK_EQ(test_ip.pieces[2], 13); + LT_CHECK_EQ(test_ip.pieces[3], 184); + LT_CHECK_EQ(test_ip.pieces[4], 135); + LT_CHECK_EQ(test_ip.pieces[5], 20); + LT_CHECK_EQ(test_ip.pieces[6], 58); + LT_CHECK_EQ(test_ip.pieces[7], 144); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 0); + LT_CHECK_EQ(test_ip.pieces[11], 0); + LT_CHECK_EQ(test_ip.pieces[12], 0); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 0); + + LT_CHECK_EQ(test_ip.mask, 0xF0FF); +LT_END_AUTO_TEST(ip_representation6_str_mask) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_nested) + http::ip_representation test_ip("::ffff:192.0.2.128"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 0); + LT_CHECK_EQ(test_ip.pieces[1], 0); + LT_CHECK_EQ(test_ip.pieces[2], 0); + LT_CHECK_EQ(test_ip.pieces[3], 0); + LT_CHECK_EQ(test_ip.pieces[4], 0); + LT_CHECK_EQ(test_ip.pieces[5], 0); + LT_CHECK_EQ(test_ip.pieces[6], 0); + LT_CHECK_EQ(test_ip.pieces[7], 0); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 255); + LT_CHECK_EQ(test_ip.pieces[11], 255); + LT_CHECK_EQ(test_ip.pieces[12], 192); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 2); + LT_CHECK_EQ(test_ip.pieces[15], 128); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation6_str_nested) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_nested_deprecated) + LT_CHECK_NOTHROW(http::ip_representation("::192.0.2.128")); + http::ip_representation test_ip("::192.0.2.128"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 0); + LT_CHECK_EQ(test_ip.pieces[1], 0); + LT_CHECK_EQ(test_ip.pieces[2], 0); + LT_CHECK_EQ(test_ip.pieces[3], 0); + LT_CHECK_EQ(test_ip.pieces[4], 0); + LT_CHECK_EQ(test_ip.pieces[5], 0); + LT_CHECK_EQ(test_ip.pieces[6], 0); + LT_CHECK_EQ(test_ip.pieces[7], 0); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 0); + LT_CHECK_EQ(test_ip.pieces[11], 0); + LT_CHECK_EQ(test_ip.pieces[12], 192); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 2); + LT_CHECK_EQ(test_ip.pieces[15], 128); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation6_str_nested_deprecated) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_ipv4_mask) + http::ip_representation test_ip("::ffff:192.0.*.*"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 0); + LT_CHECK_EQ(test_ip.pieces[1], 0); + LT_CHECK_EQ(test_ip.pieces[2], 0); + LT_CHECK_EQ(test_ip.pieces[3], 0); + LT_CHECK_EQ(test_ip.pieces[4], 0); + LT_CHECK_EQ(test_ip.pieces[5], 0); + LT_CHECK_EQ(test_ip.pieces[6], 0); + LT_CHECK_EQ(test_ip.pieces[7], 0); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 255); + LT_CHECK_EQ(test_ip.pieces[11], 255); + LT_CHECK_EQ(test_ip.pieces[12], 192); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 0); + + LT_CHECK_EQ(test_ip.mask, 0x3FFF); +LT_END_AUTO_TEST(ip_representation6_str_ipv4_mask) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_clustered_middle) + http::ip_representation test_ip("2001:db8::ff00:42:8329"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 32); + LT_CHECK_EQ(test_ip.pieces[1], 1); + LT_CHECK_EQ(test_ip.pieces[2], 13); + LT_CHECK_EQ(test_ip.pieces[3], 184); + LT_CHECK_EQ(test_ip.pieces[4], 0); + LT_CHECK_EQ(test_ip.pieces[5], 0); + LT_CHECK_EQ(test_ip.pieces[6], 0); + LT_CHECK_EQ(test_ip.pieces[7], 0); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 255); + LT_CHECK_EQ(test_ip.pieces[11], 0); + LT_CHECK_EQ(test_ip.pieces[12], 0); + LT_CHECK_EQ(test_ip.pieces[13], 66); + LT_CHECK_EQ(test_ip.pieces[14], 131); + LT_CHECK_EQ(test_ip.pieces[15], 41); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation6_str_clustered_middle) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_loopback) + http::ip_representation test_ip("::1"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 0); + LT_CHECK_EQ(test_ip.pieces[1], 0); + LT_CHECK_EQ(test_ip.pieces[2], 0); + LT_CHECK_EQ(test_ip.pieces[3], 0); + LT_CHECK_EQ(test_ip.pieces[4], 0); + LT_CHECK_EQ(test_ip.pieces[5], 0); + LT_CHECK_EQ(test_ip.pieces[6], 0); + LT_CHECK_EQ(test_ip.pieces[7], 0); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 0); + LT_CHECK_EQ(test_ip.pieces[11], 0); + LT_CHECK_EQ(test_ip.pieces[12], 0); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 1); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation6_str_loopback) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation_weight) + LT_CHECK_EQ(http::ip_representation("::1").weight(), 16); + LT_CHECK_EQ(http::ip_representation("192.168.0.1").weight(), 16); + LT_CHECK_EQ(http::ip_representation("192.168.*.*").weight(), 14); + LT_CHECK_EQ(http::ip_representation("::ffff:192.0.*.*").weight(), 14); + LT_CHECK_EQ(http::ip_representation("2001:db8:8714:3a90:*:*").weight(), 12); + LT_CHECK_EQ(http::ip_representation("2001:db8:8714:3a90:8714:2001:db8:3a90").weight(), 16); + LT_CHECK_EQ(http::ip_representation("2001:db8:8714:3a90:8714:2001:*:*").weight(), 12); + LT_CHECK_EQ(http::ip_representation("*:*:*:*:*:*:*:*").weight(), 0); +LT_END_AUTO_TEST(ip_representation_weight) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid) + LT_CHECK_THROW(http::ip_representation("2001:db8:8714:3a90::12:4:4:4")); +LT_END_AUTO_TEST(ip_representation6_str_invalid) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_block_too_long) + LT_CHECK_THROW(http::ip_representation("2001:db8:87214:3a90::12:4:4")); +LT_END_AUTO_TEST(ip_representation6_str_block_too_long) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_multiple_clusters) + LT_CHECK_THROW(http::ip_representation("2001::3a90::12:4:4")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_multiple_clusters) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_too_long_before_nested) + LT_CHECK_THROW(http::ip_representation("2001:db8:8714:3a90:13:12:13:192.0.2.128")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_too_long_before_nested) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_beyond255) + LT_CHECK_THROW(http::ip_representation("::ffff:192.0.256.128")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_beyond255) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_more_than_4_parts) + LT_CHECK_THROW(http::ip_representation("::ffff:192.0.5.128.128")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_more_than_4_parts) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_not_at_end) + LT_CHECK_THROW(http::ip_representation("::ffff:192.0.256.128:ffff")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_not_at_end) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_starting_non_zero) + LT_CHECK_THROW(http::ip_representation("0:0:1::ffff:192.0.5.128")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_starting_non_zero) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_starting_wrong_prefix) + LT_CHECK_THROW(http::ip_representation("::ffcc:192.0.5.128")); + LT_CHECK_THROW(http::ip_representation("::ccff:192.0.5.128")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_starting_wrong_prefix) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_sockaddr) + struct sockaddr_in ip4addr; + + ip4addr.sin_family = AF_INET; + ip4addr.sin_port = htons(3490); + ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + http::ip_representation test_ip((sockaddr*) &ip4addr); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV4); + + for (int i = 0; i < 12; i++) { + LT_CHECK_EQ(test_ip.pieces[i], 0); + } + + LT_CHECK_EQ(test_ip.pieces[12], 127); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 1); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation4_sockaddr) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_sockaddr) + struct sockaddr_in6 ip6addr; + + ip6addr.sin6_family = AF_INET6; + ip6addr.sin6_port = htons(3490); + inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &(ip6addr.sin6_addr)); + + http::ip_representation test_ip((sockaddr*) &ip6addr); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 32); + LT_CHECK_EQ(test_ip.pieces[1], 1); + LT_CHECK_EQ(test_ip.pieces[2], 13); + LT_CHECK_EQ(test_ip.pieces[3], 184); + LT_CHECK_EQ(test_ip.pieces[4], 135); + LT_CHECK_EQ(test_ip.pieces[5], 20); + LT_CHECK_EQ(test_ip.pieces[6], 58); + LT_CHECK_EQ(test_ip.pieces[7], 144); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 0); + LT_CHECK_EQ(test_ip.pieces[11], 0); + LT_CHECK_EQ(test_ip.pieces[12], 0); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 18); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation6_sockaddr) + +LT_BEGIN_AUTO_TEST(http_utils_suite, load_file) + LT_CHECK_EQ(http::load_file("test_content"), "test content of file\n"); +LT_END_AUTO_TEST(load_file) + +LT_BEGIN_AUTO_TEST(http_utils_suite, load_file_invalid) + LT_CHECK_THROW(http::load_file("test_content_invalid")); +LT_END_AUTO_TEST(load_file_invalid) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation_less_than) + LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.0.2"), true); + LT_CHECK_EQ(http::ip_representation("128.0.0.1") < http::ip_representation("127.0.0.2"), false); + LT_CHECK_EQ(http::ip_representation("127.0.0.2") < http::ip_representation("127.0.0.1"), false); + LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.0.1"), false); + LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.0.1"), false); + + LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:8329"), false); + LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8330") < http::ip_representation("2001:db8::ff00:42:8329"), false); + LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:8330"), true); + LT_CHECK_EQ(http::ip_representation("2002:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:8330"), false); + + LT_CHECK_EQ(http::ip_representation("::192.0.2.128") < http::ip_representation("::192.0.2.128"), false); + LT_CHECK_EQ(http::ip_representation("::192.0.2.129") < http::ip_representation("::192.0.2.128"), false); + LT_CHECK_EQ(http::ip_representation("::192.0.2.128") < http::ip_representation("::192.0.2.129"), true); + + LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.128") < http::ip_representation("::ffff:192.0.2.128"), false); + LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.129") < http::ip_representation("::ffff:192.0.2.128"), false); + LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.128") < http::ip_representation("::ffff:192.0.2.129"), true); + + LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.128") < http::ip_representation("::192.0.2.128"), false); + LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.128") < http::ip_representation("::192.0.2.128"), false); + LT_CHECK_EQ(http::ip_representation("::192.0.2.128") < http::ip_representation("::ffff:192.0.2.129"), true); +LT_END_AUTO_TEST(ip_representation_less_than) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation_less_than_with_masks) + LT_CHECK_EQ(http::ip_representation("127.0.*.*") < http::ip_representation("127.0.0.1"), false); + LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.*.*"), false); + LT_CHECK_EQ(http::ip_representation("127.0.0.*") < http::ip_representation("127.0.*.*"), false); + LT_CHECK_EQ(http::ip_representation("127.0.*.1") < http::ip_representation("127.0.0.1"), false); + LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.*.1"), false); + LT_CHECK_EQ(http::ip_representation("127.1.0.1") < http::ip_representation("127.0.*.1"), false); + LT_CHECK_EQ(http::ip_representation("127.0.*.1") < http::ip_representation("127.1.0.1"), true); + LT_CHECK_EQ(http::ip_representation("127.1.*.1") < http::ip_representation("127.0.*.1"), false); + LT_CHECK_EQ(http::ip_representation("127.0.*.1") < http::ip_representation("127.1.*.1"), true); + + LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:*") < http::ip_representation("2001:db8::ff00:42:8329"), false); + LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:*"), false); +LT_END_AUTO_TEST(ip_representation_less_than_with_masks) + +LT_BEGIN_AUTO_TEST(http_utils_suite, dump_header_map) + std::map header_map; + header_map["HEADER_ONE"] = "VALUE_ONE"; + header_map["HEADER_TWO"] = "VALUE_TWO"; + header_map["HEADER_THREE"] = "VALUE_THREE"; + + std::stringstream ss; + http::dump_header_map(ss, "prefix", header_map); + LT_CHECK_EQ(ss.str(), " prefix [HEADER_ONE:\"VALUE_ONE\" HEADER_TWO:\"VALUE_TWO\" HEADER_THREE:\"VALUE_THREE\" ]\n"); +LT_END_AUTO_TEST(dump_header_map) + +LT_BEGIN_AUTO_TEST(http_utils_suite, dump_header_map_no_prefix) + std::map header_map; + header_map["HEADER_ONE"] = "VALUE_ONE"; + header_map["HEADER_TWO"] = "VALUE_TWO"; + header_map["HEADER_THREE"] = "VALUE_THREE"; + + std::stringstream ss; + http::dump_header_map(ss, "", header_map); + LT_CHECK_EQ(ss.str(), " [HEADER_ONE:\"VALUE_ONE\" HEADER_TWO:\"VALUE_TWO\" HEADER_THREE:\"VALUE_THREE\" ]\n"); +LT_END_AUTO_TEST(dump_header_map_no_prefix) + +LT_BEGIN_AUTO_TEST(http_utils_suite, dump_arg_map) + std::map arg_map; + arg_map["ARG_ONE"] = "VALUE_ONE"; + arg_map["ARG_TWO"] = "VALUE_TWO"; + arg_map["ARG_THREE"] = "VALUE_THREE"; + + std::stringstream ss; + http::dump_arg_map(ss, "prefix", arg_map); + LT_CHECK_EQ(ss.str(), " prefix [ARG_ONE:\"VALUE_ONE\" ARG_TWO:\"VALUE_TWO\" ARG_THREE:\"VALUE_THREE\" ]\n"); +LT_END_AUTO_TEST(dump_arg_map) + +LT_BEGIN_AUTO_TEST(http_utils_suite, dump_arg_map_no_prefix) + std::map arg_map; + arg_map["ARG_ONE"] = "VALUE_ONE"; + arg_map["ARG_TWO"] = "VALUE_TWO"; + arg_map["ARG_THREE"] = "VALUE_THREE"; + + std::stringstream ss; + http::dump_arg_map(ss, "", arg_map); + LT_CHECK_EQ(ss.str(), " [ARG_ONE:\"VALUE_ONE\" ARG_TWO:\"VALUE_TWO\" ARG_THREE:\"VALUE_THREE\" ]\n"); +LT_END_AUTO_TEST(dump_arg_map_no_prefix) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() diff --git a/3rd_party/libhttpserver-0.18.2/test/unit/string_utilities_test.cpp b/3rd_party/libhttpserver-0.18.2/test/unit/string_utilities_test.cpp new file mode 100644 index 00000000..2539ac08 --- /dev/null +++ b/3rd_party/libhttpserver-0.18.2/test/unit/string_utilities_test.cpp @@ -0,0 +1,98 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "httpserver/string_utilities.hpp" + +#include + +#include "littletest.hpp" + +using namespace httpserver; +using namespace std; + +LT_BEGIN_SUITE(string_utilities_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(string_utilities_suite) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, to_upper_copy) + LT_CHECK_EQ(string_utilities::to_upper_copy("test message"), string("TEST MESSAGE")); + LT_CHECK_EQ(string_utilities::to_upper_copy("tEsT mEssAge 245&$"), string("TEST MESSAGE 245&$")); +LT_END_AUTO_TEST(to_upper_copy) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, to_upper) + string value = "test message"; + string_utilities::to_upper(value); + LT_CHECK_EQ(value, string("TEST MESSAGE")); + + value = "tEsT mEssAge 245&$"; + string_utilities::to_upper(value); + LT_CHECK_EQ(value, string("TEST MESSAGE 245&$")); +LT_END_AUTO_TEST(to_upper) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, to_lower_copy) + LT_CHECK_EQ(string_utilities::to_lower_copy("TEST MESSAGE"), string("test message")); + LT_CHECK_EQ(string_utilities::to_lower_copy("tEsT mEssAge 245&$"), string("test message 245&$")); +LT_END_AUTO_TEST(to_lower_copy) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string) + string value = "test this message here"; + string expected_arr[] = { "test", "this", "message", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = string_utilities::string_split(value, ' ', false); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(split_string) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string_multiple_spaces) + string value = "test this message here"; + string expected_arr[] = { "test", "", "this", "", "message", "", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = string_utilities::string_split(value, ' ', false); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(split_string_multiple_spaces) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string_multiple_spaces_collapse) + string value = "test this message here"; + string expected_arr[] = { "test", "this", "message", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = string_utilities::string_split(value, ' ', true); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(split_string_multiple_spaces_collapse) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string_end_space) + string value = "test this message here "; + string expected_arr[] = { "test", "this", "message", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = string_utilities::string_split(value, ' ', false); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(split_string_end_space) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() diff --git a/3rd_party/libmicrohttpd/build.cmd b/3rd_party/libmicrohttpd/build.cmd new file mode 100644 index 00000000..1dcf4eb7 --- /dev/null +++ b/3rd_party/libmicrohttpd/build.cmd @@ -0,0 +1,5 @@ +pushd "%1" + cd w32\VS2019 + if "%3" == "ON" (set BUILD_TYPE=Win32) else (set BUILD_TYPE=x64) + msbuild libmicrohttpd.sln /p:Configuration=%2-static /p:Platform="%BUILD_TYPE%" /t:libmicrohttpd || exit 1 +exit 0 \ No newline at end of file diff --git a/3rd_party/ttmath-0.9.3/CHANGELOG b/3rd_party/ttmath-0.9.3/CHANGELOG new file mode 100644 index 00000000..ab31e4b4 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/CHANGELOG @@ -0,0 +1,601 @@ +Version 0.9.3 (2012.11.28): + * fixed: in Big::FromDouble(double value) (only 32 bit version) + buffer overflow in referencing to UInt<2> + this was used when 'value' was in so called "unnormalized" state + (E=0 and F is nonzero) + it produced incorrect mantissa (on about 8th decimal digit up) + * added: Parser::InitCGamma() + initializing coefficients used when calculating the gamma (or factorial) function + this speed up the next calculations + you don't have to call this method explicitly + these coefficients will be calculated when needed + * added: option 'group_digits' to Conv struct + you can set how many digits should be grouped + * changed: small optimizations in UInt::ToString() and Big::FromString() + + +Version 0.9.2 (2010.09.23): + * fixed: Big::Add() sometimes incorrectly rounded the last bit from its mantissa + * fixed: Big::BigAnd() Big::BigOr() Big::BigXor() should have set NaN + when the argument was negative (they only returned 2) + * fixed: recurrence calling in Big::FromString(const std::string &, uint, const wchar_t **, bool *) + it should have the signature: Big::FromString(const std::string &, uint, const char **, bool *) + * fixed: Big::ToString method + in some cases when in the output string the exponent should be equal zero + the method changes the exponent to one so the last digit from the mantissa + was lost + * fixed: Big::ToDouble(double &) set always +INF (infinity) + when the value was too large (even for negative values) + (it should set -INF in such a case) + * added: some missing operators + UInt::operator~() /* bitwise neg */ + UInt::operator&() /* bitwise and */ + UInt::operator&=() + UInt::operator|() /* bitwise or */ + UInt::operator|=() + UInt::operator^() /* bitwise xor */ + UInt::operator^=() + Big::operator&() + Big::operator&=() + Big::operator|() + Big::operator|=() + Big::operator^() + Big::operator^=() + for Big<> we do not define bitwise neg + Big::operator++() + Big::operator++(int) + Big::operator--() + Big::operator--(int) + * added: macro TTMATH_DONT_USE_WCHAR + if defined then the library does not use wide characters + (wchar_t, std::wstring, ...) this is a workaround for some compilers + * added: bool UInt::IsOnlyTheHighestBitSet() + bool UInt::IsOnlyTheLowestBitSet() + returning true if only the highest/lowest bit is set + * added: uint Int::MulInt(sint ss2) + * added: void UInt::Swap(UInt & ss2) + void Big::Swap(UInt & ss2) + method for swapping this for an argument + * added: macro TTMATH_BIG_DEFAULT_CLEAR + when defined the default constructor from Big<> clears its mantissa and exponent + Big<1, 2> var; + var.mantissa and var.exponent will be set to zero + (but var has the NaN flag set too - it is not zero value, this is mainly for debug purposes) + * added: only on 32bit platforms: + uint UInt::FromUInt(uint64_t n) + uint Int::FromInt(int64_t n) + void Big::FromUInt(uint64_t n) + void Big::FromInt(int64_t n) + and appropriate constructors and operators + * added: TTMATH_FORCEASM macro + asm version of the library is available by default only for: + x86 and amd64 platforms and for Microsoft Visual and GCC compilers, + but you can force using asm version (the same asm as for Microsoft Visual) + by defining TTMATH_FORCEASM macro + you have to be sure that your compiler accept such an asm format + * added: some missing methods for converting + for UInt<>, Int<> and Big<> classes: + uint ToUInt() + sint ToInt() + ToUInt(uint32_t &) + ToInt(uint32_t &) + ToInt(int32_t &) + ToUInt(uint64_t &) + ToInt(uint64_t &) + ToInt(int64_t &) + FromUInt(uint32_t &) + FromInt(uint32_t &) + FromInt(int32_t &) + FromUInt(uint64_t &) + FromInt(uint64_t &) + FromInt(int64_t &) + and appropriate constructors and operators + * added: double Big::ToDouble() /there was only Big::ToDouble(double &) / + uint Big::ToFloat(float &) + float Big::ToFloat() + * changed: now asm version is available only on x86 and amd64 + (and only for GCC and MS VC compilers) + * removed: macro TTMATH_RELEASE (now the 'release' version is default) + for debug version define TTMATH_DEBUG macro + TTMATH_DEBUG is also automatically defined when DEBUG or _DEBUG is set + * removed: macro TTMATH_REFERENCE_ASSERT from all methods in public interface + + +Version 0.9.1 (2010.02.07): + * fixed: the parser didn't use characters for changing the base (# and &) + those characters were skipped + (this bug was introduced in 0.9.0) + * fixed: added in the parser: operator's associativity + operator ^ (powering) is right-associative: + sample: 2^3^4 is equal 2^(3^4) and it is: 2.41e+24 + previously was: 2^3^4 = (2^3)^4 = 4096 + * fixed: in Big::ToString_CreateNewMantissaAndExponent() changed the formula: + new_exp_ = [log base (2^exponent)] + 1 + now the part '+ 1' is only made when the logarithm is positive and with fraction + if the value is negative we can only skip the fraction, previously + we lost some last digits from the new mantissa + Consider this binary value (32 bit mantissa): + (bin)1.0000000000000000000000000000011 + previously ToString() gave 1, now we have: 1.000000001 + * changed: in Big::ToString() the base rounding is made only if the result value + would not be an integer, e.g. if the value is 1.999999999999 then + the base rounding will not be done - because as the result would be 2 + * added: IEEE 754 half-to-even rounding (bankers' rounding) to the following + floating point algorithms: Big::Add, Big::Sub, Big::Mul, Big::Div + * added: static sint UInt::FindLowestBitInWord(uint x) + this method is looking for the lowest set bit in a word + * added: UInt::FindLowestBit(uint & table_id, uint & index) + this method is looking for the lowest set bit + + +Version 0.9.0 (2009.11.25): + * added: support for wide characters (wchar_t, std::wstring) + * added: Big::IsInteger() + returns true if the value is integer (without fraction) + (NaN flag is not checked) + * added: global Gamma() function + * added: gamma() function to the parser + * added: CGamma class + is used with Gamma() and Factorial() in multithreaded environment + * added: multithread support for Big<> class + you should compile with TTMATH_MULTITHREADS + and use TTMATH_MULTITHREADS_HELPER macro somewhere in your *.cpp file + * added: x86_64 asm code for Microsoft Visual compiler + file: ttmathuint_x86_64_msvc.asm + (this file should be compiled first because MS VC doesn't support inline assembler in x86_64 mode) + * added: flag TTMATH_BIG_ZERO to Big<> class + if this flag is set then there is a value zero + Big::IsZero() is faster now + * added: Big::ClearInfoBit(unsigned char) + Big::SetInfoBit(unsigned char) + Big::IsInfoBit(unsigned char) + some methods for manipulating the info flags + * added: macro: TTMATH_BITS(min_bits) + which returns the number of machine words + capable to hold min_bits bits + * added: bool Parser::Calculated() + this method returns true is something was calculated + (at least one mathematical operator was used or a function or variable) + * added: to the parser: operator percentage + e.g. 1000-50%=1000-(1000*0,5)=500 + * added: struct: Conv + consists of some parameters used + in ToString() and FromString() + * added: Big::ToString() can group digits + e.g. 1234567 -> 1`234`567 + * added: Parser::SetGroup(int g) + Parser::SetComma(int c, int c2 = 0) + Parser::SetParamSep(int s) + * added: std::string UInt::ToString(uint b = 10) + std::wstring UInt::ToWString(uint b = 10) + std::string Int::ToString(uint b = 10) + std::wstring Int::ToWString(uint b = 10) + uint Big::ToString(std::string & result, const Conv & conv) + uint Big::ToString(std::wstring & result, const Conv & conv) + std::string Big::ToString(const Conv & conv) + std::string Big::ToString() + std::wstring Big::ToWString(const Conv & conv) + std::wstring Big::ToWString() + * added: uint FromString(const char * source, const Conv & conv, const char **, bool *) + uint FromString(const wchar_t * source, const Conv & conv, const wchar_t **, bool *) + uint FromString(const std::string & string, const Conv & conv, const wchar_t **, bool *) + uint FromString(const std::wstring & string, const Conv & conv, const wchar_t **, bool *) + * added: UInt::Sqrt() - a new algorithm for calculating the square root + * added: to the parser: function frac() - returns a value without the integer part + (only fraction remains) + * added: Int::DivInt(sint divisor, sint * remainder) + * added: const char * UInt::LibTypeStr() + const char * Big::LibTypeStr() + LibTypeCode UInt::LibType() + LibTypeCode Big::LibType() + returning a string/enum represents the currect type of the library + we have following types: + asm_vc_32 - with asm code designed for Microsoft Visual C++ (32 bits) + asm_gcc_32 - with asm code designed for GCC (32 bits) + asm_vc_64 - with asm for VC (64 bit) + asm_gcc_64 - with asm for GCC (64 bit) + no_asm_32 - pure C++ version (32 bit) - without any asm code + no_asm_64 - pure C++ version (64 bit) - without any asm code + * added: UInt::operator>>(int) + UInt::operator>>=(int) + UInt::operator<<(int) + UInt::operator<<=(int) + * changed: Factorial() is using the Gamma() function now + * changed: Big::Div(ss2) + Big::Mod(ss2) + they return 2 when ss2 is zero + previously returned 1 + * changed: algorithms in Big::Sqrt() and ttmath::Root(x ; n) + they were not too much accurate for some integers + e.g. Root(16;4) returned a value very closed to 2 (not exactly 2) + * changed: added specializations to Big::ToString() when the base is equal 4, 8 or 16 + the previous version was not accurate on some last digits (after the comma operator) + consider this binary value (32 bit mantissa): + base 2: 1.1111 1111 1111 1111 1111 1111 1110 101 + previous ToString() gave: + base 4: 1.33333333333332 + base 8: 1.777777777 + base 16: 1.FFFFFF + now we have: + base 4: 1.3333333333333222 + base 8: 1.77777777724 + base 16: 1.FFFFFFEA + * changed: in Big::ToString() some additional rounding (base_round) is now made only + when the value is not an integer + * changed: another compilers than MS VC or GCC by default use no asm version (TTMATH_NOASM) + * removed: Parser<>::SetFactorialMax() method + the factorial() is such a fast now that we don't need the method longer + * removed: ErrorCode::err_too_big_factorial + * removed: macros: TTMATH_COMMA_CHARACTER_1 and TTMATH_COMMA_CHARACTER_2 + the comma characters we have in Conv struct now + + +Version 0.8.6 (2009.10.25): + * fixed: UInt::SetBitInWord(uint & value, uint bit) set 1 if the bit was + equal 1 (should be set 2) + this affected only no-asm parts - when macro TTMATH_NOASM was defined + * fixed: UInt::MulInt(uint ss2) + there was a buffer overflow when value_size was equal 1 + * fixed: UInt::AddVector() and UInt::SubVector() didn't want to compile + when macro TTMATH_NOASM was defined + * fixed: Big::operator>> didn't correctly recognize values in scientific mode (with 'e' character) + * fixed: Int::FromString(const tt_string & s, uint b = 10) + didn't use 'b' (always was '10') + * fixed: buffer overflow in Big::ToInt(Int & result) + * fixed: powering algorithm in: + UInt::Pow(UInt pow) + Big::Pow(UInt pow) + Big::PowUInt(Big pow) + when 'pow' was sufficient large the algorithm returned carry + but the result could have been calculated correctly + + +Version 0.8.5 (2009.06.16): + * fixed: Big::Mod(x) didn't correctly return a carry + and the result was sometimes very big (even greater than x) + * fixed: global function Mod(x) didn't set an ErrorCode object + * fixed: global function Round() didn't test a carry + now it sets ErrorCode object + * changed: function Sin(x) to Sin(x, ErrorCode * err=0) + when x was very big the function returns zero + now it sets ErrorCode object to err_overflow + and the result has a NaN flag set + the same is to Cos() function + * changed: PrepareSin(x) is using Big::Mod() now when reducing 2PI period + should be a little accurate especially on a very big 'x' + * changed: uint Mul(const UInt & ss2, uint algorithm = 100) + void MulBig(const UInt & ss2, UInt & result, uint algorithm = 100) + those methods by default use MulFastest() and MulFastestBig() + * changed: changed a little Mul2Big() to cooperate with Mul3Big() + * added: uint UInt::Mul3(const UInt & ss2) + void UInt::Mul3Big(const UInt & ss2, UInt & result) + a new multiplication algorithm: Karatsuba multiplication, + on a vector UInt<100> with all items different from zero this algorithm is faster + about 3 times than Mul2Big(), and on a vector UInt<1000> with all items different from + zero this algorithm is faster more than 5 times than Mul2Big() + (measured on 32bit platform with GCC 4.3.3 with -O3 and -DTTMATH_RELEASE) + * added: uint MulFastest(const UInt & ss2) + void MulFastestBig(const UInt & ss2, UInt & result) + those methods are trying to select the fastest multiplication algorithm + * added: uint AddVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + uint SubVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + three forms: asm x86, asm x86_64, no-asm + those methods are used by the Karatsuba multiplication algorithm + * added: to Big<> class: support for NaN flag (Not a Number) + bool Big::IsNan() - returns true if the NaN flag is set + void Big::SetNan() - sets the NaN flag + The NaN flag is set by default after creating an object: + Big<1, 2> a; // NaN is set (it means the object has not a valid number) + std::cout << a; // cout gives "NaN" + a = 123; // now NaN is not set + std::cout << a; // cout gives "123" + The NaN is set if there was a carry during calculations + a.Mul(very_big_value); // a will have a NaN set + The NaN is set if an argument is NaN too + b.SetNan(); + a.Add(b); // a will have NaN because b has NaN too + If you try to do something on a NaN object, the result is a NaN too + a.SetNan(); + a.Add(2); // a is still a NaN + The NaN is set if you use incorrect arguments + a.Ln(-10); // a will have the NaN flag + The only way to clear the NaN flag is to assign a correct value or other correct object, + supposing 'a' has NaN flag, to remove the flag you can either: + a = 10; + a.FromInt(30); + a.SetOne(); + a.FromBig(other_object_without_nan); + etc. + + +Version 0.8.4 (2009.05.08): + * fixed: UInt::DivInt() didn't check whether the divisor is zero + there was a hardware interruption when the divisor was zero + (now the method returns one) + * fixed: the problem with GCC optimization on x86_64 + sometimes when using -O2 or -O3 GCC doesn't set correctly + the stack pointer (actually the stack is used for other things) + and you can't use instructions like push/pop in assembler code. + All the asm code in x86_64 have been rewritten, now instructions + push/pop are not used, other thing which have access to stack + (like "m" (mask) constraints in Rcl2 and Rcr2) have also gone away, + now the library works well with -O2 and -O3 and the asm code + is a little faster + * added: UInt::PrintLog(const char * msg, std::ostream & output) + used (for debugging purposes) by macro TTMATH_LOG(msg) + (it is used in nearly all methods in UInt class) + * added: macro TTMATH_DEBUG_LOG: when defined then TTMATH_LOG() + put some debug information (to std::cout) + * added: ttmathuint_x86.h, ttmathuint_x86_64.h, ttmathuint_noasm.h, + all the methods which are using assembler code have been + rewritten to no-asm forms, now we have: + 1. asm for x86 file: ttmathuint_x86.h + 2. asm for x86_64 file: ttmathuint_x86_64.h + 3. no asm file: ttmathuint_noasm.h + (it's used when macro TTMATH_NOASM is defined) + The third form can be used on x86 and x86_64 as well and + on other platforms with a little effort. + + +Version 0.8.3 (2009.04.06): + * fixed: RclMoveAllWords() and RcrMoveAllWords() sometimes didn't return + the proper carry, (when 'bits' was greater than or equal to 'value_size') + this had impact on Rcl() and Rcr(), they also returned the wrong carry + * fixed: UInt::Div() didn't return a correct result when the divisor was equal 1 + there was an error in UInt::DivInt() - when the divisor was 1 it returned + zero and the carry was set + * fixed: there was a TTMATH_REREFENCE_ASSERT error in Big::PowUInt() caused by: start.Mul(start) + * fixed: Big::Add incorrectly rounded 'this' when both exponents were equal + it caused that sometimes when adding a zero the result has changed + this had impact among other things on FromString() method + "0,8" had different binary representation from "0,80" + * fixed: template Big::FromBig(const Big & another) + didn't correctly set the exponent (when the mantisses had different size - + when 'man' was different from 'another_man') + this had impact on operator= too + sample: + Big<2,3> a = 100; + Big<3,5> b; + b = a; // b had a wrong value + * fixed: Big::Pow(const Big & pow) + it's using PowInt() only when pow.exponent is in range (-man*TTMATH_BITS_PER_UINT; 0] + previously the powering 'hung' on an input like this: "(1+ 1e-10000) ^ 10e100000000" + (there was 10e100000000 iterations in PowInt()) + * fixed: in function DegToRad(const ValueType & x, ErrorCode * err = 0) it is better + to make division first and then mutliplication -- the result is more + accurate especially when x is: 90,180,270 or 360 + * fixed: the parser didn't correctly treat operators for changing the base + (radix) -- operators '#' and '&', e.g.: + '#sin(1)' was equal '0' -- there was a zero from '#' and then + it was multipied by 'sin(1)' + the parser didn't check whether Big::FromString() has actually + read a proper value -- the method Big::FromString() didn't have + something to report such a situation + * fixed: Big::FromString() when the base is 10, the method reads the scientific + part only if such a part it correctly supplied, e.g: + '1234e10', '1234e+10', '1234e-5' + previous '1234e' was treated as: '1234e0' (now parsing stops on 'e' and + the 'e' can be parsed by other parsers, e.g. the mathematical + parser -- now in the parser would be: '1234e' = '1234 * e' = '3354,3597...' ) + * changed: renamed: Big::PowUInt(UInt pow) -> Big::Pow(UInt pow) + it returns 2 when there is: 0^0 + * changed: renamed: Big::PowInt(Int pow) -> Big::Pow(Int pow) + it returns 2 when there is: 0^0 or 0^(-something) + * changed: renamed: Big::PowBUInt() -> PowUInt(), Big::PowBInt() -> Big::PowInt() + they return 2 when the arguments are incorrect (like above) + * changed: UInt::SetBitInWord(uint & value, uint bit) is taking the first argument by a reference now, + the specific bit is set in the 'value' and the method returns the last state of the bit (zero or one) + * changed: UInt::SetBit(uint bit_index) - it's using TTMATH_ASSERT now + * changed: the size of built-in variables (constants) in ttmathbig.h + now they consist of 256 32bit words + macro TTMATH_BUILTIN_VARIABLES_SIZE is equal: 256u on a 32bit platform and 128ul on a 64bit platform + * changed: the asm code in ttmathuint.h and ttmathuint64.h has been completely rewritten + now UInt<> is faster about 15-30% than UInt<> from 0.8.2 + this has impact on Big<> too - it's faster about 10% now + * changed: in the parser: the form with operators '#' and '&' is as follows: + [-|+][#|&]numeric_value + previous was: [-|+][#|&][-|+]numeric_value + * changed: in the parser: the short form of multiplication has the same + priority as the normal multiplication, e.g.: + '2x^3' = 2 * (x^3) + previous the priority was greater than powering priority + previous: '2x^3' = (2*x) ^ 3 + * added: UInt::GetBit(uint bit_index) - returning the state of the specific bit + * added: Big::operator=(double) and Big::Big(double) + * added: UInt::Pow(UInt pow) and Int::Pow(Int pow) + * added: global template functions in ttmath.h: + ValueType GradToRad(const ValueType & x, ErrorCode * err = 0) + ValueType RadToGrad(const ValueType & x, ErrorCode * err = 0) + ValueType DegToGrad(const ValueType & x, ErrorCode * err = 0) + ValueType DegToGrad(const ValueType & d, const ValueType & m, + const ValueType & s, ErrorCode * err = 0) + ValueType GradToDeg(const ValueType & x, ErrorCode * err = 0) + * added: Parser::SetDegRadGrad(int angle) - 0 deg, 1 rad (default), 2 grad + this affects following functions (in the parser only): sin, cos, tan, cot, + asin, acos, atan, acot + * added: functions to the parser: gradtorad(grad), radtograd(rad), degtograd(deg), + degtograd(d,m,s), gradtodeg(grad) + * added: UInt::FromString, added a parametr 'after_source' + which is pointing at the end of the parsed string + * added: Int::FromString(): parameter 'const char ** after_source = 0' + if exists it's pointing at the end of the parsed string + * added: to UInt::FromString(), Int::FromString(), Big::FromString(): + parameter 'bool * value_read = 0' - (if exists) tells + whether something has actually been read (at least one digit) + * added: Objects::IsDefined(const std::string & name) + returning true if such an object is defined + * removed: Big::FromString() this method doesn't longer recognize operators + for changing the base ('#' and '&') + + +Version 0.8.2 (2008.06.18): + * added: UInt::BitNot2() this method has been proposed by + Arek + * changed: Int::FromInt(const Int & p), + Int::FromInt(sint value) (it returns zero now) + Int::operator=(uint i) + Int::Int(uint i) + * added: Int::FromUInt(const UInt & p), + Int::FromUInt(uint value) + and appropriate constructors and assignment + operators as well + * changed: Big::FromInt(Int value), + * added: Big::FromUInt(UInt value), + Big::operator=(const UInt & value) + Big::Big(const UInt & value) + * changed: the parser is allowed to recognize values which + begin with a dot, e.g '.5' is treated as '0.5' + * added: a method Big::FromDouble(double) which converts from + standard double into a Big + * added: uint Big::ToDouble(double&) - converting into double + * added: Big::FromBig() and an operator= and a contructor + for converting from another kind of a Big class + * added: to the parser: avg(), sum() + * added: 'decimal_point' parameter into Big::ToString(...) + * fixed: Big::operator>> didn't use TTMATH_COMMA_CHARACTER_2 macro + * added: a short form of multiplication (without the '*' character) + e.g. '5y', (it's used only if the second parameter + is a variable or function) + * changed: variables and functions are case-sensitive now + * added: variables and functions can have underline characters + in their names + * changed: 'max_digit_after_comma' in Big::ToString() + remove the -2 state + * added: 'remove_trailing_zeroes' in Big::ToString() + it's either true or false + * fixed/changed: the way of using Big::SetSign() + the method do not check whether there is a zero or not now + (even if there's a zero the method can set a sign bit) + I changed this due to some prior errors + (errors corrected in revision 17, 49 and 58) + + +Version 0.8.1 (2007.04.17): + * fixed: Big::PowFrac(..) didn't return a correct error code + (when 'this' was negative) + * added: Root(x; index) (and to the parser as well) + * added: macro: TTMATH_PRERELEASE_VER (can be either zero or one) + * added: UInt::MulInt(int, UInt::&) + * added: Big::MulUInt(uint) + * changed: Big::MulInt(sint) + * added: Big::ToUInt(uint &) + * changed: Big::ToInt(sint&) + * changed: Factorial() it uses Big::MulUInt() at the beginning + (faster now especially more on a 32bit platform) + * added: doxygen.cfg for generating a documentation from the doxygen + * changed: UInt::Rcl(uint c=0) and UInt::Rcr(uint c=0) into + UInt::Rcl2(uint bits, uint c) and UInt::Rcr2(uint bits, uint c) + now they can move more than one bit and they are only private + * fixed: UInt::Rcl(uint bits, uint c) and UInt::Rcr(uint bits, uint c) + didn't correctly return a carry if the 'bits' were equal + to 'value_size*TTMATH_BITS_PER_UINT' + * changed: UInt::Rcl(uint bits, uint c) and UInt::Rcr(uint bits, uint c) + into UInt::Rcl(uint bits, uint c=0) and + UInt::Rcr(uint bits, uint c=0) + they are faster now when the bits is greater than a half of + the TTMATH_BITS_PER_UINT + * changed: UInt::CompensationToLeft() it's faster now + * changed: more small changes where there were UInt::Rcl(uint c=0) and + UInt::Rcr(uint c=0) used + * changed: as the Big type uses UInt::Rcl() and UInt::Rcr() a lot then + it is much faster now (about 5-25%) + * added: ASinh(), ACosh(), ATanh() /ATgh()/, ACoth() /ACtgh()/ + and to the parser as well + * added: UInt::BitAnd(), UInt::BitOr(), UInt::BitXor(), UInt::BitNot(), + Big::BitAnd(), Big::BitOr(), Big::BitXor() + * added: to the parser: bitand(), bitor(), bitxor() + /band(), bor(), bxor()/ + * changed: the way of parsing operators in the mathematical parser + (the parser is not too much greedy now) + + +Version 0.8.0 (2007.03.28): + * added: into the parser: SetFactorialMax() + * added: DegToDeg(deg, min, sec), DegToRad(deg), DegToRad(deg, min, sec), + RadToDeg(rad), Ceil(x), Floor(x), Sqrt(x), Sinh(x), Cosh(x), + Tanh(x) /Tgh(x)/, Coth(x) /Ctgh(x)/ + * changed: class Objects in ttmathobjects.h has been completely rewritten, + we can change the names of user-defined variables or functions, + and the names are case-sensitive now + * added: class History which is used in functions which take a lot of time + during calculating e.g. Factorial(x) + * added: Tg(x) a wrapper for Tan(x) + * changed: CTan(x) is Cot(x) now + * added: Ctg(x) a wrapper for Cot(x) + * added: ATg(x) a wrapper for ATan(x) + * changed: ACTan(x) is ACot(x) now + * added: ACtg(x) a wrapper for ACot(x) + * added: UInt::PrintTable() (for debugging etc.) + * changed: the methods Big::SetPi() Big::SetE() and Big::SetLn2() have + been rewritten, now they have 128 32bit words (it's about + 1232 valid decimal digits) + * fixed: previous values from Big::SetPi() Big::SetE() and + Big::SetLn2() were not too much accurate (last 2-3 words were wrong) + * added: Big::SetLn10() (128 32bit words as well) + * added: macro TTMATH_BUILTIN_VARIABLES_SIZE which is equal 128u on + 32bit platforms and 64ul on 64bit platforms (128/2=64) + * added: macros TTMATH_PLATFORM32 and TTMATH_PLATFORM64 + * changed: a small optimisation in UInt::Mul2Big() + * added: at the end of ttmath.h: #include "ttmathparser.h" + this is for convenience for a programmer, he can only use #include + with ttmath.h even if he uses the parser + * added: to samples: big.cpp, parser.cpp + * added/changes/fixed: in copy-constructors and operators= in Int, + Uint and Big (more info in the commit log) + * renamed: Big::SetDotOne() into Big::Set05() + * changes: a few small optimisations in Big + * deleted: the word 'virtual' from destructors: UInt, Int, Big + (types in this library are not projected to be base-classes for + another ones derived from them) + * and more small changes (look at the commit log) + + +Version 0.7.2 (2007.03.09): + * added: Big::Mod - the remainder from a division + * added: Big::Sgn - the 'sign' from the value (-1,0,1) + * added: global functions Mod and Sgn too + * added: checking whether a user gives a correct value of a variable or function + (user-defined variables/functions in the mathematical parser) + * added: into the parser: logical operators: > < >= <= == != && || + * added: into the parser: logical functions: and() or() not() if() + * added: ErrorCode::err_unknown_operator when the parser couldn't read an operator + + +Version 0.7.1 (2007.02.27): + * fixed: the error 'overflow during printing' which was caused + by Big::FromInt(Int value) (the sign has to be set at the end) + * fixed: many small errors + * added: ATan (arctan), ACTan (arc ctan) functions + + +Version 0.7.0 (2007.02.24): + * finished: support for 64bit platforms + * added: ASin (arcsin), ACos (arccos) functions + + +Version 0.6.4 (2007.01.29): + * fixed: the problem with a sign in the mathematical parser /-(1) was 1/ + * added: UInt::AddInt and UInt::SubInt + * changed: UInt::AddOne and UInt::SubOne (much faster now) + * added: UInt::SetBitInWord + * changed: UInt::SetBit (much faster now) + UInt::AddTwoUints renamed to UInt::AddTwoInts + UInt::FindLeadingBit32 renamed to UInt::FindLeadingBitInWord + UInt::Mul64 renamed to UInt::MulTwoWords + UInt::Div64 renamed to UInt::DivTwoWords + * added: UInt::SetBitInWord + * and more small changes in UInt type + * start adding support for Amd64 (not finished yet) (added ttmathuint64.h) + + +Version 0.6.3 (2007.01.22): + * changed: position of arguments (x and base) in logarithm functions are swapped + * changed: it's possible to use any multiplication algorithms in the same time + (macros UINT_MUL_VERSION_'X' have gone) + * added: ExceptionInfo, ReferenceError and RuntimeError classes + * changed: the mess in macros has been cleaned up + * added: TTMATH_RELEASE macro + + +Version 0.6.2 (2007.01.10): + * added: New division algorithm (radix b) where b is 2^32 diff --git a/3rd_party/ttmath-0.9.3/COPYRIGHT b/3rd_party/ttmath-0.9.3/COPYRIGHT new file mode 100644 index 00000000..35b688cb --- /dev/null +++ b/3rd_party/ttmath-0.9.3/COPYRIGHT @@ -0,0 +1,28 @@ +Copyright (c) 2006-2012, Tomasz Sowa +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * 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. + + * Neither the name Tomasz Sowa nor the names of contributors to this + project may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. diff --git a/3rd_party/ttmath-0.9.3/README b/3rd_party/ttmath-0.9.3/README new file mode 100644 index 00000000..dd198802 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/README @@ -0,0 +1,23 @@ +A bignum library for C++ + +TTMath is a small library which allows one to perform arithmetic operations +with big unsigned integer, big signed integer and big floating point numbers. +It provides standard mathematical operations like adding, subtracting, +multiplying, dividing. With the library also goes a mathematical parser to +help you solving mathematical expressions. + +TTMath is developed under the BSD licence which means that it is free for +both personal and commercial use. + +The main goal of the library is to allow one to use big values in the same +way as the standard types like int or float. It does not need to be compiled +first because the whole library is written as the C++ templates. This means +only C++ developers can use this library and one thing they have to do is +to use 'include' directive of the preprocessor. How big the values can be +is set at compile time. + +Author: Tomasz Sowa +WWW: http://www.ttmath.org + +Contributors: +Christian Kaiser diff --git a/3rd_party/ttmath-0.9.3/samples/Makefile b/3rd_party/ttmath-0.9.3/samples/Makefile new file mode 100644 index 00000000..cd2e30d5 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/samples/Makefile @@ -0,0 +1,46 @@ +CC = g++ +CFLAGS = -Wall -pedantic -s -O2 -I.. -DTTMATH_DONT_USE_WCHAR + + +.SUFFIXES: .cpp .o + +.cpp.o: + $(CC) -c $(CFLAGS) $< + + +all: uint int big big2 parser + + +uint: uint.o + $(CC) -o uint $(CFLAGS) uint.o + +int: int.o + $(CC) -o int $(CFLAGS) int.o + +big: big.o + $(CC) -o big $(CFLAGS) big.o + +big2: big2.o + $(CC) -o big2 $(CFLAGS) big2.o + +parser: parser.o + $(CC) -o parser $(CFLAGS) parser.o + + +uint.o: uint.cpp +int.o: int.cpp +big.o: big.cpp +big2.o: big2.cpp +parser.o: parser.cpp + + +clean: + rm -f *.o + rm -f *.s + rm -f uint + rm -f int + rm -f big + rm -f big2 + rm -f parser +# on MS Windows can automatically be added suffixes .exe to the names of output programs + rm -f *.exe diff --git a/3rd_party/ttmath-0.9.3/samples/big.cpp b/3rd_party/ttmath-0.9.3/samples/big.cpp new file mode 100644 index 00000000..fb172036 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/samples/big.cpp @@ -0,0 +1,100 @@ +#include +#include + +// for convenience we're defining MyBig type +// this type has 2 words for its mantissa and 1 word for its exponent +// (on a 32bit platform one word means a word of 32 bits, +// and on a 64bit platform one word means a word of 64 bits) + +// Big +typedef ttmath::Big<1,2> MyBig; + + +void SimpleCalculating(const MyBig & a, const MyBig & b) +{ + std::cout << "Simple calculating" << std::endl; + std::cout << "a = " << a << std::endl; + std::cout << "b = " << b << std::endl; + std::cout << "a + b = " << a+b << std::endl; + std::cout << "a - b = " << a-b << std::endl; + std::cout << "a * b = " << a*b << std::endl; + std::cout << "a / b = " << a/b << std::endl; +} + + +void CalculatingWithCarry(const MyBig & a, const MyBig & b) +{ +MyBig atemp; + + std::cout << "Calculating with a carry" << std::endl; + std::cout << "a = " << a << std::endl; + std::cout << "b = " << b << std::endl; + + atemp = a; + if( !atemp.Add(b) ) + std::cout << "a + b = " << atemp << std::endl; + else + std::cout << "a + b = (carry)" << std::endl; + // it have no sense to print 'atemp' (it's undefined) + + atemp = a; + if( !atemp.Sub(b) ) + std::cout << "a - b = " << atemp << std::endl; + else + std::cout << "a - b = (carry)" << std::endl; + + atemp = a; + if( !atemp.Mul(b) ) + std::cout << "a * b = " << atemp << std::endl; + else + std::cout << "a * b = (carry)" << std::endl; + + + atemp = a; + if( !atemp.Div(b) ) + std::cout << "a / b = " << atemp << std::endl; + else + std::cout << "a / b = (carry or division by zero) " << std::endl; + +} + + +int main() +{ +MyBig a,b; + + // conversion from 'const char *' + a = "123456.543456"; + b = "98767878.124322"; + + SimpleCalculating(a,b); + + // 'a' will have the max value which can be held in this type + a.SetMax(); + + // conversion from double + b = 456.32; + + // Look at the value 'a' and the product from a+b and a-b + // Don't worry this is the nature of floating point numbers + CalculatingWithCarry(a,b); +} + +/* +the result (on 32 bit platform): + +Simple calculating +a = 123456.543456 +b = 98767878.124322 +a + b = 98891334.667778 +a - b = -98644421.580866 +a * b = 12193540837712.27076 +a / b = 0.00124996654580957646 +Calculating with a carry +a = 1.6248012560666408782e+646457012 +b = 456.319999999999993 +a + b = 1.6248012560666408782e+646457012 +a - b = 1.6248012560666408782e+646457012 +a * b = (carry) +a / b = 3.560661939136222174e+646457009 +*/ diff --git a/3rd_party/ttmath-0.9.3/samples/big2.cpp b/3rd_party/ttmath-0.9.3/samples/big2.cpp new file mode 100644 index 00000000..9bd253bb --- /dev/null +++ b/3rd_party/ttmath-0.9.3/samples/big2.cpp @@ -0,0 +1,113 @@ +#include +#include + + +// this is a similar example to big.cpp +// but now we're using TTMATH_BITS() macro +// this macro returns how many words we need to store +// the given number of bits + +// TTMATH_BITS(64) +// on a 32bit platform the macro returns 2 (2*32=64) +// on a 64bit platform the macro returns 1 + +// TTMATH_BITS(128) +// on a 32bit platform the macro returns 4 (4*32=128) +// on a 64bit platform the macro returns 2 (2*64=128) + +// Big +typedef ttmath::Big MyBig; + +// consequently on a 32bit platform we define: Big<2, 4> +// and on a 64bit platform: Big<1, 2> +// and the calculations will be the same on both platforms + + +void SimpleCalculating(const MyBig & a, const MyBig & b) +{ + std::cout << "Simple calculating" << std::endl; + std::cout << "a = " << a << std::endl; + std::cout << "b = " << b << std::endl; + std::cout << "a + b = " << a+b << std::endl; + std::cout << "a - b = " << a-b << std::endl; + std::cout << "a * b = " << a*b << std::endl; + std::cout << "a / b = " << a/b << std::endl; +} + + +void CalculatingWithCarry(const MyBig & a, const MyBig & b) +{ +MyBig atemp; + + std::cout << "Calculating with a carry" << std::endl; + std::cout << "a = " << a << std::endl; + std::cout << "b = " << b << std::endl; + + atemp = a; + if( !atemp.Add(b) ) + std::cout << "a + b = " << atemp << std::endl; + else + std::cout << "a + b = (carry)" << std::endl; + // it have no sense to print 'atemp' (it's undefined) + + atemp = a; + if( !atemp.Sub(b) ) + std::cout << "a - b = " << atemp << std::endl; + else + std::cout << "a - b = (carry)" << std::endl; + + atemp = a; + if( !atemp.Mul(b) ) + std::cout << "a * b = " << atemp << std::endl; + else + std::cout << "a * b = (carry)" << std::endl; + + + atemp = a; + if( !atemp.Div(b) ) + std::cout << "a / b = " << atemp << std::endl; + else + std::cout << "a / b = (carry or division by zero) " << std::endl; + +} + + +int main() +{ +MyBig a,b; + + // conversion from 'const char *' + a = "123456.543456"; + b = "98767878.124322"; + + SimpleCalculating(a,b); + + // 'a' will have the max value which can be held in this type + a.SetMax(); + + // conversion from double + b = 456.32; + + // Look at the value 'a' and the product from a+b and a-b + // Don't worry this is the nature of floating point numbers + CalculatingWithCarry(a,b); +} + +/* +the result (the same on a 32 or 64bit platform): + +Simple calculating +a = 123456.543456 +b = 98767878.124322 +a + b = 98891334.667778 +a - b = -98644421.580866 +a * b = 12193540837712.270763536832 +a / b = 0.0012499665458095764605964485261668609133 +Calculating with a carry +a = 2.34953455457111777368832820909595050034e+2776511644261678604 +b = 456.3199999999999931787897367030382156 +a + b = 2.34953455457111777368832820909595050034e+2776511644261678604 +a - b = 2.34953455457111777368832820909595050034e+2776511644261678604 +a * b = (carry) +a / b = 5.1488748127873374141170361292780486452e+2776511644261678601 +*/ diff --git a/3rd_party/ttmath-0.9.3/samples/int.cpp b/3rd_party/ttmath-0.9.3/samples/int.cpp new file mode 100644 index 00000000..13760d28 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/samples/int.cpp @@ -0,0 +1,92 @@ +#include +#include + + +void SimpleCalculating(const ttmath::Int<2> & a, const ttmath::Int<2> & b) +{ + std::cout << "Simple calculating" << std::endl; + std::cout << "a = " << a << std::endl; + std::cout << "b = " << b << std::endl; + std::cout << "a + b = " << a+b << std::endl; + std::cout << "a - b = " << a-b << std::endl; + std::cout << "a * b = " << a*b << std::endl; + std::cout << "a / b = " << a/b << std::endl; +} + + +void CalculatingWithCarry(const ttmath::Int<2> & a, const ttmath::Int<2> & b) +{ +ttmath::Int<2> atemp; + + std::cout << "Calculating with a carry" << std::endl; + std::cout << "a = " << a << std::endl; + std::cout << "b = " << b << std::endl; + + atemp = a; + if( !atemp.Add(b) ) + std::cout << "a + b = " << atemp << std::endl; + else + std::cout << "a + b = (carry) " << atemp << std::endl; + + atemp = a; + if( !atemp.Sub(b) ) + std::cout << "a - b = " << atemp << std::endl; + else + std::cout << "a - b = (carry) " << atemp << std::endl; + + atemp = a; + if( !atemp.Mul(b) ) + std::cout << "a * b = " << atemp << std::endl; + else + std::cout << "a * b = (carry: the result is too big) " << std::endl; + // it have no sense to print 'atemp' (it's undefined) + + atemp = a; + if( !atemp.Div(b) ) + std::cout << "a / b = " << atemp << std::endl; + else + std::cout << "a / b = (division by zero) " << std::endl; + +} + +int main() +{ +// on 32bit platforms: 'a' and 'b' have 2-words (two 32bit words) +// it means a,b are from <-2^63, 2^63 - 1> +ttmath::Int<2> a,b; + + // conversion from int + a = 123456; + + // conversion from 'const char *' + b = "98767878"; + + SimpleCalculating(a,b); + + // 'a' will have the max value which can be held in this type + a.SetMax(); + + // conversion from 'int' + b = 10; + + CalculatingWithCarry(a,b); +} + +/* +the result (on 32 bit platform): + +Simple calculating +a = 123456 +b = 98767878 +a + b = 98891334 +a - b = -98644422 +a * b = 12193487146368 +a / b = 0 +Calculating with a carry +a = 9223372036854775807 +b = 10 +a + b = (carry) -9223372036854775799 +a - b = 9223372036854775797 +a * b = (carry) the result is too big) +a / b = 922337203685477580 +*/ diff --git a/3rd_party/ttmath-0.9.3/samples/parser.cpp b/3rd_party/ttmath-0.9.3/samples/parser.cpp new file mode 100644 index 00000000..6ee2e021 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/samples/parser.cpp @@ -0,0 +1,39 @@ +#include +#include + + +// for convenience we're defining MyBig type +// this type has 2 words for its mantissa and 1 word for its exponent +// (on a 32bit platform one word means a word of 32 bits, +// and on a 64bit platform one word means a word of 64 bits) +typedef ttmath::Big<1,2> MyBig; + + +int main() +{ +ttmath::Parser parser; + +// the sine function takes its parameter as being in radians, +// the product from the arcus tangent will be in radians as well +const char equation[] = " (34 + 24) * 123 - 34.32 ^ 6 * sin(2.56) - atan(10)"; + + ttmath::ErrorCode err = parser.Parse(equation); + + if( err == ttmath::err_ok ) + std::cout << parser.stack[0].value << std::endl; + else + std::cout << "Error: " + << static_cast(err) + << std::endl; +} + +/* +the result (on 32 bit platform): +-897705014.525731067 +*/ + + +/* +the result (on 64 bit platform): +-897705014.5257310676097719585259773124 +*/ diff --git a/3rd_party/ttmath-0.9.3/samples/uint.cpp b/3rd_party/ttmath-0.9.3/samples/uint.cpp new file mode 100644 index 00000000..f6b0df48 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/samples/uint.cpp @@ -0,0 +1,93 @@ +#include +#include + + +void SimpleCalculating(const ttmath::UInt<2> & a, const ttmath::UInt<2> & b) +{ + std::cout << "Simple calculating" << std::endl; + std::cout << "a = " << a << std::endl; + std::cout << "b = " << b << std::endl; + std::cout << "a + b = " << a+b << std::endl; + std::cout << "a - b = " << a-b << std::endl; + std::cout << "a * b = " << a*b << std::endl; + std::cout << "a / b = " << a/b << std::endl; +} + + +void CalculatingWithCarry(const ttmath::UInt<2> & a, const ttmath::UInt<2> & b) +{ +ttmath::UInt<2> atemp; + + std::cout << "Calculating with a carry" << std::endl; + std::cout << "a = " << a << std::endl; + std::cout << "b = " << b << std::endl; + + atemp = a; + if( !atemp.Add(b) ) + std::cout << "a + b = " << atemp << std::endl; + else + // if there was a carry then atemp.Add(...) would have returned 1 + std::cout << "a + b = (carry: the result is too big) " << atemp << std::endl; + + atemp = a; + if( !atemp.Sub(b) ) + std::cout << "a - b = " << atemp << std::endl; + else + std::cout << "a - b = (carry: 'a' was smaller than 'b') " << atemp << std::endl; + + atemp = a; + if( !atemp.Mul(b) ) + std::cout << "a * b = " << atemp << std::endl; + else + std::cout << "a * b = (carry: the result is too big) " << std::endl; + // it have no sense to print 'atemp' (it's undefined) + + atemp = a; + if( !atemp.Div(b) ) + std::cout << "a / b = " << atemp << std::endl; + else + std::cout << "a / b = (division by zero) " << std::endl; + +} + +int main() +{ +// on 32bit platforms: 'a' and 'b' have 2-words (two 32bit words) +// it means a,b are from <0, 2^64 - 1> +ttmath::UInt<2> a,b; + + // conversion from 'const char *' + a = "123456"; + + // conversion from int + b = 9876; + + SimpleCalculating(a,b); + + // 'a' will have the max value which can be held in this type + a.SetMax(); + + // conversion from 'int' + b = 5; + + CalculatingWithCarry(a,b); +} + +/* +the result (on 32 bit platform): + +Simple calculating +a = 123456 +b = 9876 +a + b = 133332 +a - b = 113580 +a * b = 1219251456 +a / b = 12 +Calculating with a carry +a = 18446744073709551615 +b = 5 +a + b = (carry: the result is too big) 4 +a - b = 18446744073709551610 +a * b = (carry: the result is too big) +a / b = 3689348814741910323 +*/ diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmath.h b/3rd_party/ttmath-0.9.3/ttmath/ttmath.h new file mode 100644 index 00000000..ee40e6ea --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmath.h @@ -0,0 +1,2853 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2012, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef headerfilettmathmathtt +#define headerfilettmathmathtt + +/*! + \file ttmath.h + \brief Mathematics functions. +*/ + +#ifdef _MSC_VER +//warning C4127: conditional expression is constant +#pragma warning( disable: 4127 ) +//warning C4702: unreachable code +#pragma warning( disable: 4702 ) +//warning C4800: forcing value to bool 'true' or 'false' (performance warning) +#pragma warning( disable: 4800 ) +#endif + + +#include "ttmathbig.h" +#include "ttmathobjects.h" + + +namespace ttmath +{ + /* + * + * functions defined here are used only with Big<> types + * + * + */ + + + /* + * + * functions for rounding + * + * + */ + + + /*! + this function skips the fraction from x + e.g 2.2 = 2 + 2.7 = 2 + -2.2 = 2 + -2.7 = 2 + */ + template + ValueType SkipFraction(const ValueType & x) + { + ValueType result( x ); + result.SkipFraction(); + + return result; + } + + + /*! + this function rounds to the nearest integer value + e.g 2.2 = 2 + 2.7 = 3 + -2.2 = -2 + -2.7 = -3 + */ + template + ValueType Round(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType result( x ); + uint c = result.Round(); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + + /*! + this function returns a value representing the smallest integer + that is greater than or equal to x + + Ceil(-3.7) = -3 + Ceil(-3.1) = -3 + Ceil(-3.0) = -3 + Ceil(4.0) = 4 + Ceil(4.2) = 5 + Ceil(4.8) = 5 + */ + template + ValueType Ceil(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType result(x); + uint c = 0; + + result.SkipFraction(); + + if( result != x ) + { + // x is with fraction + // if x is negative we don't have to do anything + if( !x.IsSign() ) + { + ValueType one; + one.SetOne(); + + c += result.Add(one); + } + } + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + this function returns a value representing the largest integer + that is less than or equal to x + + Floor(-3.6) = -4 + Floor(-3.1) = -4 + Floor(-3) = -3 + Floor(2) = 2 + Floor(2.3) = 2 + Floor(2.8) = 2 + */ + template + ValueType Floor(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType result(x); + uint c = 0; + + result.SkipFraction(); + + if( result != x ) + { + // x is with fraction + // if x is positive we don't have to do anything + if( x.IsSign() ) + { + ValueType one; + one.SetOne(); + + c += result.Sub(one); + } + } + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + + /* + * + * logarithms and the exponent + * + * + */ + + + /*! + this function calculates the natural logarithm (logarithm with the base 'e') + */ + template + ValueType Ln(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType result; + uint state = result.Ln(x); + + if( err ) + { + switch( state ) + { + case 0: + *err = err_ok; + break; + case 1: + *err = err_overflow; + break; + case 2: + *err = err_improper_argument; + break; + default: + *err = err_internal_error; + break; + } + } + + + return result; + } + + + /*! + this function calculates the logarithm + */ + template + ValueType Log(const ValueType & x, const ValueType & base, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) *err = err_improper_argument; + return x; + } + + if( base.IsNan() ) + { + if( err ) *err = err_improper_argument; + return base; + } + + ValueType result; + uint state = result.Log(x, base); + + if( err ) + { + switch( state ) + { + case 0: + *err = err_ok; + break; + case 1: + *err = err_overflow; + break; + case 2: + case 3: + *err = err_improper_argument; + break; + default: + *err = err_internal_error; + break; + } + } + + return result; + } + + + /*! + this function calculates the expression e^x + */ + template + ValueType Exp(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType result; + uint c = result.Exp(x); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + * + * trigonometric functions + * + */ + + + /* + this namespace consists of auxiliary functions + (something like 'private' in a class) + */ + namespace auxiliaryfunctions + { + + /*! + an auxiliary function for calculating the Sine + (you don't have to call this function) + */ + template + uint PrepareSin(ValueType & x, bool & change_sign) + { + ValueType temp; + + change_sign = false; + + if( x.IsSign() ) + { + // we're using the formula 'sin(-x) = -sin(x)' + change_sign = !change_sign; + x.ChangeSign(); + } + + // we're reducing the period 2*PI + // (for big values there'll always be zero) + temp.Set2Pi(); + + if( x.Mod(temp) ) + return 1; + + + // we're setting 'x' as being in the range of <0, 0.5PI> + + temp.SetPi(); + + if( x > temp ) + { + // x is in (pi, 2*pi> + x.Sub( temp ); + change_sign = !change_sign; + } + + temp.Set05Pi(); + + if( x > temp ) + { + // x is in (0.5pi, pi> + x.Sub( temp ); + x = temp - x; + } + + return 0; + } + + + /*! + an auxiliary function for calculating the Sine + (you don't have to call this function) + + it returns Sin(x) where 'x' is from <0, PI/2> + we're calculating the Sin with using Taylor series in zero or PI/2 + (depending on which point of these two points is nearer to the 'x') + + Taylor series: + sin(x) = sin(a) + cos(a)*(x-a)/(1!) + - sin(a)*((x-a)^2)/(2!) - cos(a)*((x-a)^3)/(3!) + + sin(a)*((x-a)^4)/(4!) + ... + + when a=0 it'll be: + sin(x) = (x)/(1!) - (x^3)/(3!) + (x^5)/(5!) - (x^7)/(7!) + (x^9)/(9!) ... + + and when a=PI/2: + sin(x) = 1 - ((x-PI/2)^2)/(2!) + ((x-PI/2)^4)/(4!) - ((x-PI/2)^6)/(6!) ... + */ + template + ValueType Sin0pi05(const ValueType & x) + { + ValueType result; + ValueType numerator, denominator; + ValueType d_numerator, d_denominator; + ValueType one, temp, old_result; + + // temp = pi/4 + temp.Set05Pi(); + temp.exponent.SubOne(); + + one.SetOne(); + + if( x < temp ) + { + // we're using the Taylor series with a=0 + result = x; + numerator = x; + denominator = one; + + // d_numerator = x^2 + d_numerator = x; + d_numerator.Mul(x); + + d_denominator = 2; + } + else + { + // we're using the Taylor series with a=PI/2 + result = one; + numerator = one; + denominator = one; + + // d_numerator = (x-pi/2)^2 + ValueType pi05; + pi05.Set05Pi(); + + temp = x; + temp.Sub( pi05 ); + d_numerator = temp; + d_numerator.Mul( temp ); + + d_denominator = one; + } + + uint c = 0; + bool addition = false; + + old_result = result; + for(uint i=1 ; i<=TTMATH_ARITHMETIC_MAX_LOOP ; ++i) + { + // we're starting from a second part of the formula + c += numerator. Mul( d_numerator ); + c += denominator. Mul( d_denominator ); + c += d_denominator.Add( one ); + c += denominator. Mul( d_denominator ); + c += d_denominator.Add( one ); + temp = numerator; + c += temp.Div(denominator); + + if( c ) + // Sin is from <-1,1> and cannot make an overflow + // but the carry can be from the Taylor series + // (then we only break our calculations) + break; + + if( addition ) + result.Add( temp ); + else + result.Sub( temp ); + + + addition = !addition; + + // we're testing whether the result has changed after adding + // the next part of the Taylor formula, if not we end the loop + // (it means 'x' is zero or 'x' is PI/2 or this part of the formula + // is too small) + if( result == old_result ) + break; + + old_result = result; + } + + return result; + } + + } // namespace auxiliaryfunctions + + + + /*! + this function calculates the Sine + */ + template + ValueType Sin(ValueType x, ErrorCode * err = 0) + { + using namespace auxiliaryfunctions; + + ValueType one, result; + bool change_sign; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + if( err ) + *err = err_ok; + + if( PrepareSin( x, change_sign ) ) + { + // x is too big, we cannnot reduce the 2*PI period + // prior to version 0.8.5 the result was zero + + // result has NaN flag set by default + + if( err ) + *err = err_overflow; // maybe another error code? err_improper_argument? + + return result; // NaN is set by default + } + + result = Sin0pi05( x ); + + one.SetOne(); + + // after calculations there can be small distortions in the result + if( result > one ) + result = one; + else + if( result.IsSign() ) + // we've calculated the sin from <0, pi/2> and the result + // should be positive + result.SetZero(); + + if( change_sign ) + result.ChangeSign(); + + return result; + } + + + /*! + this function calulates the Cosine + we're using the formula cos(x) = sin(x + PI/2) + */ + template + ValueType Cos(ValueType x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType pi05; + pi05.Set05Pi(); + + uint c = x.Add( pi05 ); + + if( c ) + { + if( err ) + *err = err_overflow; + + return ValueType(); // result is undefined (NaN is set by default) + } + + return Sin(x, err); + } + + + /*! + this function calulates the Tangent + we're using the formula tan(x) = sin(x) / cos(x) + + it takes more time than calculating the Tan directly + from for example Taylor series but should be a bit preciser + because Tan receives its values from -infinity to +infinity + and when we calculate it from any series then we can make + a greater mistake than calculating 'sin/cos' + */ + template + ValueType Tan(const ValueType & x, ErrorCode * err = 0) + { + ValueType result = Cos(x, err); + + if( err && *err != err_ok ) + return result; + + if( result.IsZero() ) + { + if( err ) + *err = err_improper_argument; + + result.SetNan(); + + return result; + } + + return Sin(x, err) / result; + } + + + /*! + this function calulates the Tangent + look at the description of Tan(...) + + (the abbreviation of Tangent can be 'tg' as well) + */ + template + ValueType Tg(const ValueType & x, ErrorCode * err = 0) + { + return Tan(x, err); + } + + + /*! + this function calulates the Cotangent + we're using the formula tan(x) = cos(x) / sin(x) + + (why do we make it in this way? + look at information in Tan() function) + */ + template + ValueType Cot(const ValueType & x, ErrorCode * err = 0) + { + ValueType result = Sin(x, err); + + if( err && *err != err_ok ) + return result; + + if( result.IsZero() ) + { + if( err ) + *err = err_improper_argument; + + result.SetNan(); + + return result; + } + + return Cos(x, err) / result; + } + + + /*! + this function calulates the Cotangent + look at the description of Cot(...) + + (the abbreviation of Cotangent can be 'ctg' as well) + */ + template + ValueType Ctg(const ValueType & x, ErrorCode * err = 0) + { + return Cot(x, err); + } + + + /* + * + * inverse trigonometric functions + * + * + */ + + namespace auxiliaryfunctions + { + + /*! + an auxiliary function for calculating the Arc Sine + + we're calculating asin from the following formula: + asin(x) = x + (1*x^3)/(2*3) + (1*3*x^5)/(2*4*5) + (1*3*5*x^7)/(2*4*6*7) + ... + where abs(x) <= 1 + + we're using this formula when x is from <0, 1/2> + */ + template + ValueType ASin_0(const ValueType & x) + { + ValueType nominator, denominator, nominator_add, nominator_x, denominator_add, denominator_x; + ValueType two, result(x), x2(x); + ValueType nominator_temp, denominator_temp, old_result = result; + uint c = 0; + + x2.Mul(x); + two = 2; + + nominator.SetOne(); + denominator = two; + nominator_add = nominator; + denominator_add = denominator; + nominator_x = x; + denominator_x = 3; + + for(uint i=1 ; i<=TTMATH_ARITHMETIC_MAX_LOOP ; ++i) + { + c += nominator_x.Mul(x2); + nominator_temp = nominator_x; + c += nominator_temp.Mul(nominator); + denominator_temp = denominator; + c += denominator_temp.Mul(denominator_x); + c += nominator_temp.Div(denominator_temp); + + // if there is a carry somewhere we only break the calculating + // the result should be ok -- it's from <-pi/2, pi/2> + if( c ) + break; + + result.Add(nominator_temp); + + if( result == old_result ) + // there's no sense to calculate more + break; + + old_result = result; + + + c += nominator_add.Add(two); + c += denominator_add.Add(two); + c += nominator.Mul(nominator_add); + c += denominator.Mul(denominator_add); + c += denominator_x.Add(two); + } + + return result; + } + + + + /*! + an auxiliary function for calculating the Arc Sine + + we're calculating asin from the following formula: + asin(x) = pi/2 - sqrt(2)*sqrt(1-x) * asin_temp + asin_temp = 1 + (1*(1-x))/((2*3)*(2)) + (1*3*(1-x)^2)/((2*4*5)*(4)) + (1*3*5*(1-x)^3)/((2*4*6*7)*(8)) + ... + + where abs(x) <= 1 + + we're using this formula when x is from (1/2, 1> + */ + template + ValueType ASin_1(const ValueType & x) + { + ValueType nominator, denominator, nominator_add, nominator_x, nominator_x_add, denominator_add, denominator_x; + ValueType denominator2; + ValueType one, two, result; + ValueType nominator_temp, denominator_temp, old_result; + uint c = 0; + + two = 2; + + one.SetOne(); + nominator = one; + result = one; + old_result = result; + denominator = two; + nominator_add = nominator; + denominator_add = denominator; + nominator_x = one; + nominator_x.Sub(x); + nominator_x_add = nominator_x; + denominator_x = 3; + denominator2 = two; + + + for(uint i=1 ; i<=TTMATH_ARITHMETIC_MAX_LOOP ; ++i) + { + nominator_temp = nominator_x; + c += nominator_temp.Mul(nominator); + denominator_temp = denominator; + c += denominator_temp.Mul(denominator_x); + c += denominator_temp.Mul(denominator2); + c += nominator_temp.Div(denominator_temp); + + // if there is a carry somewhere we only break the calculating + // the result should be ok -- it's from <-pi/2, pi/2> + if( c ) + break; + + result.Add(nominator_temp); + + if( result == old_result ) + // there's no sense to calculate more + break; + + old_result = result; + + c += nominator_x.Mul(nominator_x_add); + c += nominator_add.Add(two); + c += denominator_add.Add(two); + c += nominator.Mul(nominator_add); + c += denominator.Mul(denominator_add); + c += denominator_x.Add(two); + c += denominator2.Mul(two); + } + + + nominator_x_add.exponent.AddOne(); // *2 + one.exponent.SubOne(); // =0.5 + nominator_x_add.Pow(one); // =sqrt(nominator_x_add) + result.Mul(nominator_x_add); + + one.Set05Pi(); + one.Sub(result); + + return one; + } + + + } // namespace auxiliaryfunctions + + + /*! + this function calculates the Arc Sine + x is from <-1,1> + */ + template + ValueType ASin(ValueType x, ErrorCode * err = 0) + { + using namespace auxiliaryfunctions; + + ValueType result, one; + one.SetOne(); + bool change_sign = false; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + if( x.GreaterWithoutSignThan(one) ) + { + if( err ) + *err = err_improper_argument; + + return result; // NaN is set by default + } + + if( x.IsSign() ) + { + change_sign = true; + x.Abs(); + } + + one.exponent.SubOne(); // =0.5 + + // asin(-x) = -asin(x) + if( x.GreaterWithoutSignThan(one) ) + result = ASin_1(x); + else + result = ASin_0(x); + + if( change_sign ) + result.ChangeSign(); + + if( err ) + *err = err_ok; + + return result; + } + + + /*! + this function calculates the Arc Cosine + + we're using the formula: + acos(x) = pi/2 - asin(x) + */ + template + ValueType ACos(const ValueType & x, ErrorCode * err = 0) + { + ValueType temp; + + temp.Set05Pi(); + temp.Sub(ASin(x, err)); + + return temp; + } + + + + namespace auxiliaryfunctions + { + + /*! + an auxiliary function for calculating the Arc Tangent + + arc tan (x) where x is in <0; 0.5) + (x can be in (-0.5 ; 0.5) too) + + we're using the Taylor series expanded in zero: + atan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + ... + */ + template + ValueType ATan0(const ValueType & x) + { + ValueType nominator, denominator, nominator_add, denominator_add, temp; + ValueType result, old_result; + bool adding = false; + uint c = 0; + + result = x; + old_result = result; + nominator = x; + nominator_add = x; + nominator_add.Mul(x); + + denominator.SetOne(); + denominator_add = 2; + + for(uint i=1 ; i<=TTMATH_ARITHMETIC_MAX_LOOP ; ++i) + { + c += nominator.Mul(nominator_add); + c += denominator.Add(denominator_add); + + temp = nominator; + c += temp.Div(denominator); + + if( c ) + // the result should be ok + break; + + if( adding ) + result.Add(temp); + else + result.Sub(temp); + + if( result == old_result ) + // there's no sense to calculate more + break; + + old_result = result; + adding = !adding; + } + + return result; + } + + + /*! + an auxiliary function for calculating the Arc Tangent + + where x is in <0 ; 1> + */ + template + ValueType ATan01(const ValueType & x) + { + ValueType half; + half.Set05(); + + /* + it would be better if we chose about sqrt(2)-1=0.41... instead of 0.5 here + + because as you can see below: + when x = sqrt(2)-1 + abs(x) = abs( (x-1)/(1+x) ) + so when we're calculating values around x + then they will be better converged to each other + + for example if we have x=0.4999 then during calculating ATan0(0.4999) + we have to make about 141 iterations but when we have x=0.5 + then during calculating ATan0( (x-1)/(1+x) ) we have to make + only about 89 iterations (both for Big<3,9>) + + in the future this 0.5 can be changed + */ + if( x.SmallerWithoutSignThan(half) ) + return ATan0(x); + + + /* + x>=0.5 and x<=1 + (x can be even smaller than 0.5) + + y = atac(x) + x = tan(y) + + tan(y-b) = (tan(y)-tab(b)) / (1+tan(y)*tan(b)) + y-b = atan( (tan(y)-tab(b)) / (1+tan(y)*tan(b)) ) + y = b + atan( (x-tab(b)) / (1+x*tan(b)) ) + + let b = pi/4 + tan(b) = tan(pi/4) = 1 + y = pi/4 + atan( (x-1)/(1+x) ) + + so + atac(x) = pi/4 + atan( (x-1)/(1+x) ) + when x->1 (x converges to 1) the (x-1)/(1+x) -> 0 + and we can use ATan0() function here + */ + + ValueType n(x),d(x),one,result; + + one.SetOne(); + n.Sub(one); + d.Add(one); + n.Div(d); + + result = ATan0(n); + + n.Set05Pi(); + n.exponent.SubOne(); // =pi/4 + result.Add(n); + + return result; + } + + + /*! + an auxiliary function for calculating the Arc Tangent + where x > 1 + + we're using the formula: + atan(x) = pi/2 - atan(1/x) for x>0 + */ + template + ValueType ATanGreaterThanPlusOne(const ValueType & x) + { + ValueType temp, atan; + + temp.SetOne(); + + if( temp.Div(x) ) + { + // if there was a carry here that means x is very big + // and atan(1/x) fast converged to 0 + atan.SetZero(); + } + else + atan = ATan01(temp); + + temp.Set05Pi(); + temp.Sub(atan); + + return temp; + } + + } // namespace auxiliaryfunctions + + + /*! + this function calculates the Arc Tangent + */ + template + ValueType ATan(ValueType x) + { + using namespace auxiliaryfunctions; + + ValueType one, result; + one.SetOne(); + bool change_sign = false; + + if( x.IsNan() ) + return x; + + // if x is negative we're using the formula: + // atan(-x) = -atan(x) + if( x.IsSign() ) + { + change_sign = true; + x.Abs(); + } + + if( x.GreaterWithoutSignThan(one) ) + result = ATanGreaterThanPlusOne(x); + else + result = ATan01(x); + + if( change_sign ) + result.ChangeSign(); + + return result; + } + + + /*! + this function calculates the Arc Tangent + look at the description of ATan(...) + + (the abbreviation of Arc Tangent can be 'atg' as well) + */ + template + ValueType ATg(const ValueType & x) + { + return ATan(x); + } + + + /*! + this function calculates the Arc Cotangent + + we're using the formula: + actan(x) = pi/2 - atan(x) + */ + template + ValueType ACot(const ValueType & x) + { + ValueType result; + + result.Set05Pi(); + result.Sub(ATan(x)); + + return result; + } + + + /*! + this function calculates the Arc Cotangent + look at the description of ACot(...) + + (the abbreviation of Arc Cotangent can be 'actg' as well) + */ + template + ValueType ACtg(const ValueType & x) + { + return ACot(x); + } + + + /* + * + * hyperbolic functions + * + * + */ + + + /*! + this function calculates the Hyperbolic Sine + + we're using the formula sinh(x)= ( e^x - e^(-x) ) / 2 + */ + template + ValueType Sinh(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType ex, emx; + uint c = 0; + + c += ex.Exp(x); + c += emx.Exp(-x); + + c += ex.Sub(emx); + c += ex.exponent.SubOne(); + + if( err ) + *err = c ? err_overflow : err_ok; + + return ex; + } + + + /*! + this function calculates the Hyperbolic Cosine + + we're using the formula cosh(x)= ( e^x + e^(-x) ) / 2 + */ + template + ValueType Cosh(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType ex, emx; + uint c = 0; + + c += ex.Exp(x); + c += emx.Exp(-x); + + c += ex.Add(emx); + c += ex.exponent.SubOne(); + + if( err ) + *err = c ? err_overflow : err_ok; + + return ex; + } + + + /*! + this function calculates the Hyperbolic Tangent + + we're using the formula tanh(x)= ( e^x - e^(-x) ) / ( e^x + e^(-x) ) + */ + template + ValueType Tanh(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType ex, emx, nominator, denominator; + uint c = 0; + + c += ex.Exp(x); + c += emx.Exp(-x); + + nominator = ex; + c += nominator.Sub(emx); + denominator = ex; + c += denominator.Add(emx); + + c += nominator.Div(denominator); + + if( err ) + *err = c ? err_overflow : err_ok; + + return nominator; + } + + + /*! + this function calculates the Hyperbolic Tangent + look at the description of Tanh(...) + + (the abbreviation of Hyperbolic Tangent can be 'tgh' as well) + */ + template + ValueType Tgh(const ValueType & x, ErrorCode * err = 0) + { + return Tanh(x, err); + } + + /*! + this function calculates the Hyperbolic Cotangent + + we're using the formula coth(x)= ( e^x + e^(-x) ) / ( e^x - e^(-x) ) + */ + template + ValueType Coth(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + if( x.IsZero() ) + { + if( err ) + *err = err_improper_argument; + + return ValueType(); // NaN is set by default + } + + ValueType ex, emx, nominator, denominator; + uint c = 0; + + c += ex.Exp(x); + c += emx.Exp(-x); + + nominator = ex; + c += nominator.Add(emx); + denominator = ex; + c += denominator.Sub(emx); + + c += nominator.Div(denominator); + + if( err ) + *err = c ? err_overflow : err_ok; + + return nominator; + } + + + /*! + this function calculates the Hyperbolic Cotangent + look at the description of Coth(...) + + (the abbreviation of Hyperbolic Cotangent can be 'ctgh' as well) + */ + template + ValueType Ctgh(const ValueType & x, ErrorCode * err = 0) + { + return Coth(x, err); + } + + + /* + * + * inverse hyperbolic functions + * + * + */ + + + /*! + inverse hyperbolic sine + + asinh(x) = ln( x + sqrt(x^2 + 1) ) + */ + template + ValueType ASinh(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType xx(x), one, result; + uint c = 0; + one.SetOne(); + + c += xx.Mul(x); + c += xx.Add(one); + one.exponent.SubOne(); // one=0.5 + // xx is >= 1 + c += xx.PowFrac(one); // xx=sqrt(xx) + c += xx.Add(x); + c += result.Ln(xx); // xx > 0 + + // here can only be a carry + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + inverse hyperbolic cosine + + acosh(x) = ln( x + sqrt(x^2 - 1) ) x in <1, infinity) + */ + template + ValueType ACosh(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType xx(x), one, result; + uint c = 0; + one.SetOne(); + + if( x < one ) + { + if( err ) + *err = err_improper_argument; + + return result; // NaN is set by default + } + + c += xx.Mul(x); + c += xx.Sub(one); + // xx is >= 0 + // we can't call a PowFrac when the 'x' is zero + // if x is 0 the sqrt(0) is 0 + if( !xx.IsZero() ) + { + one.exponent.SubOne(); // one=0.5 + c += xx.PowFrac(one); // xx=sqrt(xx) + } + c += xx.Add(x); + c += result.Ln(xx); // xx >= 1 + + // here can only be a carry + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + inverse hyperbolic tangent + + atanh(x) = 0.5 * ln( (1+x) / (1-x) ) x in (-1, 1) + */ + template + ValueType ATanh(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType nominator(x), denominator, one, result; + uint c = 0; + one.SetOne(); + + if( !x.SmallerWithoutSignThan(one) ) + { + if( err ) + *err = err_improper_argument; + + return result; // NaN is set by default + } + + c += nominator.Add(one); + denominator = one; + c += denominator.Sub(x); + c += nominator.Div(denominator); + c += result.Ln(nominator); + c += result.exponent.SubOne(); + + // here can only be a carry + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + inverse hyperbolic tantent + */ + template + ValueType ATgh(const ValueType & x, ErrorCode * err = 0) + { + return ATanh(x, err); + } + + + /*! + inverse hyperbolic cotangent + + acoth(x) = 0.5 * ln( (x+1) / (x-1) ) x in (-infinity, -1) or (1, infinity) + */ + template + ValueType ACoth(const ValueType & x, ErrorCode * err = 0) + { + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; // NaN + } + + ValueType nominator(x), denominator(x), one, result; + uint c = 0; + one.SetOne(); + + if( !x.GreaterWithoutSignThan(one) ) + { + if( err ) + *err = err_improper_argument; + + return result; // NaN is set by default + } + + c += nominator.Add(one); + c += denominator.Sub(one); + c += nominator.Div(denominator); + c += result.Ln(nominator); + c += result.exponent.SubOne(); + + // here can only be a carry + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + inverse hyperbolic cotantent + */ + template + ValueType ACtgh(const ValueType & x, ErrorCode * err = 0) + { + return ACoth(x, err); + } + + + + + + /* + * + * functions for converting between degrees, radians and gradians + * + * + */ + + + /*! + this function converts degrees to radians + + it returns: x * pi / 180 + */ + template + ValueType DegToRad(const ValueType & x, ErrorCode * err = 0) + { + ValueType result, temp; + uint c = 0; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + result = x; + + // it is better to make division first and then multiplication + // the result is more accurate especially when x is: 90,180,270 or 360 + temp = 180; + c += result.Div(temp); + + temp.SetPi(); + c += result.Mul(temp); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + this function converts radians to degrees + + it returns: x * 180 / pi + */ + template + ValueType RadToDeg(const ValueType & x, ErrorCode * err = 0) + { + ValueType result, delimiter; + uint c = 0; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + result = 180; + c += result.Mul(x); + + delimiter.SetPi(); + c += result.Div(delimiter); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + this function converts degrees in the long format into one value + + long format: (degrees, minutes, seconds) + minutes and seconds must be greater than or equal zero + + result: + if d>=0 : result= d + ((s/60)+m)/60 + if d<0 : result= d - ((s/60)+m)/60 + + ((s/60)+m)/60 = (s+60*m)/3600 (second version is faster because + there's only one division) + + for example: + DegToDeg(10, 30, 0) = 10.5 + DegToDeg(10, 24, 35.6)=10.4098(8) + */ + template + ValueType DegToDeg( const ValueType & d, const ValueType & m, const ValueType & s, + ErrorCode * err = 0) + { + ValueType delimiter, multipler; + uint c = 0; + + if( d.IsNan() || m.IsNan() || s.IsNan() || m.IsSign() || s.IsSign() ) + { + if( err ) + *err = err_improper_argument; + + delimiter.SetZeroNan(); // not needed, only to get rid of GCC warning about an uninitialized variable + + return delimiter; + } + + multipler = 60; + delimiter = 3600; + + c += multipler.Mul(m); + c += multipler.Add(s); + c += multipler.Div(delimiter); + + if( d.IsSign() ) + multipler.ChangeSign(); + + c += multipler.Add(d); + + if( err ) + *err = c ? err_overflow : err_ok; + + return multipler; + } + + + /*! + this function converts degrees in the long format to radians + */ + template + ValueType DegToRad( const ValueType & d, const ValueType & m, const ValueType & s, + ErrorCode * err = 0) + { + ValueType temp_deg = DegToDeg(d,m,s,err); + + if( err && *err!=err_ok ) + return temp_deg; + + return DegToRad(temp_deg, err); + } + + + /*! + this function converts gradians to radians + + it returns: x * pi / 200 + */ + template + ValueType GradToRad(const ValueType & x, ErrorCode * err = 0) + { + ValueType result, temp; + uint c = 0; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + result = x; + + // it is better to make division first and then multiplication + // the result is more accurate especially when x is: 100,200,300 or 400 + temp = 200; + c += result.Div(temp); + + temp.SetPi(); + c += result.Mul(temp); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + this function converts radians to gradians + + it returns: x * 200 / pi + */ + template + ValueType RadToGrad(const ValueType & x, ErrorCode * err = 0) + { + ValueType result, delimiter; + uint c = 0; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + result = 200; + c += result.Mul(x); + + delimiter.SetPi(); + c += result.Div(delimiter); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + this function converts degrees to gradians + + it returns: x * 200 / 180 + */ + template + ValueType DegToGrad(const ValueType & x, ErrorCode * err = 0) + { + ValueType result, temp; + uint c = 0; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + result = x; + + temp = 200; + c += result.Mul(temp); + + temp = 180; + c += result.Div(temp); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + /*! + this function converts degrees in the long format to gradians + */ + template + ValueType DegToGrad( const ValueType & d, const ValueType & m, const ValueType & s, + ErrorCode * err = 0) + { + ValueType temp_deg = DegToDeg(d,m,s,err); + + if( err && *err!=err_ok ) + return temp_deg; + + return DegToGrad(temp_deg, err); + } + + + /*! + this function converts degrees to gradians + + it returns: x * 180 / 200 + */ + template + ValueType GradToDeg(const ValueType & x, ErrorCode * err = 0) + { + ValueType result, temp; + uint c = 0; + + if( x.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return x; + } + + result = x; + + temp = 180; + c += result.Mul(temp); + + temp = 200; + c += result.Div(temp); + + if( err ) + *err = c ? err_overflow : err_ok; + + return result; + } + + + + + /* + * + * another functions + * + * + */ + + + /*! + this function calculates the square root + + Sqrt(9) = 3 + */ + template + ValueType Sqrt(ValueType x, ErrorCode * err = 0) + { + if( x.IsNan() || x.IsSign() ) + { + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return x; + } + + uint c = x.Sqrt(); + + if( err ) + *err = c ? err_overflow : err_ok; + + return x; + } + + + + namespace auxiliaryfunctions + { + + template + bool RootCheckIndexSign(ValueType & x, const ValueType & index, ErrorCode * err) + { + if( index.IsSign() ) + { + // index cannot be negative + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return true; + } + + return false; + } + + + template + bool RootCheckIndexZero(ValueType & x, const ValueType & index, ErrorCode * err) + { + if( index.IsZero() ) + { + if( x.IsZero() ) + { + // there isn't root(0;0) - we assume it's not defined + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return true; + } + + // root(x;0) is 1 (if x!=0) + x.SetOne(); + + if( err ) + *err = err_ok; + + return true; + } + + return false; + } + + + template + bool RootCheckIndexOne(const ValueType & index, ErrorCode * err) + { + ValueType one; + one.SetOne(); + + if( index == one ) + { + //root(x;1) is x + // we do it because if we used the PowFrac function + // we would lose the precision + if( err ) + *err = err_ok; + + return true; + } + + return false; + } + + + template + bool RootCheckIndexTwo(ValueType & x, const ValueType & index, ErrorCode * err) + { + if( index == 2 ) + { + x = Sqrt(x, err); + + return true; + } + + return false; + } + + + template + bool RootCheckIndexFrac(ValueType & x, const ValueType & index, ErrorCode * err) + { + if( !index.IsInteger() ) + { + // index must be integer + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return true; + } + + return false; + } + + + template + bool RootCheckXZero(ValueType & x, ErrorCode * err) + { + if( x.IsZero() ) + { + // root(0;index) is zero (if index!=0) + // RootCheckIndexZero() must be called beforehand + x.SetZero(); + + if( err ) + *err = err_ok; + + return true; + } + + return false; + } + + + template + bool RootCheckIndex(ValueType & x, const ValueType & index, ErrorCode * err, bool * change_sign) + { + *change_sign = false; + + if( index.Mod2() ) + { + // index is odd (1,3,5...) + if( x.IsSign() ) + { + *change_sign = true; + x.Abs(); + } + } + else + { + // index is even + // x cannot be negative + if( x.IsSign() ) + { + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return true; + } + } + + return false; + } + + + template + uint RootCorrectInteger(ValueType & old_x, ValueType & x, const ValueType & index) + { + if( !old_x.IsInteger() || x.IsInteger() || !index.exponent.IsSign() ) + return 0; + + // old_x is integer, + // x is not integer, + // index is relatively small (index.exponent<0 or index.exponent<=0) + // (because we're using a special powering algorithm Big::PowUInt()) + + uint c = 0; + + ValueType temp(x); + c += temp.Round(); + + ValueType temp_round(temp); + c += temp.PowUInt(index); + + if( temp == old_x ) + x = temp_round; + + return (c==0)? 0 : 1; + } + + + + } // namespace auxiliaryfunctions + + + + /*! + indexth Root of x + index must be integer and not negative <0;1;2;3....) + + if index==0 the result is one + if x==0 the result is zero and we assume root(0;0) is not defined + + if index is even (2;4;6...) the result is x^(1/index) and x>0 + if index is odd (1;2;3;...) the result is either + -(abs(x)^(1/index)) if x<0 or + x^(1/index)) if x>0 + + (for index==1 the result is equal x) + */ + template + ValueType Root(ValueType x, const ValueType & index, ErrorCode * err = 0) + { + using namespace auxiliaryfunctions; + + if( x.IsNan() || index.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return x; + } + + if( RootCheckIndexSign(x, index, err) ) return x; + if( RootCheckIndexZero(x, index, err) ) return x; + if( RootCheckIndexOne ( index, err) ) return x; + if( RootCheckIndexTwo (x, index, err) ) return x; + if( RootCheckIndexFrac(x, index, err) ) return x; + if( RootCheckXZero (x, err) ) return x; + + // index integer and index!=0 + // x!=0 + + ValueType old_x(x); + bool change_sign; + + if( RootCheckIndex(x, index, err, &change_sign ) ) return x; + + ValueType temp; + uint c = 0; + + // we're using the formula: root(x ; n) = exp( ln(x) / n ) + c += temp.Ln(x); + c += temp.Div(index); + c += x.Exp(temp); + + if( change_sign ) + { + // x is different from zero + x.SetSign(); + } + + c += RootCorrectInteger(old_x, x, index); + + if( err ) + *err = c ? err_overflow : err_ok; + + return x; + } + + + + /*! + absolute value of x + e.g. -2 = 2 + 2 = 2 + */ + template + ValueType Abs(const ValueType & x) + { + ValueType result( x ); + result.Abs(); + + return result; + } + + + /*! + it returns the sign of the value + e.g. -2 = -1 + 0 = 0 + 10 = 1 + */ + template + ValueType Sgn(ValueType x) + { + x.Sgn(); + + return x; + } + + + /*! + the remainder from a division + + e.g. + mod( 12.6 ; 3) = 0.6 because 12.6 = 3*4 + 0.6 + mod(-12.6 ; 3) = -0.6 bacause -12.6 = 3*(-4) + (-0.6) + mod( 12.6 ; -3) = 0.6 + mod(-12.6 ; -3) = -0.6 + */ + template + ValueType Mod(ValueType a, const ValueType & b, ErrorCode * err = 0) + { + if( a.IsNan() || b.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + a.SetNan(); + + return a; + } + + uint c = a.Mod(b); + + if( err ) + *err = c ? err_overflow : err_ok; + + return a; + } + + + + namespace auxiliaryfunctions + { + + /*! + this function is used to store factorials in a given container + 'more' means how many values should be added at the end + + e.g. + std::vector fact; + SetFactorialSequence(fact, 3); + // now the container has three values: 1 1 2 + + SetFactorialSequence(fact, 2); + // now the container has five values: 1 1 2 6 24 + */ + template + void SetFactorialSequence(std::vector & fact, uint more = 20) + { + if( more == 0 ) + more = 1; + + uint start = static_cast(fact.size()); + fact.resize(fact.size() + more); + + if( start == 0 ) + { + fact[0] = 1; + ++start; + } + + for(uint i=start ; i + ValueType SetBernoulliNumbersSum(CGamma & cgamma, const ValueType & n_, uint m, + const volatile StopCalculating * stop = 0) + { + ValueType k_, temp, temp2, temp3, sum; + + sum.SetZero(); + + for(uint k=0 ; kWasStopSignal() ) + return ValueType(); // NaN + + if( k>1 && (k & 1) == 1 ) // for that k the Bernoulli number is zero + continue; + + k_ = k; + + temp = n_; // n_ is equal 2 + temp.Pow(k_); + // temp = 2^k + + temp2 = cgamma.fact[m]; + temp3 = cgamma.fact[k]; + temp3.Mul(cgamma.fact[m-k]); + temp2.Div(temp3); + // temp2 = (m k) = m! / ( k! * (m-k)! ) + + temp.Mul(temp2); + temp.Mul(cgamma.bern[k]); + + sum.Add(temp); + // sum += 2^k * (m k) * B(k) + + if( sum.IsNan() ) + break; + } + + return sum; + } + + + /*! + an auxiliary function used to calculate Bernoulli numbers + start is >= 2 + + we use the recurrence formula: + B(m) = 1 / (2*(1 - 2^m)) * sum(m) + where sum(m) is calculated by SetBernoulliNumbersSum() + */ + template + bool SetBernoulliNumbersMore(CGamma & cgamma, uint start, const volatile StopCalculating * stop = 0) + { + ValueType denominator, temp, temp2, temp3, m_, sum, sum2, n_, k_; + + const uint n = 2; + n_ = n; + + // start is >= 2 + for(uint m=start ; mWasStopSignal() ) + { + cgamma.bern.resize(m); // valid numbers are in [0, m-1] + return false; + } + + cgamma.bern[m].Div(denominator); + } + } + + return true; + } + + + /*! + this function is used to calculate Bernoulli numbers, + returns false if there was a stop signal, + 'more' means how many values should be added at the end + + e.g. + typedef Big<1,2> MyBig; + CGamma cgamma; + SetBernoulliNumbers(cgamma, 3); + // now we have three first Bernoulli numbers: 1 -0.5 0.16667 + + SetBernoulliNumbers(cgamma, 4); + // now we have 7 Bernoulli numbers: 1 -0.5 0.16667 0 -0.0333 0 0.0238 + */ + template + bool SetBernoulliNumbers(CGamma & cgamma, uint more = 20, const volatile StopCalculating * stop = 0) + { + if( more == 0 ) + more = 1; + + uint start = static_cast(cgamma.bern.size()); + cgamma.bern.resize(cgamma.bern.size() + more); + + if( start == 0 ) + { + cgamma.bern[0].SetOne(); + ++start; + } + + if( cgamma.bern.size() == 1 ) + return true; + + if( start == 1 ) + { + cgamma.bern[1].Set05(); + cgamma.bern[1].ChangeSign(); + ++start; + } + + // we should have sufficient factorials in cgamma.fact + if( cgamma.fact.size() < cgamma.bern.size() ) + SetFactorialSequence(cgamma.fact, static_cast(cgamma.bern.size() - cgamma.fact.size())); + + + return SetBernoulliNumbersMore(cgamma, start, stop); + } + + + /*! + an auxiliary function used to calculate the Gamma() function + + we calculate a sum: + sum(n) = sum_{m=2} { B(m) / ( (m^2 - m) * n^(m-1) ) } = 1/(12*n) - 1/(360*n^3) + 1/(1260*n^5) + ... + B(m) means a mth Bernoulli number + the sum starts from m=2, we calculate as long as the value will not change after adding a next part + */ + template + ValueType GammaFactorialHighSum(const ValueType & n, CGamma & cgamma, ErrorCode & err, + const volatile StopCalculating * stop) + { + ValueType temp, temp2, denominator, sum, oldsum; + + sum.SetZero(); + + for(uint m=2 ; mWasStopSignal() ) + { + err = err_interrupt; + return ValueType(); // NaN + } + + temp = (m-1); + denominator = n; + denominator.Pow(temp); + // denominator = n ^ (m-1) + + temp = m; + temp2 = temp; + temp.Mul(temp2); + temp.Sub(temp2); + // temp = m^2 - m + + denominator.Mul(temp); + // denominator = (m^2 - m) * n ^ (m-1) + + if( m >= cgamma.bern.size() ) + { + if( !SetBernoulliNumbers(cgamma, m - cgamma.bern.size() + 1 + 3, stop) ) // 3 more than needed + { + // there was the stop signal + err = err_interrupt; + return ValueType(); // NaN + } + } + + temp = cgamma.bern[m]; + temp.Div(denominator); + + oldsum = sum; + sum.Add(temp); + + if( sum.IsNan() || oldsum==sum ) + break; + } + + return sum; + } + + + /*! + an auxiliary function used to calculate the Gamma() function + + we calculate a helper function GammaFactorialHigh() by using Stirling's series: + n! = (n/e)^n * sqrt(2*pi*n) * exp( sum(n) ) + where n is a real number (not only an integer) and is sufficient large (greater than TTMATH_GAMMA_BOUNDARY) + and sum(n) is calculated by GammaFactorialHighSum() + */ + template + ValueType GammaFactorialHigh(const ValueType & n, CGamma & cgamma, ErrorCode & err, + const volatile StopCalculating * stop) + { + ValueType temp, temp2, temp3, denominator, sum; + + temp.Set2Pi(); + temp.Mul(n); + temp2 = Sqrt(temp); + // temp2 = sqrt(2*pi*n) + + temp = n; + temp3.SetE(); + temp.Div(temp3); + temp.Pow(n); + // temp = (n/e)^n + + sum = GammaFactorialHighSum(n, cgamma, err, stop); + temp3.Exp(sum); + // temp3 = exp(sum) + + temp.Mul(temp2); + temp.Mul(temp3); + + return temp; + } + + + /*! + an auxiliary function used to calculate the Gamma() function + + Gamma(x) = GammaFactorialHigh(x-1) + */ + template + ValueType GammaPlusHigh(ValueType n, CGamma & cgamma, ErrorCode & err, const volatile StopCalculating * stop) + { + ValueType one; + + one.SetOne(); + n.Sub(one); + + return GammaFactorialHigh(n, cgamma, err, stop); + } + + + /*! + an auxiliary function used to calculate the Gamma() function + + we use this function when n is integer and a small value (from 0 to TTMATH_GAMMA_BOUNDARY] + we use the formula: + gamma(n) = (n-1)! = 1 * 2 * 3 * ... * (n-1) + */ + template + ValueType GammaPlusLowIntegerInt(uint n, CGamma & cgamma) + { + TTMATH_ASSERT( n > 0 ) + + if( n - 1 < static_cast(cgamma.fact.size()) ) + return cgamma.fact[n - 1]; + + ValueType res; + uint start = 2; + + if( cgamma.fact.size() < 2 ) + { + res.SetOne(); + } + else + { + start = static_cast(cgamma.fact.size()); + res = cgamma.fact[start-1]; + } + + for(uint i=start ; i + ValueType GammaPlusLowInteger(const ValueType & n, CGamma & cgamma) + { + sint n_; + + n.ToInt(n_); + + return GammaPlusLowIntegerInt(n_, cgamma); + } + + + /*! + an auxiliary function used to calculate the Gamma() function + + we use this function when n is a small value (from 0 to TTMATH_GAMMA_BOUNDARY] + we use a recurrence formula: + gamma(z+1) = z * gamma(z) + then: gamma(z) = gamma(z+1) / z + + e.g. + gamma(3.89) = gamma(2001.89) / ( 3.89 * 4.89 * 5.89 * ... * 1999.89 * 2000.89 ) + */ + template + ValueType GammaPlusLow(ValueType n, CGamma & cgamma, ErrorCode & err, const volatile StopCalculating * stop) + { + ValueType one, denominator, temp, boundary; + + if( n.IsInteger() ) + return GammaPlusLowInteger(n, cgamma); + + one.SetOne(); + denominator = n; + boundary = TTMATH_GAMMA_BOUNDARY; + + while( n < boundary ) + { + n.Add(one); + denominator.Mul(n); + } + + n.Add(one); + + // now n is sufficient big + temp = GammaPlusHigh(n, cgamma, err, stop); + temp.Div(denominator); + + return temp; + } + + + /*! + an auxiliary function used to calculate the Gamma() function + */ + template + ValueType GammaPlus(const ValueType & n, CGamma & cgamma, ErrorCode & err, const volatile StopCalculating * stop) + { + if( n > TTMATH_GAMMA_BOUNDARY ) + return GammaPlusHigh(n, cgamma, err, stop); + + return GammaPlusLow(n, cgamma, err, stop); + } + + + /*! + an auxiliary function used to calculate the Gamma() function + + this function is used when n is negative + we use the reflection formula: + gamma(1-z) * gamma(z) = pi / sin(pi*z) + then: gamma(z) = pi / (sin(pi*z) * gamma(1-z)) + + */ + template + ValueType GammaMinus(const ValueType & n, CGamma & cgamma, ErrorCode & err, const volatile StopCalculating * stop) + { + ValueType pi, denominator, temp, temp2; + + if( n.IsInteger() ) + { + // gamma function is not defined when n is negative and integer + err = err_improper_argument; + return temp; // NaN + } + + pi.SetPi(); + + temp = pi; + temp.Mul(n); + temp2 = Sin(temp); + // temp2 = sin(pi * n) + + temp.SetOne(); + temp.Sub(n); + temp = GammaPlus(temp, cgamma, err, stop); + // temp = gamma(1 - n) + + temp.Mul(temp2); + pi.Div(temp); + + return pi; + } + + } // namespace auxiliaryfunctions + + + + /*! + this function calculates the Gamma function + + it's multithread safe, you should create a CGamma<> object and use it whenever you call the Gamma() + e.g. + typedef Big<1,2> MyBig; + MyBig x=234, y=345.53; + CGamma cgamma; + std::cout << Gamma(x, cgamma) << std::endl; + std::cout << Gamma(y, cgamma) << std::endl; + in the CGamma<> object the function stores some coefficients (factorials, Bernoulli numbers), + and they will be reused in next calls to the function + + each thread should have its own CGamma<> object, and you can use these objects with Factorial() function too + */ + template + ValueType Gamma(const ValueType & n, CGamma & cgamma, ErrorCode * err = 0, + const volatile StopCalculating * stop = 0) + { + using namespace auxiliaryfunctions; + + ValueType result; + ErrorCode err_tmp; + + if( n.IsNan() ) + { + if( err ) + *err = err_improper_argument; + + return n; + } + + if( cgamma.history.Get(n, result, err_tmp) ) + { + if( err ) + *err = err_tmp; + + return result; + } + + err_tmp = err_ok; + + if( n.IsSign() ) + { + result = GammaMinus(n, cgamma, err_tmp, stop); + } + else + if( n.IsZero() ) + { + err_tmp = err_improper_argument; + result.SetNan(); + } + else + { + result = GammaPlus(n, cgamma, err_tmp, stop); + } + + if( result.IsNan() && err_tmp==err_ok ) + err_tmp = err_overflow; + + if( err ) + *err = err_tmp; + + if( stop && !stop->WasStopSignal() ) + cgamma.history.Add(n, result, err_tmp); + + return result; + } + + + /*! + this function calculates the Gamma function + + note: this function should be used only in a single-thread environment + */ + template + ValueType Gamma(const ValueType & n, ErrorCode * err = 0) + { + // warning: this static object is not thread safe + static CGamma cgamma; + + return Gamma(n, cgamma, err); + } + + + + namespace auxiliaryfunctions + { + + /*! + an auxiliary function for calculating the factorial function + + we use the formula: + x! = gamma(x+1) + */ + template + ValueType Factorial2(ValueType x, + CGamma * cgamma = 0, + ErrorCode * err = 0, + const volatile StopCalculating * stop = 0) + { + ValueType result, one; + + if( x.IsNan() || x.IsSign() || !x.IsInteger() ) + { + if( err ) + *err = err_improper_argument; + + x.SetNan(); + + return x; + } + + one.SetOne(); + x.Add(one); + + if( cgamma ) + return Gamma(x, *cgamma, err, stop); + + return Gamma(x, err); + } + + } // namespace auxiliaryfunctions + + + + /*! + the factorial from given 'x' + e.g. + Factorial(4) = 4! = 1*2*3*4 + + it's multithread safe, you should create a CGamma<> object and use it whenever you call the Factorial() + e.g. + typedef Big<1,2> MyBig; + MyBig x=234, y=54345; + CGamma cgamma; + std::cout << Factorial(x, cgamma) << std::endl; + std::cout << Factorial(y, cgamma) << std::endl; + in the CGamma<> object the function stores some coefficients (factorials, Bernoulli numbers), + and they will be reused in next calls to the function + + each thread should have its own CGamma<> object, and you can use these objects with Gamma() function too + */ + template + ValueType Factorial(const ValueType & x, CGamma & cgamma, ErrorCode * err = 0, + const volatile StopCalculating * stop = 0) + { + return auxiliaryfunctions::Factorial2(x, &cgamma, err, stop); + } + + + /*! + the factorial from given 'x' + e.g. + Factorial(4) = 4! = 1*2*3*4 + + note: this function should be used only in a single-thread environment + */ + template + ValueType Factorial(const ValueType & x, ErrorCode * err = 0) + { + return auxiliaryfunctions::Factorial2(x, (CGamma*)0, err, 0); + } + + + /*! + this method prepares some coefficients: factorials and Bernoulli numbers + stored in 'fact' and 'bern' objects + + we're defining the method here because we're using Gamma() function which + is not available in ttmathobjects.h + + read the doc info in ttmathobjects.h file where CGamma<> struct is declared + */ + template + void CGamma::InitAll() + { + ValueType x = TTMATH_GAMMA_BOUNDARY + 1; + + // history.Remove(x) removes only one object + // we must be sure that there are not others objects with the key 'x' + while( history.Remove(x) ) + { + } + + // the simplest way to initialize is to call the Gamma function with (TTMATH_GAMMA_BOUNDARY + 1) + // when x is larger then fewer coefficients we need + Gamma(x, *this); + } + + + +} // namespace + + +/*! + this is for convenience for the user + he can only use '#include ' +*/ +#include "ttmathparser.h" + +// Dec is not finished yet +//#include "ttmathdec.h" + + + +#ifdef _MSC_VER +//warning C4127: conditional expression is constant +#pragma warning( default: 4127 ) +//warning C4702: unreachable code +#pragma warning( default: 4702 ) +//warning C4800: forcing value to bool 'true' or 'false' (performance warning) +#pragma warning( default: 4800 ) +#endif + +#endif diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmathbig.h b/3rd_party/ttmath-0.9.3/ttmath/ttmathbig.h new file mode 100644 index 00000000..45793b41 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmathbig.h @@ -0,0 +1,6045 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2012, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef headerfilettmathbig +#define headerfilettmathbig + +/*! + \file ttmathbig.h + \brief A Class for representing floating point numbers +*/ + +#include "ttmathint.h" +#include "ttmaththreads.h" + +#include + +#ifdef TTMATH_MULTITHREADS +#include +#endif + +namespace ttmath +{ + + +/*! + \brief Big implements the floating point numbers +*/ +template +class Big +{ + +/* + value = mantissa * 2^exponent + + exponent - an integer value with a sign + mantissa - an integer value without a sing + + mantissa must be pushed into the left side that is the highest bit from + mantissa must be one (of course if there's another value than zero) -- this job + (pushing bits into the left side) making Standardizing() method + + for example: + if we want to store value one (1) into our Big object we must: + set mantissa to 1 + set exponent to 0 + set info to 0 + and call method Standardizing() +*/ + + +public: + +Int exponent; +UInt mantissa; +unsigned char info; + + +/*! + Sign + the mask of a bit from 'info' which means that there is a sign + (when the bit is set) +*/ +#define TTMATH_BIG_SIGN 128 + + +/*! + Not a number + if this bit is set that there is not a valid number +*/ +#define TTMATH_BIG_NAN 64 + + +/*! + Zero + if this bit is set that there is value zero + mantissa should be zero and exponent should be zero too + (the Standardizing() method does this) +*/ +#define TTMATH_BIG_ZERO 32 + + + /*! + this method sets NaN if there was a carry (and returns 1 in such a case) + + c can be 0, 1 or other value different from zero + */ + uint CheckCarry(uint c) + { + if( c != 0 ) + { + SetNan(); + return 1; + } + + return 0; + } + +public: + + + /*! + returning the string represents the currect type of the library + we have following types: + asm_vc_32 - with asm code designed for Microsoft Visual C++ (32 bits) + asm_gcc_32 - with asm code designed for GCC (32 bits) + asm_vc_64 - with asm for VC (64 bit) + asm_gcc_64 - with asm for GCC (64 bit) + no_asm_32 - pure C++ version (32 bit) - without any asm code + no_asm_64 - pure C++ version (64 bit) - without any asm code + */ + static const char * LibTypeStr() + { + return UInt::LibTypeStr(); + } + + + /*! + returning the currect type of the library + */ + static LibTypeCode LibType() + { + return UInt::LibType(); + } + + + + /*! + this method moves all bits from mantissa into its left side + (suitably changes the exponent) or if the mantissa is zero + it sets the exponent to zero as well + (and clears the sign bit and sets the zero bit) + + it can return a carry + the carry will be when we don't have enough space in the exponent + + you don't have to use this method if you don't change the mantissa + and exponent directly + */ + uint Standardizing() + { + if( mantissa.IsTheHighestBitSet() ) + { + ClearInfoBit(TTMATH_BIG_ZERO); + return 0; + } + + if( CorrectZero() ) + return 0; + + uint comp = mantissa.CompensationToLeft(); + + return exponent.Sub( comp ); + } + + +private: + + /*! + if the mantissa is equal zero this method sets exponent to zero and + info without the sign + + it returns true if there was the correction + */ + bool CorrectZero() + { + if( mantissa.IsZero() ) + { + SetInfoBit(TTMATH_BIG_ZERO); + ClearInfoBit(TTMATH_BIG_SIGN); + exponent.SetZero(); + + return true; + } + else + { + ClearInfoBit(TTMATH_BIG_ZERO); + } + + return false; + } + + +public: + + /*! + this method clears a specific bit in the 'info' variable + + bit is one of: TTMATH_BIG_SIGN, TTMATH_BIG_NAN etc. + */ + void ClearInfoBit(unsigned char bit) + { + info = info & (~bit); + } + + + /*! + this method sets a specific bit in the 'info' variable + + bit is one of: TTMATH_BIG_SIGN, TTMATH_BIG_NAN etc. + + */ + void SetInfoBit(unsigned char bit) + { + info = info | bit; + } + + + /*! + this method returns true if a specific bit in the 'info' variable is set + + bit is one of: TTMATH_BIG_SIGN, TTMATH_BIG_NAN etc. + */ + bool IsInfoBit(unsigned char bit) const + { + return (info & bit) != 0; + } + + + /*! + this method sets zero + */ + void SetZero() + { + info = TTMATH_BIG_ZERO; + exponent.SetZero(); + mantissa.SetZero(); + + /* + we don't have to compensate zero + */ + } + + + /*! + this method sets one + */ + void SetOne() + { + info = 0; + mantissa.SetZero(); + mantissa.table[man-1] = TTMATH_UINT_HIGHEST_BIT; + exponent = -sint(man * TTMATH_BITS_PER_UINT - 1); + + // don't have to Standardize() - the last bit from mantissa is set + } + + + /*! + this method sets value 0.5 + */ + void Set05() + { + SetOne(); + exponent.SubOne(); + } + + + /*! + this method sets NaN flag (Not a Number) + when this flag is set that means there is no a valid number + */ + void SetNan() + { + SetInfoBit(TTMATH_BIG_NAN); + } + + + /*! + this method sets NaN flag (Not a Number) + also clears the mantissa and exponent (similarly as it would be a zero value) + */ + void SetZeroNan() + { + SetZero(); + SetNan(); + } + + + /*! + this method swappes this for an argument + */ + void Swap(Big & ss2) + { + unsigned char info_temp = info; + info = ss2.info; + ss2.info = info_temp; + + exponent.Swap(ss2.exponent); + mantissa.Swap(ss2.mantissa); + } + + +private: + + /*! + this method sets the mantissa of the value of pi + */ + void SetMantissaPi() + { + // this is a static table which represents the value of Pi (mantissa of it) + // (first is the highest word) + // we must define this table as 'unsigned int' because + // both on 32bit and 64bit platforms this table is 32bit + static const unsigned int temp_table[] = { + 0xc90fdaa2, 0x2168c234, 0xc4c6628b, 0x80dc1cd1, 0x29024e08, 0x8a67cc74, 0x020bbea6, 0x3b139b22, + 0x514a0879, 0x8e3404dd, 0xef9519b3, 0xcd3a431b, 0x302b0a6d, 0xf25f1437, 0x4fe1356d, 0x6d51c245, + 0xe485b576, 0x625e7ec6, 0xf44c42e9, 0xa637ed6b, 0x0bff5cb6, 0xf406b7ed, 0xee386bfb, 0x5a899fa5, + 0xae9f2411, 0x7c4b1fe6, 0x49286651, 0xece45b3d, 0xc2007cb8, 0xa163bf05, 0x98da4836, 0x1c55d39a, + 0x69163fa8, 0xfd24cf5f, 0x83655d23, 0xdca3ad96, 0x1c62f356, 0x208552bb, 0x9ed52907, 0x7096966d, + 0x670c354e, 0x4abc9804, 0xf1746c08, 0xca18217c, 0x32905e46, 0x2e36ce3b, 0xe39e772c, 0x180e8603, + 0x9b2783a2, 0xec07a28f, 0xb5c55df0, 0x6f4c52c9, 0xde2bcbf6, 0x95581718, 0x3995497c, 0xea956ae5, + 0x15d22618, 0x98fa0510, 0x15728e5a, 0x8aaac42d, 0xad33170d, 0x04507a33, 0xa85521ab, 0xdf1cba64, + 0xecfb8504, 0x58dbef0a, 0x8aea7157, 0x5d060c7d, 0xb3970f85, 0xa6e1e4c7, 0xabf5ae8c, 0xdb0933d7, + 0x1e8c94e0, 0x4a25619d, 0xcee3d226, 0x1ad2ee6b, 0xf12ffa06, 0xd98a0864, 0xd8760273, 0x3ec86a64, + 0x521f2b18, 0x177b200c, 0xbbe11757, 0x7a615d6c, 0x770988c0, 0xbad946e2, 0x08e24fa0, 0x74e5ab31, + 0x43db5bfc, 0xe0fd108e, 0x4b82d120, 0xa9210801, 0x1a723c12, 0xa787e6d7, 0x88719a10, 0xbdba5b26, + 0x99c32718, 0x6af4e23c, 0x1a946834, 0xb6150bda, 0x2583e9ca, 0x2ad44ce8, 0xdbbbc2db, 0x04de8ef9, + 0x2e8efc14, 0x1fbecaa6, 0x287c5947, 0x4e6bc05d, 0x99b2964f, 0xa090c3a2, 0x233ba186, 0x515be7ed, + 0x1f612970, 0xcee2d7af, 0xb81bdd76, 0x2170481c, 0xd0069127, 0xd5b05aa9, 0x93b4ea98, 0x8d8fddc1, + 0x86ffb7dc, 0x90a6c08f, 0x4df435c9, 0x34028492, 0x36c3fab4, 0xd27c7026, 0xc1d4dcb2, 0x602646de, + 0xc9751e76, 0x3dba37bd, 0xf8ff9406, 0xad9e530e, 0xe5db382f, 0x413001ae, 0xb06a53ed, 0x9027d831, + 0x179727b0, 0x865a8918, 0xda3edbeb, 0xcf9b14ed, 0x44ce6cba, 0xced4bb1b, 0xdb7f1447, 0xe6cc254b, + 0x33205151, 0x2bd7af42, 0x6fb8f401, 0x378cd2bf, 0x5983ca01, 0xc64b92ec, 0xf032ea15, 0xd1721d03, + 0xf482d7ce, 0x6e74fef6, 0xd55e702f, 0x46980c82, 0xb5a84031, 0x900b1c9e, 0x59e7c97f, 0xbec7e8f3, + 0x23a97a7e, 0x36cc88be, 0x0f1d45b7, 0xff585ac5, 0x4bd407b2, 0x2b4154aa, 0xcc8f6d7e, 0xbf48e1d8, + 0x14cc5ed2, 0x0f8037e0, 0xa79715ee, 0xf29be328, 0x06a1d58b, 0xb7c5da76, 0xf550aa3d, 0x8a1fbff0, + 0xeb19ccb1, 0xa313d55c, 0xda56c9ec, 0x2ef29632, 0x387fe8d7, 0x6e3c0468, 0x043e8f66, 0x3f4860ee, + 0x12bf2d5b, 0x0b7474d6, 0xe694f91e, 0x6dbe1159, 0x74a3926f, 0x12fee5e4, 0x38777cb6, 0xa932df8c, + 0xd8bec4d0, 0x73b931ba, 0x3bc832b6, 0x8d9dd300, 0x741fa7bf, 0x8afc47ed, 0x2576f693, 0x6ba42466, + 0x3aab639c, 0x5ae4f568, 0x3423b474, 0x2bf1c978, 0x238f16cb, 0xe39d652d, 0xe3fdb8be, 0xfc848ad9, + 0x22222e04, 0xa4037c07, 0x13eb57a8, 0x1a23f0c7, 0x3473fc64, 0x6cea306b, 0x4bcbc886, 0x2f8385dd, + 0xfa9d4b7f, 0xa2c087e8, 0x79683303, 0xed5bdd3a, 0x062b3cf5, 0xb3a278a6, 0x6d2a13f8, 0x3f44f82d, + 0xdf310ee0, 0x74ab6a36, 0x4597e899, 0xa0255dc1, 0x64f31cc5, 0x0846851d, 0xf9ab4819, 0x5ded7ea1, + 0xb1d510bd, 0x7ee74d73, 0xfaf36bc3, 0x1ecfa268, 0x359046f4, 0xeb879f92, 0x4009438b, 0x481c6cd7, + 0x889a002e, 0xd5ee382b, 0xc9190da6, 0xfc026e47, 0x9558e447, 0x5677e9aa, 0x9e3050e2, 0x765694df, + 0xc81f56e8, 0x80b96e71, 0x60c980dd, 0x98a573ea, 0x4472065a, 0x139cd290, 0x6cd1cb72, 0x9ec52a53 // last one was: 0x9ec52a52 + //0x86d44014, ... + // (the last word 0x9ec52a52 was rounded up because the next one is 0x86d44014 -- first bit is one 0x8..) + // 256 32bit words for the mantissa -- about 2464 valid decimal digits + }; + // the value of PI is comming from the website http://zenwerx.com/pi.php + // 3101 digits were taken from this website + // (later the digits were compared with: + // http://www.eveandersson.com/pi/digits/1000000 and http://www.geom.uiuc.edu/~huberty/math5337/groupe/digits.html ) + // and they were set into Big<1,400> type (using operator=(const char*) on a 32bit platform) + // and then the first 256 words were taken into this table + // (TTMATH_BUILTIN_VARIABLES_SIZE on 32bit platform should have the value 256, + // and on 64bit platform value 128 (256/2=128)) + + mantissa.SetFromTable(temp_table, sizeof(temp_table) / sizeof(int)); + } + +public: + + + /*! + this method sets the value of pi + */ + void SetPi() + { + SetMantissaPi(); + info = 0; + exponent = -sint(man)*sint(TTMATH_BITS_PER_UINT) + 2; + } + + + /*! + this method sets the value of 0.5 * pi + */ + void Set05Pi() + { + SetMantissaPi(); + info = 0; + exponent = -sint(man)*sint(TTMATH_BITS_PER_UINT) + 1; + } + + + /*! + this method sets the value of 2 * pi + */ + void Set2Pi() + { + SetMantissaPi(); + info = 0; + exponent = -sint(man)*sint(TTMATH_BITS_PER_UINT) + 3; + } + + + /*! + this method sets the value of e + (the base of the natural logarithm) + */ + void SetE() + { + static const unsigned int temp_table[] = { + 0xadf85458, 0xa2bb4a9a, 0xafdc5620, 0x273d3cf1, 0xd8b9c583, 0xce2d3695, 0xa9e13641, 0x146433fb, + 0xcc939dce, 0x249b3ef9, 0x7d2fe363, 0x630c75d8, 0xf681b202, 0xaec4617a, 0xd3df1ed5, 0xd5fd6561, + 0x2433f51f, 0x5f066ed0, 0x85636555, 0x3ded1af3, 0xb557135e, 0x7f57c935, 0x984f0c70, 0xe0e68b77, + 0xe2a689da, 0xf3efe872, 0x1df158a1, 0x36ade735, 0x30acca4f, 0x483a797a, 0xbc0ab182, 0xb324fb61, + 0xd108a94b, 0xb2c8e3fb, 0xb96adab7, 0x60d7f468, 0x1d4f42a3, 0xde394df4, 0xae56ede7, 0x6372bb19, + 0x0b07a7c8, 0xee0a6d70, 0x9e02fce1, 0xcdf7e2ec, 0xc03404cd, 0x28342f61, 0x9172fe9c, 0xe98583ff, + 0x8e4f1232, 0xeef28183, 0xc3fe3b1b, 0x4c6fad73, 0x3bb5fcbc, 0x2ec22005, 0xc58ef183, 0x7d1683b2, + 0xc6f34a26, 0xc1b2effa, 0x886b4238, 0x611fcfdc, 0xde355b3b, 0x6519035b, 0xbc34f4de, 0xf99c0238, + 0x61b46fc9, 0xd6e6c907, 0x7ad91d26, 0x91f7f7ee, 0x598cb0fa, 0xc186d91c, 0xaefe1309, 0x85139270, + 0xb4130c93, 0xbc437944, 0xf4fd4452, 0xe2d74dd3, 0x64f2e21e, 0x71f54bff, 0x5cae82ab, 0x9c9df69e, + 0xe86d2bc5, 0x22363a0d, 0xabc52197, 0x9b0deada, 0x1dbf9a42, 0xd5c4484e, 0x0abcd06b, 0xfa53ddef, + 0x3c1b20ee, 0x3fd59d7c, 0x25e41d2b, 0x669e1ef1, 0x6e6f52c3, 0x164df4fb, 0x7930e9e4, 0xe58857b6, + 0xac7d5f42, 0xd69f6d18, 0x7763cf1d, 0x55034004, 0x87f55ba5, 0x7e31cc7a, 0x7135c886, 0xefb4318a, + 0xed6a1e01, 0x2d9e6832, 0xa907600a, 0x918130c4, 0x6dc778f9, 0x71ad0038, 0x092999a3, 0x33cb8b7a, + 0x1a1db93d, 0x7140003c, 0x2a4ecea9, 0xf98d0acc, 0x0a8291cd, 0xcec97dcf, 0x8ec9b55a, 0x7f88a46b, + 0x4db5a851, 0xf44182e1, 0xc68a007e, 0x5e0dd902, 0x0bfd64b6, 0x45036c7a, 0x4e677d2c, 0x38532a3a, + 0x23ba4442, 0xcaf53ea6, 0x3bb45432, 0x9b7624c8, 0x917bdd64, 0xb1c0fd4c, 0xb38e8c33, 0x4c701c3a, + 0xcdad0657, 0xfccfec71, 0x9b1f5c3e, 0x4e46041f, 0x388147fb, 0x4cfdb477, 0xa52471f7, 0xa9a96910, + 0xb855322e, 0xdb6340d8, 0xa00ef092, 0x350511e3, 0x0abec1ff, 0xf9e3a26e, 0x7fb29f8c, 0x183023c3, + 0x587e38da, 0x0077d9b4, 0x763e4e4b, 0x94b2bbc1, 0x94c6651e, 0x77caf992, 0xeeaac023, 0x2a281bf6, + 0xb3a739c1, 0x22611682, 0x0ae8db58, 0x47a67cbe, 0xf9c9091b, 0x462d538c, 0xd72b0374, 0x6ae77f5e, + 0x62292c31, 0x1562a846, 0x505dc82d, 0xb854338a, 0xe49f5235, 0xc95b9117, 0x8ccf2dd5, 0xcacef403, + 0xec9d1810, 0xc6272b04, 0x5b3b71f9, 0xdc6b80d6, 0x3fdd4a8e, 0x9adb1e69, 0x62a69526, 0xd43161c1, + 0xa41d570d, 0x7938dad4, 0xa40e329c, 0xcff46aaa, 0x36ad004c, 0xf600c838, 0x1e425a31, 0xd951ae64, + 0xfdb23fce, 0xc9509d43, 0x687feb69, 0xedd1cc5e, 0x0b8cc3bd, 0xf64b10ef, 0x86b63142, 0xa3ab8829, + 0x555b2f74, 0x7c932665, 0xcb2c0f1c, 0xc01bd702, 0x29388839, 0xd2af05e4, 0x54504ac7, 0x8b758282, + 0x2846c0ba, 0x35c35f5c, 0x59160cc0, 0x46fd8251, 0x541fc68c, 0x9c86b022, 0xbb709987, 0x6a460e74, + 0x51a8a931, 0x09703fee, 0x1c217e6c, 0x3826e52c, 0x51aa691e, 0x0e423cfc, 0x99e9e316, 0x50c1217b, + 0x624816cd, 0xad9a95f9, 0xd5b80194, 0x88d9c0a0, 0xa1fe3075, 0xa577e231, 0x83f81d4a, 0x3f2fa457, + 0x1efc8ce0, 0xba8a4fe8, 0xb6855dfe, 0x72b0a66e, 0xded2fbab, 0xfbe58a30, 0xfafabe1c, 0x5d71a87e, + 0x2f741ef8, 0xc1fe86fe, 0xa6bbfde5, 0x30677f0d, 0x97d11d49, 0xf7a8443d, 0x0822e506, 0xa9f4614e, + 0x011e2a94, 0x838ff88c, 0xd68c8bb7, 0xc51eef6d, 0x49ea8ab4, 0xf2c3df5b, 0xb4e0735a, 0xb0d68749 + // 0x2fe26dd4, ... + // 256 32bit words for the mantissa -- about 2464 valid decimal digits + }; + + // above value was calculated using Big<1,400> type on a 32bit platform + // and then the first 256 words were taken, + // the calculating was made by using ExpSurrounding0(1) method + // which took 1420 iterations + // (the result was compared with e taken from http://antwrp.gsfc.nasa.gov/htmltest/gifcity/e.2mil) + // (TTMATH_BUILTIN_VARIABLES_SIZE on 32bit platform should have the value 256, + // and on 64bit platform value 128 (256/2=128)) + + mantissa.SetFromTable(temp_table, sizeof(temp_table) / sizeof(int)); + exponent = -sint(man)*sint(TTMATH_BITS_PER_UINT) + 2; + info = 0; + } + + + /*! + this method sets the value of ln(2) + the natural logarithm from 2 + */ + void SetLn2() + { + static const unsigned int temp_table[] = { + 0xb17217f7, 0xd1cf79ab, 0xc9e3b398, 0x03f2f6af, 0x40f34326, 0x7298b62d, 0x8a0d175b, 0x8baafa2b, + 0xe7b87620, 0x6debac98, 0x559552fb, 0x4afa1b10, 0xed2eae35, 0xc1382144, 0x27573b29, 0x1169b825, + 0x3e96ca16, 0x224ae8c5, 0x1acbda11, 0x317c387e, 0xb9ea9bc3, 0xb136603b, 0x256fa0ec, 0x7657f74b, + 0x72ce87b1, 0x9d6548ca, 0xf5dfa6bd, 0x38303248, 0x655fa187, 0x2f20e3a2, 0xda2d97c5, 0x0f3fd5c6, + 0x07f4ca11, 0xfb5bfb90, 0x610d30f8, 0x8fe551a2, 0xee569d6d, 0xfc1efa15, 0x7d2e23de, 0x1400b396, + 0x17460775, 0xdb8990e5, 0xc943e732, 0xb479cd33, 0xcccc4e65, 0x9393514c, 0x4c1a1e0b, 0xd1d6095d, + 0x25669b33, 0x3564a337, 0x6a9c7f8a, 0x5e148e82, 0x074db601, 0x5cfe7aa3, 0x0c480a54, 0x17350d2c, + 0x955d5179, 0xb1e17b9d, 0xae313cdb, 0x6c606cb1, 0x078f735d, 0x1b2db31b, 0x5f50b518, 0x5064c18b, + 0x4d162db3, 0xb365853d, 0x7598a195, 0x1ae273ee, 0x5570b6c6, 0x8f969834, 0x96d4e6d3, 0x30af889b, + 0x44a02554, 0x731cdc8e, 0xa17293d1, 0x228a4ef9, 0x8d6f5177, 0xfbcf0755, 0x268a5c1f, 0x9538b982, + 0x61affd44, 0x6b1ca3cf, 0x5e9222b8, 0x8c66d3c5, 0x422183ed, 0xc9942109, 0x0bbb16fa, 0xf3d949f2, + 0x36e02b20, 0xcee886b9, 0x05c128d5, 0x3d0bd2f9, 0x62136319, 0x6af50302, 0x0060e499, 0x08391a0c, + 0x57339ba2, 0xbeba7d05, 0x2ac5b61c, 0xc4e9207c, 0xef2f0ce2, 0xd7373958, 0xd7622658, 0x901e646a, + 0x95184460, 0xdc4e7487, 0x156e0c29, 0x2413d5e3, 0x61c1696d, 0xd24aaebd, 0x473826fd, 0xa0c238b9, + 0x0ab111bb, 0xbd67c724, 0x972cd18b, 0xfbbd9d42, 0x6c472096, 0xe76115c0, 0x5f6f7ceb, 0xac9f45ae, + 0xcecb72f1, 0x9c38339d, 0x8f682625, 0x0dea891e, 0xf07afff3, 0xa892374e, 0x175eb4af, 0xc8daadd8, + 0x85db6ab0, 0x3a49bd0d, 0xc0b1b31d, 0x8a0e23fa, 0xc5e5767d, 0xf95884e0, 0x6425a415, 0x26fac51c, + 0x3ea8449f, 0xe8f70edd, 0x062b1a63, 0xa6c4c60c, 0x52ab3316, 0x1e238438, 0x897a39ce, 0x78b63c9f, + 0x364f5b8a, 0xef22ec2f, 0xee6e0850, 0xeca42d06, 0xfb0c75df, 0x5497e00c, 0x554b03d7, 0xd2874a00, + 0x0ca8f58d, 0x94f0341c, 0xbe2ec921, 0x56c9f949, 0xdb4a9316, 0xf281501e, 0x53daec3f, 0x64f1b783, + 0x154c6032, 0x0e2ff793, 0x33ce3573, 0xfacc5fdc, 0xf1178590, 0x3155bbd9, 0x0f023b22, 0x0224fcd8, + 0x471bf4f4, 0x45f0a88a, 0x14f0cd97, 0x6ea354bb, 0x20cdb5cc, 0xb3db2392, 0x88d58655, 0x4e2a0e8a, + 0x6fe51a8c, 0xfaa72ef2, 0xad8a43dc, 0x4212b210, 0xb779dfe4, 0x9d7307cc, 0x846532e4, 0xb9694eda, + 0xd162af05, 0x3b1751f3, 0xa3d091f6, 0x56658154, 0x12b5e8c2, 0x02461069, 0xac14b958, 0x784934b8, + 0xd6cce1da, 0xa5053701, 0x1aa4fb42, 0xb9a3def4, 0x1bda1f85, 0xef6fdbf2, 0xf2d89d2a, 0x4b183527, + 0x8fd94057, 0x89f45681, 0x2b552879, 0xa6168695, 0xc12963b0, 0xff01eaab, 0x73e5b5c1, 0x585318e7, + 0x624f14a5, 0x1a4a026b, 0x68082920, 0x57fd99b6, 0x6dc085a9, 0x8ac8d8ca, 0xf9eeeea9, 0x8a2400ca, + 0xc95f260f, 0xd10036f9, 0xf91096ac, 0x3195220a, 0x1a356b2a, 0x73b7eaad, 0xaf6d6058, 0x71ef7afb, + 0x80bc4234, 0x33562e94, 0xb12dfab4, 0x14451579, 0xdf59eae0, 0x51707062, 0x4012a829, 0x62c59cab, + 0x347f8304, 0xd889659e, 0x5a9139db, 0x14efcc30, 0x852be3e8, 0xfc99f14d, 0x1d822dd6, 0xe2f76797, + 0xe30219c8, 0xaa9ce884, 0x8a886eb3, 0xc87b7295, 0x988012e8, 0x314186ed, 0xbaf86856, 0xccd3c3b6, + 0xee94e62f, 0x110a6783, 0xd2aae89c, 0xcc3b76fc, 0x435a0ce1, 0x34c2838f, 0xd571ec6c, 0x1366a993 // last one was: 0x1366a992 + //0xcbb9ac40, ... + // (the last word 0x1366a992 was rounded up because the next one is 0xcbb9ac40 -- first bit is one 0xc..) + // 256 32bit words for the mantissa -- about 2464 valid decimal digits + }; + + // above value was calculated using Big<1,400> type on a 32bit platform + // and then the first 256 words were taken, + // the calculating was made by using LnSurrounding1(2) method + // which took 4035 iterations + // (the result was compared with ln(2) taken from http://ja0hxv.calico.jp/pai/estart.html) + // (TTMATH_BUILTIN_VARIABLES_SIZE on 32bit platform should have the value 256, + // and on 64bit platform value 128 (256/2=128)) + + mantissa.SetFromTable(temp_table, sizeof(temp_table) / sizeof(int)); + exponent = -sint(man)*sint(TTMATH_BITS_PER_UINT); + info = 0; + } + + + /*! + this method sets the value of ln(10) + the natural logarithm from 10 + + I introduced this constant especially to make the conversion ToString() + being faster. In fact the method ToString() is keeping values of logarithms + it has calculated but it must calculate the logarithm at least once. + If a program, which uses this library, is running for a long time this + would be ok, but for programs which are running shorter, for example for + CGI applications which only once are printing values, this would be much + inconvenience. Then if we're printing with base (radix) 10 and the mantissa + of our value is smaller than or equal to TTMATH_BUILTIN_VARIABLES_SIZE + we don't calculate the logarithm but take it from this constant. + */ + void SetLn10() + { + static const unsigned int temp_table[] = { + 0x935d8ddd, 0xaaa8ac16, 0xea56d62b, 0x82d30a28, 0xe28fecf9, 0xda5df90e, 0x83c61e82, 0x01f02d72, + 0x962f02d7, 0xb1a8105c, 0xcc70cbc0, 0x2c5f0d68, 0x2c622418, 0x410be2da, 0xfb8f7884, 0x02e516d6, + 0x782cf8a2, 0x8a8c911e, 0x765aa6c3, 0xb0d831fb, 0xef66ceb0, 0x4ab3c6fa, 0x5161bb49, 0xd219c7bb, + 0xca67b35b, 0x23605085, 0x8e93368d, 0x44789c4f, 0x5b08b057, 0xd5ede20f, 0x469ea58e, 0x9305e981, + 0xe2478fca, 0xad3aee98, 0x9cd5b42e, 0x6a271619, 0xa47ecb26, 0x978c5d4f, 0xdb1d28ea, 0x57d4fdc0, + 0xe40bf3cc, 0x1e14126a, 0x45765cde, 0x268339db, 0xf47fa96d, 0xeb271060, 0xaf88486e, 0xa9b7401e, + 0x3dfd3c51, 0x748e6d6e, 0x3848c8d2, 0x5faf1bca, 0xe88047f1, 0x7b0d9b50, 0xa949eaaa, 0xdf69e8a5, + 0xf77e3760, 0x4e943960, 0xe38a5700, 0xffde2db1, 0xad6bfbff, 0xd821ba0a, 0x4cb0466d, 0x61ba648e, + 0xef99c8e5, 0xf6974f36, 0x3982a78c, 0xa45ddfc8, 0x09426178, 0x19127a6e, 0x3b70fcda, 0x2d732d47, + 0xb5e4b1c8, 0xc0e5a10a, 0xaa6604a5, 0x324ec3dc, 0xbc64ea80, 0x6e198566, 0x1f1d366c, 0x20663834, + 0x4d5e843f, 0x20642b97, 0x0a62d18e, 0x478f7bd5, 0x8fcd0832, 0x4a7b32a6, 0xdef85a05, 0xeb56323a, + 0x421ef5e0, 0xb00410a0, 0xa0d9c260, 0x794a976f, 0xf6ff363d, 0xb00b6b33, 0xf42c58de, 0xf8a3c52d, + 0xed69b13d, 0xc1a03730, 0xb6524dc1, 0x8c167e86, 0x99d6d20e, 0xa2defd2b, 0xd006f8b4, 0xbe145a2a, + 0xdf3ccbb3, 0x189da49d, 0xbc1261c8, 0xb3e4daad, 0x6a36cecc, 0xb2d5ae5b, 0x89bf752f, 0xb5dfb353, + 0xff3065c4, 0x0cfceec8, 0x1be5a9a9, 0x67fddc57, 0xc4b83301, 0x006bf062, 0x4b40ed7a, 0x56c6cdcd, + 0xa2d6fe91, 0x388e9e3e, 0x48a93f5f, 0x5e3b6eb4, 0xb81c4a5b, 0x53d49ea6, 0x8e668aea, 0xba83c7f8, + 0xfb5f06c3, 0x58ac8f70, 0xfa9d8c59, 0x8c574502, 0xbaf54c96, 0xc84911f0, 0x0482d095, 0x1a0af022, + 0xabbab080, 0xec97efd3, 0x671e4e0e, 0x52f166b6, 0xcd5cd226, 0x0dc67795, 0x2e1e34a3, 0xf799677f, + 0x2c1d48f1, 0x2944b6c5, 0x2ba1307e, 0x704d67f9, 0x1c1035e4, 0x4e927c63, 0x03cf12bf, 0xe2cd2e31, + 0xf8ee4843, 0x344d51b0, 0xf37da42b, 0x9f0b0fd9, 0x134fb2d9, 0xf815e490, 0xd966283f, 0x23962766, + 0xeceab1e4, 0xf3b5fc86, 0x468127e2, 0xb606d10d, 0x3a45f4b6, 0xb776102d, 0x2fdbb420, 0x80c8fa84, + 0xd0ff9f45, 0xc58aef38, 0xdb2410fd, 0x1f1cebad, 0x733b2281, 0x52ca5f36, 0xddf29daa, 0x544334b8, + 0xdeeaf659, 0x4e462713, 0x1ed485b4, 0x6a0822e1, 0x28db471c, 0xa53938a8, 0x44c3bef7, 0xf35215c8, + 0xb382bc4e, 0x3e4c6f15, 0x6285f54c, 0x17ab408e, 0xccbf7f5e, 0xd16ab3f6, 0xced2846d, 0xf457e14f, + 0xbb45d9c5, 0x646ad497, 0xac697494, 0x145de32e, 0x93907128, 0xd263d521, 0x79efb424, 0xd64651d6, + 0xebc0c9f0, 0xbb583a44, 0xc6412c84, 0x85bb29a6, 0x4d31a2cd, 0x92954469, 0xa32b1abd, 0xf7f5202c, + 0xa4aa6c93, 0x2e9b53cf, 0x385ab136, 0x2741f356, 0x5de9c065, 0x6009901c, 0x88abbdd8, 0x74efcf73, + 0x3f761ad4, 0x35f3c083, 0xfd6b8ee0, 0x0bef11c7, 0xc552a89d, 0x58ce4a21, 0xd71e54f2, 0x4157f6c7, + 0xd4622316, 0xe98956d7, 0x450027de, 0xcbd398d8, 0x4b98b36a, 0x0724c25c, 0xdb237760, 0xe9324b68, + 0x7523e506, 0x8edad933, 0x92197f00, 0xb853a326, 0xb330c444, 0x65129296, 0x34bc0670, 0xe177806d, + 0xe338dac4, 0x5537492a, 0xe19add83, 0xcf45000f, 0x5b423bce, 0x6497d209, 0xe30e18a1, 0x3cbf0687, + 0x67973103, 0xd9485366, 0x81506bba, 0x2e93a9a4, 0x7dd59d3f, 0xf17cd746, 0x8c2075be, 0x552a4348 // last one was: 0x552a4347 + // 0xb4a638ef, ... + //(the last word 0x552a4347 was rounded up because the next one is 0xb4a638ef -- first bit is one 0xb..) + // 256 32bit words for the mantissa -- about 2464 valid digits (decimal) + }; + + // above value was calculated using Big<1,400> type on a 32bit platform + // and then the first 256 32bit words were taken, + // the calculating was made by using LnSurrounding1(10) method + // which took 22080 iterations + // (the result was compared with ln(10) taken from http://ja0hxv.calico.jp/pai/estart.html) + // (the formula used in LnSurrounding1(x) converges badly when + // the x is greater than one but in fact we can use it, only the + // number of iterations will be greater) + // (TTMATH_BUILTIN_VARIABLES_SIZE on 32bit platform should have the value 256, + // and on 64bit platform value 128 (256/2=128)) + + mantissa.SetFromTable(temp_table, sizeof(temp_table) / sizeof(int)); + exponent = -sint(man)*sint(TTMATH_BITS_PER_UINT) + 2; + info = 0; + } + + + /*! + this method sets the maximum value which can be held in this type + */ + void SetMax() + { + info = 0; + mantissa.SetMax(); + exponent.SetMax(); + + // we don't have to use 'Standardizing()' because the last bit from + // the mantissa is set + } + + + /*! + this method sets the minimum value which can be held in this type + */ + void SetMin() + { + info = 0; + + mantissa.SetMax(); + exponent.SetMax(); + SetSign(); + + // we don't have to use 'Standardizing()' because the last bit from + // the mantissa is set + } + + + /*! + testing whether there is a value zero or not + */ + bool IsZero() const + { + return IsInfoBit(TTMATH_BIG_ZERO); + } + + + /*! + this method returns true when there's the sign set + also we don't check the NaN flag + */ + bool IsSign() const + { + return IsInfoBit(TTMATH_BIG_SIGN); + } + + + /*! + this method returns true when there is not a valid number + */ + bool IsNan() const + { + return IsInfoBit(TTMATH_BIG_NAN); + } + + + + /*! + this method clears the sign + (there'll be an absolute value) + + e.g. + -1 -> 1 + 2 -> 2 + */ + void Abs() + { + ClearInfoBit(TTMATH_BIG_SIGN); + } + + + /*! + this method remains the 'sign' of the value + e.g. -2 = -1 + 0 = 0 + 10 = 1 + */ + void Sgn() + { + // we have to check the NaN flag, because the next SetOne() method would clear it + if( IsNan() ) + return; + + if( IsSign() ) + { + SetOne(); + SetSign(); + } + else + if( IsZero() ) + SetZero(); // !! is nedeed here? + else + SetOne(); + } + + + + /*! + this method sets the sign + + e.g. + -1 -> -1 + 2 -> -2 + + we do not check whether there is a zero or not, if you're using this method + you must be sure that the value is (or will be afterwards) different from zero + */ + void SetSign() + { + SetInfoBit(TTMATH_BIG_SIGN); + } + + + /*! + this method changes the sign + when there is a value of zero then the sign is not changed + + e.g. + -1 -> 1 + 2 -> -2 + */ + void ChangeSign() + { + // we don't have to check the NaN flag here + + if( IsZero() ) + return; + + if( IsSign() ) + ClearInfoBit(TTMATH_BIG_SIGN); + else + SetInfoBit(TTMATH_BIG_SIGN); + } + + + +private: + + /*! + this method does the half-to-even rounding (banker's rounding) + + if is_half is: + true - that means the rest was equal the half (0.5 decimal) + false - that means the rest was greater than a half (greater than 0.5 decimal) + + if the rest was less than a half then don't call this method + (the rounding should does nothing then) + */ + uint RoundHalfToEven(bool is_half, bool rounding_up = true) + { + uint c = 0; + + if( !is_half || mantissa.IsTheLowestBitSet() ) + { + if( rounding_up ) + { + if( mantissa.AddOne() ) + { + mantissa.Rcr(1, 1); + c = exponent.AddOne(); + } + } + else + { + #ifdef TTMATH_DEBUG + uint c_from_zero = + #endif + mantissa.SubOne(); + + // we're using rounding_up=false in Add() when the mantissas have different signs + // mantissa can be zero only when previous mantissa was equal to ss2.mantissa + // but in such a case 'last_bit_set' will not be set and consequently 'do_rounding' will be false + TTMATH_ASSERT( c_from_zero == 0 ) + } + } + + return c; + } + + + + + + /*! + * + * basic mathematic functions + * + */ + + + /*! + this method adds one to the existing value + */ + uint AddOne() + { + Big one; + + one.SetOne(); + + return Add(one); + } + + + /*! + this method subtracts one from the existing value + */ + uint SubOne() + { + Big one; + + one.SetOne(); + + return Sub(one); + } + + +private: + + + /*! + an auxiliary method for adding + */ + void AddCheckExponents( Big & ss2, + Int & exp_offset, + bool & last_bit_set, + bool & rest_zero, + bool & do_adding, + bool & do_rounding) + { + Int mantissa_size_in_bits( man * TTMATH_BITS_PER_UINT ); + + if( exp_offset == mantissa_size_in_bits ) + { + last_bit_set = ss2.mantissa.IsTheHighestBitSet(); + rest_zero = ss2.mantissa.AreFirstBitsZero(man*TTMATH_BITS_PER_UINT - 1); + do_rounding = true; // we'are only rounding + } + else + if( exp_offset < mantissa_size_in_bits ) + { + uint moved = exp_offset.ToInt(); // how many times we must move ss2.mantissa + rest_zero = true; + + if( moved > 0 ) + { + last_bit_set = static_cast( ss2.mantissa.GetBit(moved-1) ); + + if( moved > 1 ) + rest_zero = ss2.mantissa.AreFirstBitsZero(moved - 1); + + // (2) moving 'exp_offset' times + ss2.mantissa.Rcr(moved, 0); + } + + do_adding = true; + do_rounding = true; + } + + // if exp_offset is greater than mantissa_size_in_bits then we do nothing + // ss2 is too small for taking into consideration in the sum + } + + + /*! + an auxiliary method for adding + */ + uint AddMantissas( Big & ss2, + bool & last_bit_set, + bool & rest_zero) + { + uint c = 0; + + if( IsSign() == ss2.IsSign() ) + { + // values have the same signs + if( mantissa.Add(ss2.mantissa) ) + { + // we have one bit more from addition (carry) + // now rest_zero means the old rest_zero with the old last_bit_set + rest_zero = (!last_bit_set && rest_zero); + last_bit_set = mantissa.Rcr(1,1); + c += exponent.AddOne(); + } + } + else + { + // values have different signs + // there shouldn't be a carry here because + // (1) (2) guarantee that the mantissa of this + // is greater than or equal to the mantissa of the ss2 + + #ifdef TTMATH_DEBUG + uint c_temp = + #endif + mantissa.Sub(ss2.mantissa); + + TTMATH_ASSERT( c_temp == 0 ) + } + + return c; + } + + +public: + + + /*! + Addition this = this + ss2 + + it returns carry if the sum is too big + */ + uint Add(Big ss2, bool round = true, bool adding = true) + { + bool last_bit_set, rest_zero, do_adding, do_rounding, rounding_up; + Int exp_offset( exponent ); + uint c = 0; + + if( IsNan() || ss2.IsNan() ) + return CheckCarry(1); + + if( !adding ) + ss2.ChangeSign(); // subtracting + + exp_offset.Sub( ss2.exponent ); + exp_offset.Abs(); + + // (1) abs(this) will be >= abs(ss2) + if( SmallerWithoutSignThan(ss2) ) + Swap(ss2); + + if( ss2.IsZero() ) + return 0; + + last_bit_set = rest_zero = do_adding = do_rounding = false; + rounding_up = (IsSign() == ss2.IsSign()); + + AddCheckExponents(ss2, exp_offset, last_bit_set, rest_zero, do_adding, do_rounding); + + if( do_adding ) + c += AddMantissas(ss2, last_bit_set, rest_zero); + + if( !round || !last_bit_set ) + do_rounding = false; + + if( do_rounding ) + c += RoundHalfToEven(rest_zero, rounding_up); + + if( do_adding || do_rounding ) + c += Standardizing(); + + return CheckCarry(c); + } + + + /*! + Subtraction this = this - ss2 + + it returns carry if the result is too big + */ + uint Sub(const Big & ss2, bool round = true) + { + return Add(ss2, round, false); + } + + + /*! + bitwise AND + + this and ss2 must be >= 0 + return values: + 0 - ok + 1 - carry + 2 - this or ss2 was negative + */ + uint BitAnd(Big ss2) + { + if( IsNan() || ss2.IsNan() ) + return CheckCarry(1); + + if( IsSign() || ss2.IsSign() ) + { + SetNan(); + return 2; + } + + if( IsZero() ) + return 0; + + if( ss2.IsZero() ) + { + SetZero(); + return 0; + } + + Int exp_offset( exponent ); + Int mantissa_size_in_bits( man * TTMATH_BITS_PER_UINT ); + + uint c = 0; + + exp_offset.Sub( ss2.exponent ); + exp_offset.Abs(); + + // abs(this) will be >= abs(ss2) + if( SmallerWithoutSignThan(ss2) ) + Swap(ss2); + + if( exp_offset >= mantissa_size_in_bits ) + { + // the second value is too small + SetZero(); + return 0; + } + + // exp_offset < mantissa_size_in_bits, moving 'exp_offset' times + ss2.mantissa.Rcr( exp_offset.ToInt(), 0 ); + mantissa.BitAnd(ss2.mantissa); + + c += Standardizing(); + + return CheckCarry(c); + } + + + /*! + bitwise OR + + this and ss2 must be >= 0 + return values: + 0 - ok + 1 - carry + 2 - this or ss2 was negative + */ + uint BitOr(Big ss2) + { + if( IsNan() || ss2.IsNan() ) + return CheckCarry(1); + + if( IsSign() || ss2.IsSign() ) + { + SetNan(); + return 2; + } + + if( IsZero() ) + { + *this = ss2; + return 0; + } + + if( ss2.IsZero() ) + return 0; + + Int exp_offset( exponent ); + Int mantissa_size_in_bits( man * TTMATH_BITS_PER_UINT ); + + uint c = 0; + + exp_offset.Sub( ss2.exponent ); + exp_offset.Abs(); + + // abs(this) will be >= abs(ss2) + if( SmallerWithoutSignThan(ss2) ) + Swap(ss2); + + if( exp_offset >= mantissa_size_in_bits ) + // the second value is too small + return 0; + + // exp_offset < mantissa_size_in_bits, moving 'exp_offset' times + ss2.mantissa.Rcr( exp_offset.ToInt(), 0 ); + mantissa.BitOr(ss2.mantissa); + + c += Standardizing(); + + return CheckCarry(c); + } + + + /*! + bitwise XOR + + this and ss2 must be >= 0 + return values: + 0 - ok + 1 - carry + 2 - this or ss2 was negative + */ + uint BitXor(Big ss2) + { + if( IsNan() || ss2.IsNan() ) + return CheckCarry(1); + + if( IsSign() || ss2.IsSign() ) + { + SetNan(); + return 2; + } + + if( ss2.IsZero() ) + return 0; + + if( IsZero() ) + { + *this = ss2; + return 0; + } + + Int exp_offset( exponent ); + Int mantissa_size_in_bits( man * TTMATH_BITS_PER_UINT ); + + uint c = 0; + + exp_offset.Sub( ss2.exponent ); + exp_offset.Abs(); + + // abs(this) will be >= abs(ss2) + if( SmallerWithoutSignThan(ss2) ) + Swap(ss2); + + if( exp_offset >= mantissa_size_in_bits ) + // the second value is too small + return 0; + + // exp_offset < mantissa_size_in_bits, moving 'exp_offset' times + ss2.mantissa.Rcr( exp_offset.ToInt(), 0 ); + mantissa.BitXor(ss2.mantissa); + + c += Standardizing(); + + return CheckCarry(c); + } + + + + /*! + Multiplication this = this * ss2 (ss2 is uint) + + ss2 without a sign + */ + uint MulUInt(uint ss2) + { + UInt man_result; + uint i,c = 0; + + if( IsNan() ) + return 1; + + if( IsZero() ) + return 0; + + if( ss2 == 0 ) + { + SetZero(); + return 0; + } + + // man_result = mantissa * ss2.mantissa + mantissa.MulInt(ss2, man_result); + + sint bit = UInt::FindLeadingBitInWord(man_result.table[man]); // man - last word + + if( bit!=-1 && uint(bit) > (TTMATH_BITS_PER_UINT/2) ) + { + // 'i' will be from 0 to TTMATH_BITS_PER_UINT + i = man_result.CompensationToLeft(); + c = exponent.Add( TTMATH_BITS_PER_UINT - i ); + + for(i=0 ; i0 && (tab[len-1] & TTMATH_UINT_HIGHEST_BIT)!=0 ) + + for(i=0 ; i & ss2, bool round = true) + { + TTMATH_REFERENCE_ASSERT( ss2 ) + + UInt man_result; + uint c = 0; + uint i; + + if( IsNan() || ss2.IsNan() ) + return CheckCarry(1); + + if( IsZero() ) + return 0; + + if( ss2.IsZero() ) + { + SetZero(); + return 0; + } + + // man_result = mantissa * ss2.mantissa + mantissa.MulBig(ss2.mantissa, man_result); + + // 'i' will be from 0 to man*TTMATH_BITS_PER_UINT + // because mantissa and ss2.mantissa are standardized + // (the highest bit in man_result is set to 1 or + // if there is a zero value in man_result the method CompensationToLeft() + // returns 0 but we'll correct this at the end in Standardizing() method) + i = man_result.CompensationToLeft(); + uint exp_add = man * TTMATH_BITS_PER_UINT - i; + + if( exp_add ) + c += exponent.Add( exp_add ); + + c += exponent.Add( ss2.exponent ); + + for(i=0 ; i & ss2, bool round = true) + { + if( this == &ss2 ) + { + Big copy_ss2(ss2); + return MulRef(copy_ss2, round); + } + else + { + return MulRef(ss2, round); + } + } + + +private: + + /*! + division this = this / ss2 + + return value: + 0 - ok + 1 - carry (in a division carry can be as well) + 2 - improper argument (ss2 is zero) + */ + uint DivRef(const Big & ss2, bool round = true) + { + TTMATH_REFERENCE_ASSERT( ss2 ) + + UInt man1; + UInt man2; + uint i,c = 0; + + if( IsNan() || ss2.IsNan() ) + return CheckCarry(1); + + if( ss2.IsZero() ) + { + SetNan(); + return 2; + } + + if( IsZero() ) + return 0; + + // !! this two loops can be joined together + + for(i=0 ; i & ss2, bool round = true) + { + if( this == &ss2 ) + { + Big copy_ss2(ss2); + return DivRef(copy_ss2, round); + } + else + { + return DivRef(ss2, round); + } + } + + +private: + + /*! + the remainder from a division + */ + uint ModRef(const Big & ss2) + { + TTMATH_REFERENCE_ASSERT( ss2 ) + + uint c = 0; + + if( IsNan() || ss2.IsNan() ) + return CheckCarry(1); + + if( ss2.IsZero() ) + { + SetNan(); + return 2; + } + + if( !SmallerWithoutSignThan(ss2) ) + { + Big temp(*this); + + c = temp.Div(ss2); + temp.SkipFraction(); + c += temp.Mul(ss2); + c += Sub(temp); + + if( !SmallerWithoutSignThan( ss2 ) ) + c += 1; + } + + return CheckCarry(c); + } + + +public: + + /*! + the remainder from a division + + e.g. + 12.6 mod 3 = 0.6 because 12.6 = 3*4 + 0.6 + -12.6 mod 3 = -0.6 bacause -12.6 = 3*(-4) + (-0.6) + 12.6 mod -3 = 0.6 + -12.6 mod -3 = -0.6 + + it means: + in other words: this(old) = ss2 * q + this(new) + + return value: + 0 - ok + 1 - carry + 2 - improper argument (ss2 is zero) + */ + uint Mod(const Big & ss2) + { + if( this == &ss2 ) + { + Big copy_ss2(ss2); + return ModRef(copy_ss2); + } + else + { + return ModRef(ss2); + } + } + + + /*! + this method returns: 'this' mod 2 + (either zero or one) + + this method is much faster than using Mod( object_with_value_two ) + */ + uint Mod2() const + { + if( exponent>sint(0) || exponent<=-sint(man*TTMATH_BITS_PER_UINT) ) + return 0; + + sint exp_int = exponent.ToInt(); + // 'exp_int' is negative (or zero), we set it as positive + exp_int = -exp_int; + + return mantissa.GetBit(exp_int); + } + + + /*! + power this = this ^ pow + (pow without a sign) + + binary algorithm (r-to-l) + + return values: + 0 - ok + 1 - carry + 2 - incorrect arguments (0^0) + */ + template + uint Pow(UInt pow) + { + if( IsNan() ) + return 1; + + if( IsZero() ) + { + if( pow.IsZero() ) + { + // we don't define zero^zero + SetNan(); + return 2; + } + + // 0^(+something) is zero + return 0; + } + + Big start(*this); + Big result; + result.SetOne(); + uint c = 0; + + while( !c ) + { + if( pow.table[0] & 1 ) + c += result.Mul(start); + + pow.Rcr(1); + + if( pow.IsZero() ) + break; + + c += start.Mul(start); + } + + *this = result; + + return CheckCarry(c); + } + + + /*! + power this = this ^ pow + p can be negative + + return values: + 0 - ok + 1 - carry + 2 - incorrect arguments 0^0 or 0^(-something) + */ + template + uint Pow(Int pow) + { + if( IsNan() ) + return 1; + + if( !pow.IsSign() ) + return Pow( UInt(pow) ); + + if( IsZero() ) + { + // if 'p' is negative then + // 'this' must be different from zero + SetNan(); + return 2; + } + + uint c = pow.ChangeSign(); + + Big t(*this); + c += t.Pow( UInt(pow) ); // here can only be a carry (return:1) + + SetOne(); + c += Div(t); + + return CheckCarry(c); + } + + + /*! + power this = this ^ abs([pow]) + pow is treated as a value without a sign and without a fraction + if pow has a sign then the method pow.Abs() is used + if pow has a fraction the fraction is skipped (not used in calculation) + + return values: + 0 - ok + 1 - carry + 2 - incorrect arguments (0^0) + */ + uint PowUInt(Big pow) + { + if( IsNan() || pow.IsNan() ) + return CheckCarry(1); + + if( IsZero() ) + { + if( pow.IsZero() ) + { + SetNan(); + return 2; + } + + // 0^(+something) is zero + return 0; + } + + if( pow.IsSign() ) + pow.Abs(); + + Big start(*this); + Big result; + Big one; + uint c = 0; + one.SetOne(); + result = one; + + while( !c ) + { + if( pow.Mod2() ) + c += result.Mul(start); + + c += pow.exponent.SubOne(); + + if( pow < one ) + break; + + c += start.Mul(start); + } + + *this = result; + + return CheckCarry(c); + } + + + /*! + power this = this ^ [pow] + pow is treated as a value without a fraction + pow can be negative + + return values: + 0 - ok + 1 - carry + 2 - incorrect arguments 0^0 or 0^(-something) + */ + uint PowInt(const Big & pow) + { + if( IsNan() || pow.IsNan() ) + return CheckCarry(1); + + if( !pow.IsSign() ) + return PowUInt(pow); + + if( IsZero() ) + { + // if 'pow' is negative then + // 'this' must be different from zero + SetNan(); + return 2; + } + + Big temp(*this); + uint c = temp.PowUInt(pow); // here can only be a carry (result:1) + + SetOne(); + c += Div(temp); + + return CheckCarry(c); + } + + + /*! + power this = this ^ pow + this must be greater than zero (this > 0) + pow can be negative and with fraction + + return values: + 0 - ok + 1 - carry + 2 - incorrect argument ('this' <= 0) + */ + uint PowFrac(const Big & pow) + { + if( IsNan() || pow.IsNan() ) + return CheckCarry(1); + + Big temp; + uint c = temp.Ln(*this); + + if( c != 0 ) // can be 2 from Ln() + { + SetNan(); + return c; + } + + c += temp.Mul(pow); + c += Exp(temp); + + return CheckCarry(c); + } + + + /*! + power this = this ^ pow + pow can be negative and with fraction + + return values: + 0 - ok + 1 - carry + 2 - incorrect argument ('this' or 'pow') + */ + uint Pow(const Big & pow) + { + if( IsNan() || pow.IsNan() ) + return CheckCarry(1); + + if( IsZero() ) + { + // 0^pow will be 0 only for pow>0 + if( pow.IsSign() || pow.IsZero() ) + { + SetNan(); + return 2; + } + + SetZero(); + + return 0; + } + + if( pow.exponent>-sint(man*TTMATH_BITS_PER_UINT) && pow.exponent<=0 ) + { + if( pow.IsInteger() ) + return PowInt( pow ); + } + + return PowFrac(pow); + } + + + /*! + this function calculates the square root + e.g. let this=9 then this.Sqrt() gives 3 + + return: 0 - ok + 1 - carry + 2 - improper argument (this<0 or NaN) + */ + uint Sqrt() + { + if( IsNan() || IsSign() ) + { + SetNan(); + return 2; + } + + if( IsZero() ) + return 0; + + Big old(*this); + Big ln; + uint c = 0; + + // we're using the formula: sqrt(x) = e ^ (ln(x) / 2) + c += ln.Ln(*this); + c += ln.exponent.SubOne(); // ln = ln / 2 + c += Exp(ln); + + // above formula doesn't give accurate results for some integers + // e.g. Sqrt(81) would not be 9 but a value very closed to 9 + // we're rounding the result, calculating result*result and comparing + // with the old value, if they are equal then the result is an integer too + + if( !c && old.IsInteger() && !IsInteger() ) + { + Big temp(*this); + c += temp.Round(); + + Big temp2(temp); + c += temp.Mul(temp2); + + if( temp == old ) + *this = temp2; + } + + return CheckCarry(c); + } + + +private: + +#ifdef TTMATH_CONSTANTSGENERATOR +public: +#endif + + /*! + Exponent this = exp(x) = e^x where x is in (-1,1) + + we're using the formula exp(x) = 1 + (x)/(1!) + (x^2)/(2!) + (x^3)/(3!) + ... + */ + void ExpSurrounding0(const Big & x, uint * steps = 0) + { + TTMATH_REFERENCE_ASSERT( x ) + + Big denominator, denominator_i; + Big one, old_value, next_part; + Big numerator = x; + + SetOne(); + one.SetOne(); + denominator.SetOne(); + denominator_i.SetOne(); + + uint i; + old_value = *this; + + // we begin from 1 in order to not test at the beginning + #ifdef TTMATH_CONSTANTSGENERATOR + for(i=1 ; true ; ++i) + #else + for(i=1 ; i<=TTMATH_ARITHMETIC_MAX_LOOP ; ++i) + #endif + { + bool testing = ((i & 3) == 0); // it means '(i % 4) == 0' + + next_part = numerator; + + if( next_part.Div( denominator ) ) + // if there is a carry here we only break the loop + // however the result we return as good + // it means there are too many parts of the formula + break; + + // there shouldn't be a carry here + Add( next_part ); + + if( testing ) + { + if( old_value == *this ) + // we've added next few parts of the formula but the result + // is still the same then we break the loop + break; + else + old_value = *this; + } + + // we set the denominator and the numerator for a next part of the formula + if( denominator_i.Add(one) ) + // if there is a carry here the result we return as good + break; + + if( denominator.Mul(denominator_i) ) + break; + + if( numerator.Mul(x) ) + break; + } + + if( steps ) + *steps = i; + } + +public: + + + /*! + Exponent this = exp(x) = e^x + + we're using the fact that our value is stored in form of: + x = mantissa * 2^exponent + then + e^x = e^(mantissa* 2^exponent) or + e^x = (e^mantissa)^(2^exponent) + + 'Exp' returns a carry if we can't count the result ('x' is too big) + */ + uint Exp(const Big & x) + { + uint c = 0; + + if( x.IsNan() ) + return CheckCarry(1); + + if( x.IsZero() ) + { + SetOne(); + return 0; + } + + // m will be the value of the mantissa in range (-1,1) + Big m(x); + m.exponent = -sint(man*TTMATH_BITS_PER_UINT); + + // 'e_' will be the value of '2^exponent' + // e_.mantissa.table[man-1] = TTMATH_UINT_HIGHEST_BIT; and + // e_.exponent.Add(1) mean: + // e_.mantissa.table[0] = 1; + // e_.Standardizing(); + // e_.exponent.Add(man*TTMATH_BITS_PER_UINT) + // (we must add 'man*TTMATH_BITS_PER_UINT' because we've taken it from the mantissa) + Big e_(x); + e_.mantissa.SetZero(); + e_.mantissa.table[man-1] = TTMATH_UINT_HIGHEST_BIT; + c += e_.exponent.Add(1); + e_.Abs(); + + /* + now we've got: + m - the value of the mantissa in range (-1,1) + e_ - 2^exponent + + e_ can be as: + ...2^-2, 2^-1, 2^0, 2^1, 2^2 ... + ...1/4 , 1/2 , 1 , 2 , 4 ... + + above one e_ is integer + + if e_ is greater than 1 we calculate the exponent as: + e^(m * e_) = ExpSurrounding0(m) ^ e_ + and if e_ is smaller or equal one we calculate the exponent in this way: + e^(m * e_) = ExpSurrounding0(m* e_) + because if e_ is smaller or equal 1 then the product of m*e_ is smaller or equal m + */ + + if( e_ <= 1 ) + { + m.Mul(e_); + ExpSurrounding0(m); + } + else + { + ExpSurrounding0(m); + c += PowUInt(e_); + } + + return CheckCarry(c); + } + + + + +private: + +#ifdef TTMATH_CONSTANTSGENERATOR +public: +#endif + + /*! + Natural logarithm this = ln(x) where x in range <1,2) + + we're using the formula: + ln x = 2 * [ (x-1)/(x+1) + (1/3)((x-1)/(x+1))^3 + (1/5)((x-1)/(x+1))^5 + ... ] + */ + void LnSurrounding1(const Big & x, uint * steps = 0) + { + Big old_value, next_part, denominator, one, two, x1(x), x2(x); + + one.SetOne(); + + if( x == one ) + { + // LnSurrounding1(1) is 0 + SetZero(); + return; + } + + two = 2; + + x1.Sub(one); + x2.Add(one); + + x1.Div(x2); + x2 = x1; + x2.Mul(x1); + + denominator.SetOne(); + SetZero(); + + old_value = *this; + uint i; + + + #ifdef TTMATH_CONSTANTSGENERATOR + for(i=1 ; true ; ++i) + #else + // we begin from 1 in order to not test at the beginning + for(i=1 ; i<=TTMATH_ARITHMETIC_MAX_LOOP ; ++i) + #endif + { + bool testing = ((i & 3) == 0); // it means '(i % 4) == 0' + + next_part = x1; + + if( next_part.Div(denominator) ) + // if there is a carry here we only break the loop + // however the result we return as good + // it means there are too many parts of the formula + break; + + // there shouldn't be a carry here + Add(next_part); + + if( testing ) + { + if( old_value == *this ) + // we've added next (step_test) parts of the formula but the result + // is still the same then we break the loop + break; + else + old_value = *this; + } + + if( x1.Mul(x2) ) + // if there is a carry here the result we return as good + break; + + if( denominator.Add(two) ) + break; + } + + // this = this * 2 + // ( there can't be a carry here because we calculate the logarithm between <1,2) ) + exponent.AddOne(); + + if( steps ) + *steps = i; + } + + + + +public: + + + /*! + Natural logarithm this = ln(x) + (a logarithm with the base equal 'e') + + we're using the fact that our value is stored in form of: + x = mantissa * 2^exponent + then + ln(x) = ln (mantissa * 2^exponent) = ln (mantissa) + (exponent * ln (2)) + + the mantissa we'll show as a value from range <1,2) because the logarithm + is decreasing too fast when 'x' is going to 0 + + return values: + 0 - ok + 1 - overflow (carry) + 2 - incorrect argument (x<=0) + */ + uint Ln(const Big & x) + { + if( x.IsNan() ) + return CheckCarry(1); + + if( x.IsSign() || x.IsZero() ) + { + SetNan(); + return 2; + } + + Big exponent_temp; + exponent_temp.FromInt( x.exponent ); + + // m will be the value of the mantissa in range <1,2) + Big m(x); + m.exponent = -sint(man*TTMATH_BITS_PER_UINT - 1); + + // we must add 'man*TTMATH_BITS_PER_UINT-1' because we've taken it from the mantissa + uint c = exponent_temp.Add(man*TTMATH_BITS_PER_UINT-1); + + LnSurrounding1(m); + + Big ln2; + ln2.SetLn2(); + c += exponent_temp.Mul(ln2); + c += Add(exponent_temp); + + return CheckCarry(c); + } + + + /*! + Logarithm from 'x' with a 'base' + + we're using the formula: + Log(x) with 'base' = ln(x) / ln(base) + + return values: + 0 - ok + 1 - overflow + 2 - incorrect argument (x<=0) + 3 - incorrect base (a<=0 lub a=1) + */ + uint Log(const Big & x, const Big & base) + { + if( x.IsNan() || base.IsNan() ) + return CheckCarry(1); + + if( x.IsSign() || x.IsZero() ) + { + SetNan(); + return 2; + } + + Big denominator;; + denominator.SetOne(); + + if( base.IsSign() || base.IsZero() || base==denominator ) + { + SetNan(); + return 3; + } + + if( x == denominator ) // (this is: if x == 1) + { + // log(1) is 0 + SetZero(); + return 0; + } + + // another error values we've tested at the beginning + // there can only be a carry + uint c = Ln(x); + + c += denominator.Ln(base); + c += Div(denominator); + + return CheckCarry(c); + } + + + + + /*! + * + * converting methods + * + */ + + + /*! + converting from another type of a Big object + */ + template + uint FromBig(const Big & another) + { + info = another.info; + + if( IsNan() ) + return 1; + + if( exponent.FromInt(another.exponent) ) + { + SetNan(); + return 1; + } + + uint man_len_min = (man < another_man)? man : another_man; + uint i; + uint c = 0; + + for( i = 0 ; i another_man )' and 'if( man < another_man )' and there'll be no such situation here + #ifdef _MSC_VER + #pragma warning( disable: 4307 ) + #endif + + if( man > another_man ) + { + uint man_diff = (man - another_man) * TTMATH_BITS_PER_UINT; + c += exponent.SubInt(man_diff, 0); + } + else + if( man < another_man ) + { + uint man_diff = (another_man - man) * TTMATH_BITS_PER_UINT; + c += exponent.AddInt(man_diff, 0); + } + + #ifdef _MSC_VER + #pragma warning( default: 4307 ) + #endif + + // mantissa doesn't have to be standardized (either the highest bit is set or all bits are equal zero) + CorrectZero(); + + return CheckCarry(c); + } + + +private: + + /*! + an auxiliary method for converting 'this' into 'result' + if the value is too big this method returns a carry (1) + */ + uint ToUIntOrInt(uint & result) const + { + result = 0; + + if( IsZero() ) + return 0; + + sint maxbit = -sint(man*TTMATH_BITS_PER_UINT); + + if( exponent > maxbit + sint(TTMATH_BITS_PER_UINT) ) + // if exponent > (maxbit + sint(TTMATH_BITS_PER_UINT)) the value can't be passed + // into the 'sint' type (it's too big) + return 1; + + if( exponent <= maxbit ) + // our value is from the range of (-1,1) and we return zero + return 0; + + // exponent is from a range of (maxbit, maxbit + sint(TTMATH_BITS_PER_UINT) > + // and [maxbit + sint(TTMATH_BITS_PER_UINT] <= 0 + sint how_many_bits = exponent.ToInt(); + + // how_many_bits is negative, we'll make it positive + how_many_bits = -how_many_bits; + + result = (mantissa.table[man-1] >> (how_many_bits % TTMATH_BITS_PER_UINT)); + + return 0; + } + + +public: + + /*! + this method converts 'this' into uint + */ + uint ToUInt() const + { + uint result; + + ToUInt(result); + + return result; + } + + + /*! + this method converts 'this' into 'result' + + if the value is too big this method returns a carry (1) + */ + uint ToUInt(uint & result) const + { + if( ToUIntOrInt(result) ) + return 1; + + if( IsSign() ) + return 1; + + return 0; + } + + + /*! + this method converts 'this' into sint + */ + sint ToInt() const + { + sint result; + + ToInt(result); + + return result; + } + + + /*! + this method converts 'this' into 'result' + + if the value is too big this method returns a carry (1) + */ + uint ToInt(uint & result) const + { + return ToUInt(result); + } + + + /*! + this method converts 'this' into 'result' + + if the value is too big this method returns a carry (1) + */ + uint ToInt(sint & result) const + { + uint result_uint; + + uint c = ToUIntOrInt(result_uint); + result = sint(result_uint); + + if( c ) + return 1; + + uint mask = 0; + + if( IsSign() ) + { + mask = TTMATH_UINT_MAX_VALUE; + result = -result; + } + + return ((result & TTMATH_UINT_HIGHEST_BIT) == (mask & TTMATH_UINT_HIGHEST_BIT)) ? 0 : 1; + } + + +private: + + /*! + an auxiliary method for converting 'this' into 'result' + + if the value is too big this method returns a carry (1) + */ + template + uint ToUIntOrInt(UInt & result) const + { + result.SetZero(); + + if( IsZero() ) + return 0; + + sint maxbit = -sint(man*TTMATH_BITS_PER_UINT); + + if( exponent > maxbit + sint(int_size*TTMATH_BITS_PER_UINT) ) + // if exponent > (maxbit + sint(int_size*TTMATH_BITS_PER_UINT)) the value can't be passed + // into the 'UInt' type (it's too big) + return 1; + + if( exponent <= maxbit ) + // our value is from range (-1,1) and we return zero + return 0; + + sint how_many_bits = exponent.ToInt(); + + if( how_many_bits < 0 ) + { + how_many_bits = -how_many_bits; + uint index = how_many_bits / TTMATH_BITS_PER_UINT; + + UInt mantissa_temp(mantissa); + mantissa_temp.Rcr( how_many_bits % TTMATH_BITS_PER_UINT, 0 ); + + for(uint i=index, a=0 ; i maxbit + sint(int_size*TTMATH_BITS_PER_UINT) )" + // but gcc doesn't understand our types - exponent is Int<> + + for(uint i=0 ; i + uint ToUInt(UInt & result) const + { + uint c = ToUIntOrInt(result); + + if( c ) + return 1; + + if( IsSign() ) + return 1; + + return 0; + } + + + /*! + this method converts 'this' into 'result' + + if the value is too big this method returns a carry (1) + */ + template + uint ToInt(UInt & result) const + { + return ToUInt(result); + } + + + /*! + this method converts 'this' into 'result' + + if the value is too big this method returns a carry (1) + */ + template + uint ToInt(Int & result) const + { + uint c = ToUIntOrInt(result); + + if( c ) + return 1; + + uint mask = 0; + + if( IsSign() ) + { + result.ChangeSign(); + mask = TTMATH_UINT_MAX_VALUE; + } + + return ((result.table[int_size-1] & TTMATH_UINT_HIGHEST_BIT) == (mask & TTMATH_UINT_HIGHEST_BIT))? 0 : 1; + } + + + /*! + a method for converting 'uint' to this class + */ + uint FromUInt(uint value) + { + if( value == 0 ) + { + SetZero(); + return 0; + } + + info = 0; + + for(uint i=0 ; i> 20; + uint m1 = ((temp.u[1] & 0xFFFFFu) << 11) | (temp.u[0] >> 21); + uint m2 = temp.u[0] << 11; + + if( e == 2047 ) + { + // If E=2047 and F is nonzero, then V=NaN ("Not a number") + // If E=2047 and F is zero and S is 1, then V=-Infinity + // If E=2047 and F is zero and S is 0, then V=Infinity + + // we do not support -Infinity and +Infinity + // we assume that there is always NaN + + SetNan(); + } + else + if( e > 0 ) + { + // If 0 m; + m.table[1] = m1; + m.table[0] = m2; + uint moved = m.CompensationToLeft(); + + FromDouble_SetExpAndMan((temp.u[1] & 0x80000000u) != 0, + e - 1022 - man*TTMATH_BITS_PER_UINT + 1 - moved, 0, + m.table[1], m.table[0]); + } + else + { + // If E=0 and F is zero and S is 1, then V=-0 + // If E=0 and F is zero and S is 0, then V=0 + + // we do not support -0 or 0, only is one 0 + SetZero(); + } + } + + return 0; // never be a carry + } + + +private: + + void FromDouble_SetExpAndMan(bool is_sign, int e, uint mhighest, uint m1, uint m2) + { + exponent = e; + + if( man > 1 ) + { + mantissa.table[man-1] = m1 | mhighest; + mantissa.table[sint(man-2)] = m2; + // although man>1 we're using casting into sint + // to get rid from a warning which generates Microsoft Visual: + // warning C4307: '*' : integral constant overflow + + for(uint i=0 ; i> 52; + uint m = (temp.u & 0xFFFFFFFFFFFFFul) << 11; + + if( e == 2047 ) + { + // If E=2047 and F is nonzero, then V=NaN ("Not a number") + // If E=2047 and F is zero and S is 1, then V=-Infinity + // If E=2047 and F is zero and S is 0, then V=Infinity + + // we do not support -Infinity and +Infinity + // we assume that there is always NaN + + SetNan(); + } + else + if( e > 0 ) + { + // If 0> 23) & 0xff) == 0xff ) + { + if( (temp.u & 0x7FFFFF) == 0 ) + return true; // +/- infinity + } + + return false; + } + + +public: + + /*! + this method converts from this class into the 'float' + + if the value is too big: + 'result' will be +/-infinity (depending on the sign) + if the value is too small: + 'result' will be 0 + */ + float ToFloat() const + { + float result; + + ToFloat(result); + + return result; + } + + + /*! + this method converts from this class into the 'float' + + if the value is too big: + 'result' will be +/-infinity (depending on the sign) + and the method returns 1 + if the value is too small: + 'result' will be 0 + and the method returns 1 + */ + uint ToFloat(float & result) const + { + double result_double; + + uint c = ToDouble(result_double); + result = float(result_double); + + if( result == -0.0f ) + result = 0.0f; + + if( c ) + return 1; + + // although the result_double can have a correct value + // but after converting to float there can be infinity + + if( IsInf(result) ) + return 1; + + if( result == 0.0f && result_double != 0.0 ) + // result_double was too small for float + return 1; + + return 0; + } + + + /*! + this method converts from this class into the 'double' + + if the value is too big: + 'result' will be +/-infinity (depending on the sign) + and the method returns 1 + if the value is too small: + 'result' will be 0 + and the method returns 1 + */ + uint ToDouble(double & result) const + { + if( IsZero() ) + { + result = 0.0; + return 0; + } + + if( IsNan() ) + { + result = ToDouble_SetDouble( false, 2047, 0, false, true); + + return 0; + } + + sint e_correction = sint(man*TTMATH_BITS_PER_UINT) - 1; + + if( exponent >= 1024 - e_correction ) + { + // +/- infinity + result = ToDouble_SetDouble( IsSign(), 2047, 0, true); + + return 1; + } + else + if( exponent <= -1023 - 52 - e_correction ) + { + // too small value - we assume that there'll be a zero + result = 0; + + // and return a carry + return 1; + } + + sint e = exponent.ToInt() + e_correction; + + if( e <= -1023 ) + { + // -1023-52 < e <= -1023 (unnormalized value) + result = ToDouble_SetDouble( IsSign(), 0, -(e + 1023)); + } + else + { + // -1023 < e < 1024 + result = ToDouble_SetDouble( IsSign(), e + 1023, -1); + } + + return 0; + } + +private: + +#ifdef TTMATH_PLATFORM32 + + // 32bit platforms + double ToDouble_SetDouble(bool is_sign, uint e, sint move, bool infinity = false, bool nan = false) const + { + union + { + double d; + uint u[2]; // two 32bit words + } temp; + + temp.u[0] = temp.u[1] = 0; + + if( is_sign ) + temp.u[1] |= 0x80000000u; + + temp.u[1] |= (e << 20) & 0x7FF00000u; + + if( nan ) + { + temp.u[0] |= 1; + return temp.d; + } + + if( infinity ) + return temp.d; + + UInt<2> m; + m.table[1] = mantissa.table[man-1]; + m.table[0] = ( man > 1 ) ? mantissa.table[sint(man-2)] : 0; + // although man>1 we're using casting into sint + // to get rid from a warning which generates Microsoft Visual: + // warning C4307: '*' : integral constant overflow + + m.Rcr( 12 + move ); + m.table[1] &= 0xFFFFFu; // cutting the 20 bit (when 'move' was -1) + + temp.u[1] |= m.table[1]; + temp.u[0] |= m.table[0]; + + return temp.d; + } + +#else + + // 64bit platforms + double ToDouble_SetDouble(bool is_sign, uint e, sint move, bool infinity = false, bool nan = false) const + { + union + { + double d; + uint u; // 64bit word + } temp; + + temp.u = 0; + + if( is_sign ) + temp.u |= 0x8000000000000000ul; + + temp.u |= (e << 52) & 0x7FF0000000000000ul; + + if( nan ) + { + temp.u |= 1; + return temp.d; + } + + if( infinity ) + return temp.d; + + uint m = mantissa.table[man-1]; + + m >>= ( 12 + move ); + m &= 0xFFFFFFFFFFFFFul; // cutting the 20 bit (when 'move' was -1) + temp.u |= m; + + return temp.d; + } + +#endif + + +public: + + + /*! + an operator= for converting 'sint' to this class + */ + Big & operator=(sint value) + { + FromInt(value); + + return *this; + } + + + /*! + an operator= for converting 'uint' to this class + */ + Big & operator=(uint value) + { + FromUInt(value); + + return *this; + } + + + /*! + an operator= for converting 'float' to this class + */ + Big & operator=(float value) + { + FromFloat(value); + + return *this; + } + + + /*! + an operator= for converting 'double' to this class + */ + Big & operator=(double value) + { + FromDouble(value); + + return *this; + } + + + /*! + a constructor for converting 'sint' to this class + */ + Big(sint value) + { + FromInt(value); + } + + /*! + a constructor for converting 'uint' to this class + */ + Big(uint value) + { + FromUInt(value); + } + + + /*! + a constructor for converting 'double' to this class + */ + Big(double value) + { + FromDouble(value); + } + + + /*! + a constructor for converting 'float' to this class + */ + Big(float value) + { + FromFloat(value); + } + + +#ifdef TTMATH_PLATFORM32 + + /*! + this method converts 'this' into 'result' (64 bit unsigned integer) + if the value is too big this method returns a carry (1) + */ + uint ToUInt(ulint & result) const + { + UInt<2> temp; // 64 bits container + + uint c = ToUInt(temp); + temp.ToUInt(result); + + return c; + } + + + /*! + this method converts 'this' into 'result' (64 bit unsigned integer) + if the value is too big this method returns a carry (1) + */ + uint ToInt(ulint & result) const + { + return ToUInt(result); + } + + + /*! + this method converts 'this' into 'result' (64 bit unsigned integer) + if the value is too big this method returns a carry (1) + */ + uint ToInt(slint & result) const + { + Int<2> temp; // 64 bits container + + uint c = ToInt(temp); + temp.ToInt(result); + + return c; + } + + + /*! + a method for converting 'ulint' (64bit unsigned integer) to this class + */ + uint FromUInt(ulint value) + { + if( value == 0 ) + { + SetZero(); + return 0; + } + + info = 0; + + if( man == 1 ) + { + sint bit = mantissa.FindLeadingBitInWord(uint(value >> TTMATH_BITS_PER_UINT)); + + if( bit != -1 ) + { + // the highest word from value is different from zero + bit += 1; + value >>= bit; + exponent = bit; + } + else + { + exponent.SetZero(); + } + + mantissa.table[0] = uint(value); + } + else + { + #ifdef _MSC_VER + //warning C4307: '*' : integral constant overflow + #pragma warning( disable: 4307 ) + #endif + + // man >= 2 + mantissa.table[man-1] = uint(value >> TTMATH_BITS_PER_UINT); + mantissa.table[man-2] = uint(value); + + #ifdef _MSC_VER + //warning C4307: '*' : integral constant overflow + #pragma warning( default: 4307 ) + #endif + + exponent = -sint(man-2) * sint(TTMATH_BITS_PER_UINT); + + for(uint i=0 ; i & operator=(ulint value) + { + FromUInt(value); + + return *this; + } + + + /*! + a constructor for converting 'slint' (64bit signed integer) to this class + */ + Big(slint value) + { + FromInt(value); + } + + + /*! + an operator for converting 'slint' (64bit signed integer) to this class + */ + Big & operator=(slint value) + { + FromInt(value); + + return *this; + } + +#endif + + + +#ifdef TTMATH_PLATFORM64 + + + /*! + this method converts 'this' into 'result' (32 bit unsigned integer) + ***this method is created only on a 64bit platform*** + if the value is too big this method returns a carry (1) + */ + uint ToUInt(unsigned int & result) const + { + uint result_uint; + + uint c = ToUInt(result_uint); + result = (unsigned int)result_uint; + + if( c || result_uint != uint(result) ) + return 1; + + return 0; + } + + + /*! + this method converts 'this' into 'result' (32 bit unsigned integer) + ***this method is created only on a 64bit platform*** + if the value is too big this method returns a carry (1) + */ + uint ToInt(unsigned int & result) const + { + return ToUInt(result); + } + + + /*! + this method converts 'this' into 'result' (32 bit signed integer) + ***this method is created only on a 64bit platform*** + if the value is too big this method returns a carry (1) + */ + uint ToInt(signed int & result) const + { + sint result_sint; + + uint c = ToInt(result_sint); + result = (signed int)result_sint; + + if( c || result_sint != sint(result) ) + return 1; + + return 0; + } + + + /* + this method converts 32 bit unsigned int to this class + ***this method is created only on a 64bit platform*** + */ + uint FromUInt(unsigned int value) + { + return FromUInt(uint(value)); + } + + + /* + this method converts 32 bit unsigned int to this class + ***this method is created only on a 64bit platform*** + */ + uint FromInt(unsigned int value) + { + return FromUInt(uint(value)); + } + + + /* + this method converts 32 bit signed int to this class + ***this method is created only on a 64bit platform*** + */ + uint FromInt(signed int value) + { + return FromInt(sint(value)); + } + + + /*! + an operator= for converting 32 bit unsigned int to this class + ***this operator is created only on a 64bit platform*** + */ + Big & operator=(unsigned int value) + { + FromUInt(value); + + return *this; + } + + + /*! + a constructor for converting 32 bit unsigned int to this class + ***this constructor is created only on a 64bit platform*** + */ + Big(unsigned int value) + { + FromUInt(value); + } + + + /*! + an operator for converting 32 bit signed int to this class + ***this operator is created only on a 64bit platform*** + */ + Big & operator=(signed int value) + { + FromInt(value); + + return *this; + } + + + /*! + a constructor for converting 32 bit signed int to this class + ***this constructor is created only on a 64bit platform*** + */ + Big(signed int value) + { + FromInt(value); + } + +#endif + + +private: + + /*! + an auxiliary method for converting from UInt and Int + + we assume that there'll never be a carry here + (we have an exponent and the value in Big can be bigger than + that one from the UInt) + */ + template + uint FromUIntOrInt(const UInt & value, sint compensation) + { + uint minimum_size = (int_size < man)? int_size : man; + exponent = (sint(int_size)-sint(man)) * sint(TTMATH_BITS_PER_UINT) - compensation; + + // copying the highest words + uint i; + for(i=1 ; i<=minimum_size ; ++i) + mantissa.table[man-i] = value.table[int_size-i]; + + // setting the rest of mantissa.table into zero (if some has left) + for( ; i<=man ; ++i) + mantissa.table[man-i] = 0; + + // the highest bit is either one or zero (when the whole mantissa is zero) + // we can only call CorrectZero() + CorrectZero(); + + return 0; + } + + +public: + + /*! + a method for converting from 'UInt' to this class + */ + template + uint FromUInt(UInt value) + { + info = 0; + sint compensation = (sint)value.CompensationToLeft(); + + return FromUIntOrInt(value, compensation); + } + + + /*! + a method for converting from 'UInt' to this class + */ + template + uint FromInt(const UInt & value) + { + return FromUInt(value); + } + + + /*! + a method for converting from 'Int' to this class + */ + template + uint FromInt(Int value) + { + info = 0; + bool is_sign = false; + + if( value.IsSign() ) + { + value.ChangeSign(); + is_sign = true; + } + + sint compensation = (sint)value.CompensationToLeft(); + FromUIntOrInt(value, compensation); + + if( is_sign ) + SetSign(); + + return 0; + } + + + /*! + an operator= for converting from 'Int' to this class + */ + template + Big & operator=(const Int & value) + { + FromInt(value); + + return *this; + } + + + /*! + a constructor for converting from 'Int' to this class + */ + template + Big(const Int & value) + { + FromInt(value); + } + + + /*! + an operator= for converting from 'UInt' to this class + */ + template + Big & operator=(const UInt & value) + { + FromUInt(value); + + return *this; + } + + + /*! + a constructor for converting from 'UInt' to this class + */ + template + Big(const UInt & value) + { + FromUInt(value); + } + + + /*! + an operator= for converting from 'Big' to this class + */ + template + Big & operator=(const Big & value) + { + FromBig(value); + + return *this; + } + + + /*! + a constructor for converting from 'Big' to this class + */ + template + Big(const Big & value) + { + FromBig(value); + } + + + /*! + a default constructor + + by default we don't set any of the members to zero + only NaN flag is set + + if you want the mantissa and exponent to be set to zero + define TTMATH_BIG_DEFAULT_CLEAR macro + (useful for debug purposes) + */ + Big() + { + #ifdef TTMATH_BIG_DEFAULT_CLEAR + + SetZeroNan(); + + #else + + info = TTMATH_BIG_NAN; + // we're directly setting 'info' (instead of calling SetNan()) + // in order to get rid of a warning saying that 'info' is uninitialized + + #endif + } + + + /*! + a destructor + */ + ~Big() + { + } + + + /*! + the default assignment operator + */ + Big & operator=(const Big & value) + { + info = value.info; + exponent = value.exponent; + mantissa = value.mantissa; + + return *this; + } + + + /*! + a constructor for copying from another object of this class + */ + + Big(const Big & value) + { + operator=(value); + } + + + + /*! + a method for converting into a string + struct Conv is defined in ttmathtypes.h, look there for more information about parameters + + output: + return value: + 0 - ok and 'result' will be an object of type std::string (or std::wstring) which holds the value + 1 - if there is a carry (it shoudn't be in a normal situation - if it is that means there + is somewhere an error in the library) + */ + uint ToString( std::string & result, + uint base = 10, + bool scient = false, + sint scient_from = 15, + sint round = -1, + bool trim_zeroes = true, + char comma = '.' ) const + { + Conv conv; + + conv.base = base; + conv.scient = scient; + conv.scient_from = scient_from; + conv.round = round; + conv.trim_zeroes = trim_zeroes; + conv.comma = static_cast(comma); + + return ToStringBase(result, conv); + } + + + /*! + a method for converting into a string + struct Conv is defined in ttmathtypes.h, look there for more information about parameters + */ + uint ToString(std::string & result, const Conv & conv) const + { + return ToStringBase(result, conv); + } + + + /*! + a method for converting into a string + struct Conv is defined in ttmathtypes.h, look there for more information about parameters + */ + std::string ToString(const Conv & conv) const + { + std::string result; + ToStringBase(result, conv); + + return result; + } + + + /*! + a method for converting into a string + struct Conv is defined in ttmathtypes.h, look there for more information about parameters + */ + std::string ToString(uint base = 10) const + { + Conv conv; + conv.base = base; + + return ToString(conv); + } + + + +#ifndef TTMATH_DONT_USE_WCHAR + + + /*! + a method for converting into a string + struct Conv is defined in ttmathtypes.h, look there for more information about parameters + */ + uint ToString( std::wstring & result, + uint base = 10, + bool scient = false, + sint scient_from = 15, + sint round = -1, + bool trim_zeroes = true, + wchar_t comma = '.' ) const + { + Conv conv; + + conv.base = base; + conv.scient = scient; + conv.scient_from = scient_from; + conv.round = round; + conv.trim_zeroes = trim_zeroes; + conv.comma = static_cast(comma); + + return ToStringBase(result, conv); + } + + + /*! + a method for converting into a string + struct Conv is defined in ttmathtypes.h, look there for more information about parameters + */ + uint ToString(std::wstring & result, const Conv & conv) const + { + return ToStringBase(result, conv); + } + + + /*! + a method for converting into a string + struct Conv is defined in ttmathtypes.h, look there for more information about parameters + */ + std::wstring ToWString(const Conv & conv) const + { + std::wstring result; + ToStringBase(result, conv); + + return result; + } + + + /*! + a method for converting into a string + struct Conv is defined in ttmathtypes.h, look there for more information about parameters + */ + std::wstring ToWString(uint base = 10) const + { + Conv conv; + conv.base = base; + + return ToWString(conv); + } + +#endif + + + +private: + + + /*! + an auxiliary method for converting into the string + */ + template + uint ToStringBase(string_type & result, const Conv & conv) const + { + static char error_overflow_msg[] = "overflow"; + static char error_nan_msg[] = "NaN"; + result.erase(); + + if( IsNan() ) + { + Misc::AssignString(result, error_nan_msg); + return 0; + } + + if( conv.base<2 || conv.base>16 ) + { + Misc::AssignString(result, error_overflow_msg); + return 1; + } + + if( IsZero() ) + { + result = '0'; + + return 0; + } + + /* + since 'base' is greater or equal 2 that 'new_exp' of type 'Int' should + hold the new value of exponent but we're using 'Int' because + if the value for example would be 'max()' then we couldn't show it + + max() -> 11111111 * 2 ^ 11111111111 (bin)(the mantissa and exponent have all bits set) + if we were using 'Int' we couldn't show it in this format: + 1,1111111 * 2 ^ 11111111111 (bin) + because we have to add something to the mantissa and because + mantissa is full we can't do it and it'll be a carry + (look at ToString_SetCommaAndExponent(...)) + + when the base would be greater than two (for example 10) + we could use 'Int' here + */ + Int new_exp; + + if( ToString_CreateNewMantissaAndExponent(result, conv, new_exp) ) + { + Misc::AssignString(result, error_overflow_msg); + return 1; + } + + + if( ToString_SetCommaAndExponent(result, conv, new_exp) ) + { + Misc::AssignString(result, error_overflow_msg); + return 1; + } + + if( IsSign() ) + result.insert(result.begin(), '-'); + + + // converted successfully + return 0; + } + + + + /*! + in the method 'ToString_CreateNewMantissaAndExponent()' we're using + type 'Big' and we should have the ability to use some + necessary methods from that class (methods which are private here) + */ + friend class Big; + + + /*! + an auxiliary method for converting into the string + + input: + base - the base in range <2,16> + + output: + return values: + 0 - ok + 1 - if there was a carry + new_man - the new mantissa for 'base' + new_exp - the new exponent for 'base' + + mathematic part: + + the value is stored as: + value = mantissa * 2^exponent + we want to show 'value' as: + value = new_man * base^new_exp + + then 'new_man' we'll print using the standard method from UInt<> type for printing + and 'new_exp' is the offset of the comma operator in a system of a base 'base' + + value = mantissa * 2^exponent + value = mantissa * 2^exponent * (base^new_exp / base^new_exp) + value = mantissa * (2^exponent / base^new_exp) * base^new_exp + + look at the part (2^exponent / base^new_exp), there'll be good if we take + a 'new_exp' equal that value when the (2^exponent / base^new_exp) will be equal one + + on account of the 'base' is not as power of 2 (can be from 2 to 16), + this formula will not be true for integer 'new_exp' then in our case we take + 'base^new_exp' _greater_ than '2^exponent' + + if 'base^new_exp' were smaller than '2^exponent' the new mantissa could be + greater than the max value of the container UInt + + value = mantissa * (2^exponent / base^new_exp) * base^new_exp + let M = mantissa * (2^exponent / base^new_exp) then + value = M * base^new_exp + + in our calculation we treat M as floating value showing it as: + M = mm * 2^ee where ee will be <= 0 + + next we'll move all bits of mm into the right when ee is equal zero + abs(ee) must not be too big that only few bits from mm we can leave + + then we'll have: + M = mmm * 2^0 + 'mmm' is the new_man which we're looking for + + + new_exp we calculate in this way: + 2^exponent <= base^new_exp + new_exp >= log base (2^exponent) <- logarithm with the base 'base' from (2^exponent) + + but we need new_exp as integer then we test: + if new_exp is greater than zero and with fraction we add one to new_exp + new_exp = new_exp + 1 (if new_exp>0 and with fraction) + and at the end we take the integer part: + new_exp = int(new_exp) + */ + template + uint ToString_CreateNewMantissaAndExponent( string_type & new_man, const Conv & conv, + Int & new_exp) const + { + uint c = 0; + + if( conv.base<2 || conv.base>16 ) + return 1; + + // special method for base equal 2 + if( conv.base == 2 ) + return ToString_CreateNewMantissaAndExponent_Base2(new_man, new_exp); + + // special method for base equal 4 + if( conv.base == 4 ) + return ToString_CreateNewMantissaAndExponent_BasePow2(new_man, new_exp, 2); + + // special method for base equal 8 + if( conv.base == 8 ) + return ToString_CreateNewMantissaAndExponent_BasePow2(new_man, new_exp, 3); + + // special method for base equal 16 + if( conv.base == 16 ) + return ToString_CreateNewMantissaAndExponent_BasePow2(new_man, new_exp, 4); + + + // this = mantissa * 2^exponent + + // temp = +1 * 2^exponent + // we're using a bigger type than 'big' (look below) + Big temp; + temp.info = 0; + temp.exponent = exponent; + temp.mantissa.SetOne(); + c += temp.Standardizing(); + + // new_exp_ = log base (2^exponent) + // if new_exp_ is positive and with fraction then we add one + Big new_exp_; + c += new_exp_.ToString_Log(temp, conv.base); // this logarithm isn't very complicated + + // rounding up to the nearest integer + if( !new_exp_.IsInteger() ) + { + if( !new_exp_.IsSign() ) + c += new_exp_.AddOne(); // new_exp_ > 0 and with fraction + + new_exp_.SkipFraction(); + } + + if( ToString_CreateNewMantissaTryExponent(new_man, conv, new_exp_, new_exp) ) + { + // in very rare cases there can be an overflow from ToString_CreateNewMantissaTryExponent + // it means that new_exp_ was too small (the problem comes from floating point numbers precision) + // so we increse new_exp_ and try again + new_exp_.AddOne(); + c += ToString_CreateNewMantissaTryExponent(new_man, conv, new_exp_, new_exp); + } + + return (c==0)? 0 : 1; + } + + + + /*! + an auxiliary method for converting into the string + + trying to calculate new_man for given exponent (new_exp_) + if there is a carry it can mean that new_exp_ is too small + */ + template + uint ToString_CreateNewMantissaTryExponent( string_type & new_man, const Conv & conv, + const Big & new_exp_, Int & new_exp) const + { + uint c = 0; + + // because 'base^new_exp' is >= '2^exponent' then + // because base is >= 2 then we've got: + // 'new_exp_' must be smaller or equal 'new_exp' + // and we can pass it into the Int type + // (in fact we're using a greater type then it'll be ok) + c += new_exp_.ToInt(new_exp); + + // base_ = base + Big base_(conv.base); + + // base_ = base_ ^ new_exp_ + c += base_.Pow( new_exp_ ); // use new_exp_ so Pow(Big<> &) version will be used + // if we hadn't used a bigger type than 'Big' then the result + // of this formula 'Pow(...)' would have been with an overflow + + // temp = mantissa * 2^exponent / base_^new_exp_ + Big temp; + temp.info = 0; + temp.mantissa = mantissa; + temp.exponent = exponent; + c += temp.Div(base_); + + // moving all bits of the mantissa into the right + // (how many times to move depend on the exponent) + c += temp.ToString_MoveMantissaIntoRight(); + + // because we took 'new_exp' as small as it was + // possible ([log base (2^exponent)] + 1) that after the division + // (temp.Div( base_ )) the value of exponent should be equal zero or + // minimum smaller than zero then we've got the mantissa which has + // maximum valid bits + temp.mantissa.ToString(new_man, conv.base); + + if( IsInteger() ) + { + // making sure the new mantissa will be without fraction (integer) + ToString_CheckMantissaInteger(new_man, new_exp); + } + else + if( conv.base_round ) + { + c += ToString_BaseRound(new_man, conv, new_exp); + } + + return (c==0)? 0 : 1; + } + + + /*! + this method calculates the logarithm + it is used by ToString_CreateNewMantissaAndExponent() method + + it's not too complicated + because x=+1*2^exponent (mantissa is one) then during the calculation + the Ln(x) will not be making the long formula from LnSurrounding1() + and only we have to calculate 'Ln(base)' but it'll be calculated + only once, the next time we will get it from the 'history' + + x is greater than 0 + base is in <2,16> range + */ + uint ToString_Log(const Big & x, uint base) + { + TTMATH_REFERENCE_ASSERT( x ) + TTMATH_ASSERT( base>=2 && base<=16 ) + + Big temp; + temp.SetOne(); + + if( x == temp ) + { + // log(1) is 0 + SetZero(); + + return 0; + } + + // there can be only a carry + // because the 'x' is in '1+2*exponent' form then + // the long formula from LnSurrounding1() will not be calculated + // (LnSurrounding1() will return one immediately) + uint c = Ln(x); + + if( base==10 && man<=TTMATH_BUILTIN_VARIABLES_SIZE ) + { + // for the base equal 10 we're using SetLn10() instead of calculating it + // (only if we have the constant sufficient big) + temp.SetLn10(); + } + else + { + c += ToString_LogBase(base, temp); + } + + c += Div( temp ); + + return (c==0)? 0 : 1; + } + + +#ifndef TTMATH_MULTITHREADS + + /*! + this method calculates the logarithm of 'base' + it's used in single thread environment + */ + uint ToString_LogBase(uint base, Big & result) + { + TTMATH_ASSERT( base>=2 && base<=16 ) + + // this guardians are initialized before the program runs (static POD types) + static int guardians[15] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + static Big log_history[15]; + uint index = base - 2; + uint c = 0; + + if( guardians[index] == 0 ) + { + Big base_(base); + c += log_history[index].Ln(base_); + guardians[index] = 1; + } + + result = log_history[index]; + + return (c==0)? 0 : 1; + } + +#else + + /*! + this method calculates the logarithm of 'base' + it's used in multi-thread environment + */ + uint ToString_LogBase(uint base, Big & result) + { + TTMATH_ASSERT( base>=2 && base<=16 ) + + // this guardians are initialized before the program runs (static POD types) + volatile static sig_atomic_t guardians[15] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + static Big * plog_history; + uint index = base - 2; + uint c = 0; + + // double-checked locking + if( guardians[index] == 0 ) + { + ThreadLock thread_lock; + + // locking + if( thread_lock.Lock() ) + { + static Big log_history[15]; + + if( guardians[index] == 0 ) + { + plog_history = log_history; + + Big base_(base); + c += log_history[index].Ln(base_); + guardians[index] = 1; + } + } + else + { + // there was a problem with locking, we store the result directly in 'result' object + Big base_(base); + c += result.Ln(base_); + + return (c==0)? 0 : 1; + } + + // automatically unlocking + } + + result = plog_history[index]; + + return (c==0)? 0 : 1; + } + +#endif + + /*! + an auxiliary method for converting into the string (private) + + this method moving all bits from mantissa into the right side + the exponent tell us how many times moving (the exponent is <=0) + */ + uint ToString_MoveMantissaIntoRight() + { + if( exponent.IsZero() ) + return 0; + + // exponent can't be greater than zero + // because we would cat the highest bits of the mantissa + if( !exponent.IsSign() ) + return 1; + + + if( exponent <= -sint(man*TTMATH_BITS_PER_UINT) ) + // if 'exponent' is <= than '-sint(man*TTMATH_BITS_PER_UINT)' + // it means that we must cut the whole mantissa + // (there'll not be any of the valid bits) + return 1; + + // e will be from (-man*TTMATH_BITS_PER_UINT, 0> + sint e = -( exponent.ToInt() ); + mantissa.Rcr(e,0); + + return 0; + } + + + /*! + a special method similar to the 'ToString_CreateNewMantissaAndExponent' + when the 'base' is equal 2 + + we use it because if base is equal 2 we don't have to make those + complicated calculations and the output is directly from the source + (there will not be any small distortions) + */ + template + uint ToString_CreateNewMantissaAndExponent_Base2( string_type & new_man, + Int & new_exp ) const + { + for( sint i=man-1 ; i>=0 ; --i ) + { + uint value = mantissa.table[i]; + + for( uint bit=0 ; bit + uint ToString_CreateNewMantissaAndExponent_BasePow2( string_type & new_man, + Int & new_exp, + uint bits) const + { + sint move; // how many times move the mantissa + UInt man_temp(mantissa); // man+1 for moving + new_exp = exponent; + new_exp.DivInt((sint)bits, move); + + if( move != 0 ) + { + // we're moving the man_temp to left-hand side + if( move < 0 ) + { + move = sint(bits) + move; + new_exp.SubOne(); // when move is < than 0 then new_exp is < 0 too + } + + man_temp.Rcl(move); + } + + + if( bits == 3 ) + { + // base 8 + // now 'move' is greater than or equal 0 + uint len = man*TTMATH_BITS_PER_UINT + move; + return ToString_CreateNewMantissaAndExponent_Base8(new_man, man_temp, len, bits); + } + else + { + // base 4 or 16 + return ToString_CreateNewMantissaAndExponent_Base4or16(new_man, man_temp, bits); + } + } + + + /*! + a special method used to calculate the new mantissa + when the 'base' is equal 8 + + bits is always 3 + + we can use this algorithm when the base is 4 or 16 too + but we have a faster method ToString_CreateNewMantissaAndExponent_Base4or16() + */ + template + uint ToString_CreateNewMantissaAndExponent_Base8( string_type & new_man, + UInt & man_temp, + uint len, + uint bits) const + { + uint shift = TTMATH_BITS_PER_UINT - bits; + uint mask = TTMATH_UINT_MAX_VALUE >> shift; + uint i; + + for( i=0 ; i(Misc::DigitToChar(digit))); + + man_temp.Rcr(bits); + } + + TTMATH_ASSERT( man_temp.IsZero() ) + + return 0; + } + + + /*! + a special method used to calculate the new mantissa + when the 'base' is equal 4 or 16 + + when the base is equal 4 or 16 the bits is 2 or 4 + and because TTMATH_BITS_PER_UINT (32 or 64) is divisible by 2 (or 4) + then we can get digits from the end of our mantissa + */ + template + uint ToString_CreateNewMantissaAndExponent_Base4or16( string_type & new_man, + UInt & man_temp, + uint bits) const + { + TTMATH_ASSERT( TTMATH_BITS_PER_UINT % 2 == 0 ) + TTMATH_ASSERT( TTMATH_BITS_PER_UINT % 4 == 0 ) + + uint shift = TTMATH_BITS_PER_UINT - bits; + uint mask = TTMATH_UINT_MAX_VALUE << shift; + uint digit; + + // table[man] - last word - is different from zero if we moved man_temp + digit = man_temp.table[man]; + + if( digit != 0 ) + new_man += static_cast(Misc::DigitToChar(digit)); + + + for( int i=man-1 ; i>=0 ; --i ) + { + uint shift_local = shift; + uint mask_local = mask; + + while( mask_local != 0 ) + { + digit = man_temp.table[i] & mask_local; + + if( shift_local != 0 ) + digit = digit >> shift_local; + + new_man += static_cast(Misc::DigitToChar(digit)); + mask_local = mask_local >> bits; + shift_local = shift_local - bits; + } + } + + return 0; + } + + + /*! + an auxiliary method for converting into the string + */ + template + bool ToString_RoundMantissaWouldBeInteger(string_type & new_man, const Conv & conv, Int & new_exp) const + { + // if new_exp is greater or equal to zero then we have an integer value, + // if new_exp is equal -1 then we have only one digit after the comma + // and after rounding it would be an integer value + if( !new_exp.IsSign() || new_exp == -1 ) + return true; + + if( new_man.size() >= TTMATH_UINT_HIGHEST_BIT || new_man.size() < 2 ) + return true; // oops, the mantissa is too large for calculating (or too small) - we are not doing the base rounding + + uint i = 0; + char_type digit; + + if( new_exp >= -sint(new_man.size()) ) + { + uint new_exp_abs = -new_exp.ToInt(); + i = new_man.size() - new_exp_abs; // start from the first digit after the comma operator + } + + if( Misc::CharToDigit(new_man[new_man.size()-1]) >= conv.base/2 ) + { + if( new_exp < -sint(new_man.size()) ) + { + // there are some zeroes after the comma operator + // (between the comma and the first digit from the mantissa) + // and the result value will never be an integer + return false; + } + + digit = static_cast( Misc::DigitToChar(conv.base-1) ); + } + else + { + digit = '0'; + } + + for( ; i < new_man.size()-1 ; ++i) + if( new_man[i] != digit ) + return false; // it will not be an integer + + return true; // it will be integer after rounding + } + + + /*! + an auxiliary method for converting into the string + (when this is integer) + + after floating point calculating the new mantissa can consist of some fraction + so if our value is integer we should check the new mantissa + (after the decimal point there should be only zeroes) + + often this is a last digit different from zero + ToString_BaseRound would not get rid of it because the method make a test against + an integer value (ToString_RoundMantissaWouldBeInteger) and returns immediately + */ + template + void ToString_CheckMantissaInteger(string_type & new_man, const Int & new_exp) const + { + if( !new_exp.IsSign() ) + return; // return if new_exp >= 0 + + uint i = 0; + uint man_size = new_man.size(); + + if( man_size >= TTMATH_UINT_HIGHEST_BIT ) + return; // ops, the mantissa is too long + + sint sman_size = -sint(man_size); + + if( new_exp >= sman_size ) + { + sint e = new_exp.ToInt(); + e = -e; + // now e means how many last digits from the mantissa should be equal zero + + i = man_size - uint(e); + } + + for( ; i + uint ToString_BaseRound(string_type & new_man, const Conv & conv, Int & new_exp) const + { + // we must have minimum two characters + if( new_man.size() < 2 ) + return 0; + + // assert that there will not be an integer after rounding + if( ToString_RoundMantissaWouldBeInteger(new_man, conv, new_exp) ) + return 0; + + typename string_type::size_type i = new_man.length() - 1; + + // we're erasing the last character + uint digit = Misc::CharToDigit( new_man[i] ); + new_man.erase(i, 1); + uint c = new_exp.AddOne(); + + // if the last character is greater or equal 'base/2' + // we are adding one into the new mantissa + if( digit >= conv.base / 2 ) + ToString_RoundMantissa_AddOneIntoMantissa(new_man, conv); + + return c; + } + + + /*! + an auxiliary method for converting into the string + + this method addes one into the new mantissa + */ + template + void ToString_RoundMantissa_AddOneIntoMantissa(string_type & new_man, const Conv & conv) const + { + if( new_man.empty() ) + return; + + sint i = sint( new_man.length() ) - 1; + bool was_carry = true; + + for( ; i>=0 && was_carry ; --i ) + { + // we can have the comma as well because + // we're using this method later in ToString_CorrectDigitsAfterComma_Round() + // (we're only ignoring it) + if( new_man[i] == static_cast(conv.comma) ) + continue; + + // we're adding one + uint digit = Misc::CharToDigit( new_man[i] ) + 1; + + if( digit == conv.base ) + digit = 0; + else + was_carry = false; + + new_man[i] = static_cast( Misc::DigitToChar(digit) ); + } + + if( i<0 && was_carry ) + new_man.insert( new_man.begin() , '1' ); + } + + + + /*! + an auxiliary method for converting into the string + + this method sets the comma operator and/or puts the exponent + into the string + */ + template + uint ToString_SetCommaAndExponent(string_type & new_man, const Conv & conv, Int & new_exp) const + { + uint carry = 0; + + if( new_man.empty() ) + return carry; + + Int scientific_exp( new_exp ); + + // 'new_exp' depends on the 'new_man' which is stored like this e.g: + // 32342343234 (the comma is at the end) + // we'd like to show it in this way: + // 3.2342343234 (the 'scientific_exp' is connected with this example) + + sint offset = sint( new_man.length() ) - 1; + carry += scientific_exp.Add( offset ); + // there shouldn't have been a carry because we're using + // a greater type -- 'Int' instead of 'Int' + + bool print_scientific = conv.scient; + + if( !print_scientific ) + { + if( scientific_exp > conv.scient_from || scientific_exp < -sint(conv.scient_from) ) + print_scientific = true; + } + + if( !print_scientific ) + ToString_SetCommaAndExponent_Normal(new_man, conv, new_exp); + else + // we're passing the 'scientific_exp' instead of 'new_exp' here + ToString_SetCommaAndExponent_Scientific(new_man, conv, scientific_exp); + + return (carry==0)? 0 : 1; + } + + + /*! + an auxiliary method for converting into the string + */ + template + void ToString_SetCommaAndExponent_Normal(string_type & new_man, const Conv & conv, Int & new_exp ) const + { + if( !new_exp.IsSign() ) // it means: if( new_exp >= 0 ) + ToString_SetCommaAndExponent_Normal_AddingZero(new_man, new_exp); + else + ToString_SetCommaAndExponent_Normal_SetCommaInside(new_man, conv, new_exp); + + + ToString_Group_man(new_man, conv); + } + + + /*! + an auxiliary method for converting into the string + */ + template + void ToString_SetCommaAndExponent_Normal_AddingZero(string_type & new_man, + Int & new_exp) const + { + // we're adding zero characters at the end + // 'i' will be smaller than 'when_scientific' (or equal) + uint i = new_exp.ToInt(); + + if( new_man.length() + i > new_man.capacity() ) + // about 6 characters more (we'll need it for the comma or something) + new_man.reserve( new_man.length() + i + 6 ); + + for( ; i>0 ; --i) + new_man += '0'; + } + + + /*! + an auxiliary method for converting into the string + */ + template + void ToString_SetCommaAndExponent_Normal_SetCommaInside( + string_type & new_man, + const Conv & conv, + Int & new_exp ) const + { + // new_exp is < 0 + + sint new_man_len = sint(new_man.length()); // 'new_man_len' with a sign + sint e = -( new_exp.ToInt() ); // 'e' will be positive + + if( new_exp > -new_man_len ) + { + // we're setting the comma within the mantissa + + sint index = new_man_len - e; + new_man.insert( new_man.begin() + index, static_cast(conv.comma)); + } + else + { + // we're adding zero characters before the mantissa + + uint how_many = e - new_man_len; + string_type man_temp(how_many+1, '0'); + + man_temp.insert( man_temp.begin()+1, static_cast(conv.comma)); + new_man.insert(0, man_temp); + } + + ToString_CorrectDigitsAfterComma(new_man, conv); + } + + + /*! + an auxiliary method for converting into the string + */ + template + void ToString_SetCommaAndExponent_Scientific( string_type & new_man, + const Conv & conv, + Int & scientific_exp ) const + { + if( new_man.empty() ) + return; + + if( new_man.size() > 1 ) + { + new_man.insert( new_man.begin()+1, static_cast(conv.comma) ); + ToString_CorrectDigitsAfterComma(new_man, conv); + } + + ToString_Group_man(new_man, conv); + + if( conv.base == 10 ) + { + new_man += 'e'; + + if( !scientific_exp.IsSign() ) + new_man += '+'; + } + else + { + // the 10 here is meant as the base 'base' + // (no matter which 'base' we're using there'll always be 10 here) + Misc::AddString(new_man, "*10^"); + } + + string_type temp_exp; + scientific_exp.ToString( temp_exp, conv.base ); + + new_man += temp_exp; + } + + + /*! + an auxiliary method for converting into the string + */ + template + void ToString_Group_man(string_type & new_man, const Conv & conv) const + { + typedef typename string_type::size_type StrSize; + + if( conv.group == 0 ) + return; + + // first we're looking for the comma operator + StrSize index = new_man.find(static_cast(conv.comma), 0); + + if( index == string_type::npos ) + index = new_man.size(); + + ToString_Group_man_before_comma(new_man, conv, index); + ToString_Group_man_after_comma(new_man, conv, index+1); + } + + + + /*! + an auxiliary method for converting into the string + */ + template + void ToString_Group_man_before_comma( string_type & new_man, const Conv & conv, + typename string_type::size_type & index) const + { + typedef typename string_type::size_type StrSize; + + uint group = 0; + StrSize i = index; + uint group_digits = conv.group_digits; + + if( group_digits < 1 ) + group_digits = 1; + + // adding group characters before the comma operator + // i>0 because on the first position we don't put any additional grouping characters + for( ; i>0 ; --i, ++group) + { + if( group >= group_digits ) + { + group = 0; + new_man.insert(i, 1, static_cast(conv.group)); + ++index; + } + } + } + + + /*! + an auxiliary method for converting into the string + */ + template + void ToString_Group_man_after_comma(string_type & new_man, const Conv & conv, + typename string_type::size_type index) const + { + uint group = 0; + uint group_digits = conv.group_digits; + + if( group_digits < 1 ) + group_digits = 1; + + for( ; index= group_digits ) + { + group = 0; + new_man.insert(index, 1, static_cast(conv.group)); + ++index; + } + } + } + + + /*! + an auxiliary method for converting into the string + */ + template + void ToString_CorrectDigitsAfterComma( string_type & new_man, + const Conv & conv ) const + { + if( conv.round >= 0 ) + ToString_CorrectDigitsAfterComma_Round(new_man, conv); + + if( conv.trim_zeroes ) + ToString_CorrectDigitsAfterComma_CutOffZeroCharacters(new_man, conv); + } + + + /*! + an auxiliary method for converting into the string + */ + template + void ToString_CorrectDigitsAfterComma_CutOffZeroCharacters( + string_type & new_man, + const Conv & conv) const + { + // minimum two characters + if( new_man.length() < 2 ) + return; + + // we're looking for the index of the last character which is not zero + uint i = uint( new_man.length() ) - 1; + for( ; i>0 && new_man[i]=='0' ; --i ); + + // if there is another character than zero at the end + // we're finishing + if( i == new_man.length() - 1 ) + return; + + // we must have a comma + // (the comma can be removed by ToString_CorrectDigitsAfterComma_Round + // which is called before) + if( new_man.find_last_of(static_cast(conv.comma), i) == string_type::npos ) + return; + + // if directly before the first zero is the comma operator + // we're cutting it as well + if( i>0 && new_man[i]==static_cast(conv.comma) ) + --i; + + new_man.erase(i+1, new_man.length()-i-1); + } + + + /*! + an auxiliary method for converting into the string + */ + template + void ToString_CorrectDigitsAfterComma_Round( + string_type & new_man, + const Conv & conv ) const + { + typedef typename string_type::size_type StrSize; + + // first we're looking for the comma operator + StrSize index = new_man.find(static_cast(conv.comma), 0); + + if( index == string_type::npos ) + // nothing was found (actually there can't be this situation) + return; + + // we're calculating how many digits there are at the end (after the comma) + // 'after_comma' will be greater than zero because at the end + // we have at least one digit + StrSize after_comma = new_man.length() - index - 1; + + // if 'max_digit_after_comma' is greater than 'after_comma' (or equal) + // we don't have anything for cutting + if( static_cast(conv.round) >= after_comma ) + return; + + uint last_digit = Misc::CharToDigit( new_man[ index + conv.round + 1 ], conv.base ); + + // we're cutting the rest of the string + new_man.erase(index + conv.round + 1, after_comma - conv.round); + + if( conv.round == 0 ) + { + // we're cutting the comma operator as well + // (it's not needed now because we've cut the whole rest after the comma) + new_man.erase(index, 1); + } + + if( last_digit >= conv.base / 2 ) + // we must round here + ToString_RoundMantissa_AddOneIntoMantissa(new_man, conv); + } + + + +public: + + /*! + a method for converting a string into its value + + it returns 1 if the value is too big -- we cannot pass it into the range + of our class Big (or if the base is incorrect) + + that means only digits before the comma operator can make this value too big, + all digits after the comma we can ignore + + 'source' - pointer to the string for parsing + + if 'after_source' is set that when this method finishes + it sets the pointer to the new first character after parsed value + + 'value_read' - if the pointer is provided that means the value_read will be true + only when a value has been actually read, there can be situation where only such + a string '-' or '+' will be parsed -- 'after_source' will be different from 'source' but + no value has been read (there are no digits) + on other words if 'value_read' is true -- there is at least one digit in the string + */ + uint FromString(const char * source, uint base = 10, const char ** after_source = 0, bool * value_read = 0) + { + Conv conv; + conv.base = base; + + return FromStringBase(source, conv, after_source, value_read); + } + + + /*! + a method for converting a string into its value + */ + uint FromString(const char * source, const Conv & conv, const char ** after_source = 0, bool * value_read = 0) + { + return FromStringBase(source, conv, after_source, value_read); + } + + + /*! + a method for converting a string into its value + */ + uint FromString(const std::string & string, uint base = 10, const char ** after_source = 0, bool * value_read = 0) + { + return FromString(string.c_str(), base, after_source, value_read); + } + + + /*! + a method for converting a string into its value + */ + uint FromString(const std::string & string, const Conv & conv, const char ** after_source = 0, bool * value_read = 0) + { + return FromString(string.c_str(), conv, after_source, value_read); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + a method for converting a string into its value + */ + uint FromString(const wchar_t * source, uint base = 10, const wchar_t ** after_source = 0, bool * value_read = 0) + { + Conv conv; + conv.base = base; + + return FromStringBase(source, conv, after_source, value_read); + } + + + /*! + a method for converting a string into its value + */ + uint FromString(const wchar_t * source, const Conv & conv, const wchar_t ** after_source = 0, bool * value_read = 0) + { + return FromStringBase(source, conv, after_source, value_read); + } + + + /*! + a method for converting a string into its value + */ + uint FromString(const std::wstring & string, uint base = 10, const wchar_t ** after_source = 0, bool * value_read = 0) + { + return FromString(string.c_str(), base, after_source, value_read); + } + + + /*! + a method for converting a string into its value + */ + uint FromString(const std::wstring & string, const Conv & conv, const wchar_t ** after_source = 0, bool * value_read = 0) + { + return FromString(string.c_str(), conv, after_source, value_read); + } + +#endif + + +private: + + + /*! + an auxiliary method for converting from a string + */ + template + uint FromStringBase(const char_type * source, const Conv & conv, const char_type ** after_source = 0, bool * value_read = 0) + { + bool is_sign; + bool value_read_temp = false; + + if( conv.base<2 || conv.base>16 ) + { + SetNan(); + + if( after_source ) + *after_source = source; + + if( value_read ) + *value_read = value_read_temp; + + return 1; + } + + SetZero(); + FromString_TestSign( source, is_sign ); + + uint c = FromString_ReadPartBeforeComma( source, conv, value_read_temp ); + + if( FromString_TestCommaOperator(source, conv) ) + c += FromString_ReadPartAfterComma( source, conv, value_read_temp ); + + if( value_read_temp && conv.base == 10 ) + c += FromString_ReadScientificIfExists( source ); + + if( is_sign && !IsZero() ) + ChangeSign(); + + if( after_source ) + *after_source = source; + + if( value_read ) + *value_read = value_read_temp; + + return CheckCarry(c); + } + + + /*! + we're testing whether the value is with the sign + + (this method is used from 'FromString_ReadPartScientific' too) + */ + template + void FromString_TestSign( const char_type * & source, bool & is_sign ) + { + Misc::SkipWhiteCharacters(source); + + is_sign = false; + + if( *source == '-' ) + { + is_sign = true; + ++source; + } + else + if( *source == '+' ) + { + ++source; + } + } + + + /*! + we're testing whether there's a comma operator + */ + template + bool FromString_TestCommaOperator(const char_type * & source, const Conv & conv) + { + if( (*source == static_cast(conv.comma)) || + (*source == static_cast(conv.comma2) && conv.comma2 != 0 ) ) + { + ++source; + + return true; + } + + return false; + } + + + /*! + this method reads the first part of a string + (before the comma operator) + */ + template + uint FromString_ReadPartBeforeComma( const char_type * & source, const Conv & conv, bool & value_read ) + { + sint character; + Big temp; + Big base_( conv.base ); + + Misc::SkipWhiteCharacters( source ); + + for( ; true ; ++source ) + { + if( conv.group!=0 && *source==static_cast(conv.group) ) + continue; + + character = Misc::CharToDigit(*source, conv.base); + + if( character == -1 ) + break; + + value_read = true; + temp = character; + + if( Mul(base_) ) + return 1; + + if( Add(temp) ) + return 1; + } + + return 0; + } + + + /*! + this method reads the second part of a string + (after the comma operator) + */ + template + uint FromString_ReadPartAfterComma( const char_type * & source, const Conv & conv, bool & value_read ) + { + sint character; + uint c = 0, power = 0; + UInt<1> power_; + Big sum, base_(conv.base); + + // we don't remove any white characters here + sum.SetZero(); + + for( ; sum.exponent.IsSign() || sum.exponent.IsZero() ; ++source ) + { + if( conv.group!=0 && *source==static_cast(conv.group) ) + continue; + + character = Misc::CharToDigit(*source, conv.base); + + if( character == -1 ) + break; + + value_read = true; + + // there actually shouldn't be a carry here + c += sum.Mul(base_); + c += sum.Add(character); + power += 1; + + if( power == 0 ) + c += 1; + } + + // we could break the parsing somewhere in the middle of the string, + // but the result (value) still can be good + // we should set a correct value of 'source' now + for( ; Misc::CharToDigit(*source, conv.base) != -1 ; ++source ); + + power_ = power; + c += base_.Pow(power_); + c += sum.Div(base_); + c += Add(sum); + + return (c==0)? 0 : 1; + } + + + /*! + this method checks whether there is a scientific part: [e|E][-|+]value + + it is called when the base is 10 and some digits were read before + */ + template + uint FromString_ReadScientificIfExists(const char_type * & source) + { + uint c = 0; + + bool scientific_read = false; + const char_type * before_scientific = source; + + if( FromString_TestScientific(source) ) + c += FromString_ReadPartScientific( source, scientific_read ); + + if( !scientific_read ) + source = before_scientific; + + return (c==0)? 0 : 1; + } + + + + /*! + we're testing whether is there the character 'e' + + this character is only allowed when we're using the base equals 10 + */ + template + bool FromString_TestScientific(const char_type * & source) + { + Misc::SkipWhiteCharacters(source); + + if( *source=='e' || *source=='E' ) + { + ++source; + + return true; + } + + return false; + } + + + /*! + this method reads the exponent (after 'e' character) when there's a scientific + format of value and only when we're using the base equals 10 + */ + template + uint FromString_ReadPartScientific( const char_type * & source, bool & scientific_read ) + { + uint c = 0; + Big new_exponent, temp; + bool was_sign = false; + + FromString_TestSign( source, was_sign ); + c += FromString_ReadPartScientific_ReadExponent( source, new_exponent, scientific_read ); + + if( scientific_read ) + { + if( was_sign ) + new_exponent.ChangeSign(); + + temp = 10; + c += temp.Pow( new_exponent ); + c += Mul(temp); + } + + return (c==0)? 0 : 1; + } + + + /*! + this method reads the value of the extra exponent when scientific format is used + (only when base == 10) + */ + template + uint FromString_ReadPartScientific_ReadExponent( const char_type * & source, Big & new_exponent, bool & scientific_read ) + { + sint character; + Big base, temp; + + Misc::SkipWhiteCharacters(source); + + new_exponent.SetZero(); + base = 10; + + for( ; (character=Misc::CharToDigit(*source, 10)) != -1 ; ++source ) + { + scientific_read = true; + + temp = character; + + if( new_exponent.Mul(base) ) + return 1; + + if( new_exponent.Add(temp) ) + return 1; + } + + return 0; + } + + +public: + + + /*! + a constructor for converting a string into this class + */ + Big(const char * string) + { + FromString( string ); + } + + + /*! + a constructor for converting a string into this class + */ + Big(const std::string & string) + { + FromString( string.c_str() ); + } + + + /*! + an operator= for converting a string into its value + */ + Big & operator=(const char * string) + { + FromString( string ); + + return *this; + } + + + /*! + an operator= for converting a string into its value + */ + Big & operator=(const std::string & string) + { + FromString( string.c_str() ); + + return *this; + } + + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + a constructor for converting a string into this class + */ + Big(const wchar_t * string) + { + FromString( string ); + } + + + /*! + a constructor for converting a string into this class + */ + Big(const std::wstring & string) + { + FromString( string.c_str() ); + } + + + /*! + an operator= for converting a string into its value + */ + Big & operator=(const wchar_t * string) + { + FromString( string ); + + return *this; + } + + + /*! + an operator= for converting a string into its value + */ + Big & operator=(const std::wstring & string) + { + FromString( string.c_str() ); + + return *this; + } + + +#endif + + + + /*! + * + * methods for comparing + * + */ + + + /*! + this method performs the formula 'abs(this) < abs(ss2)' + and returns the result + + (in other words it treats 'this' and 'ss2' as values without a sign) + we don't check the NaN flag + */ + bool SmallerWithoutSignThan(const Big & ss2) const + { + if( IsZero() ) + { + if( ss2.IsZero() ) + // we've got two zeroes + return false; + else + // this==0 and ss2!=0 + return true; + } + + if( ss2.IsZero() ) + // this!=0 and ss2==0 + return false; + + // we're using the fact that all bits in mantissa are pushed + // into the left side -- Standardizing() + if( exponent == ss2.exponent ) + return mantissa < ss2.mantissa; + + return exponent < ss2.exponent; + } + + + /*! + this method performs the formula 'abs(this) > abs(ss2)' + and returns the result + + (in other words it treats 'this' and 'ss2' as values without a sign) + we don't check the NaN flag + */ + bool GreaterWithoutSignThan(const Big & ss2) const + { + if( IsZero() ) + { + if( ss2.IsZero() ) + // we've got two zeroes + return false; + else + // this==0 and ss2!=0 + return false; + } + + if( ss2.IsZero() ) + // this!=0 and ss2==0 + return true; + + // we're using the fact that all bits in mantissa are pushed + // into the left side -- Standardizing() + if( exponent == ss2.exponent ) + return mantissa > ss2.mantissa; + + return exponent > ss2.exponent; + } + + + /*! + this method performs the formula 'abs(this) == abs(ss2)' + and returns the result + + (in other words it treats 'this' and 'ss2' as values without a sign) + we don't check the NaN flag + */ + bool EqualWithoutSign(const Big & ss2) const + { + if( IsZero() ) + { + if( ss2.IsZero() ) + // we've got two zeroes + return true; + else + // this==0 and ss2!=0 + return false; + } + + if( ss2.IsZero() ) + // this!=0 and ss2==0 + return false; + + if( exponent==ss2.exponent && mantissa==ss2.mantissa ) + return true; + + return false; + } + + + bool operator<(const Big & ss2) const + { + if( IsSign() && !ss2.IsSign() ) + // this<0 and ss2>=0 + return true; + + if( !IsSign() && ss2.IsSign() ) + // this>=0 and ss2<0 + return false; + + // both signs are the same + + if( IsSign() ) + return ss2.SmallerWithoutSignThan( *this ); + + return SmallerWithoutSignThan( ss2 ); + } + + + bool operator==(const Big & ss2) const + { + if( IsSign() != ss2.IsSign() ) + return false; + + return EqualWithoutSign( ss2 ); + } + + + bool operator>(const Big & ss2) const + { + if( IsSign() && !ss2.IsSign() ) + // this<0 and ss2>=0 + return false; + + if( !IsSign() && ss2.IsSign() ) + // this>=0 and ss2<0 + return true; + + // both signs are the same + + if( IsSign() ) + return ss2.GreaterWithoutSignThan( *this ); + + return GreaterWithoutSignThan( ss2 ); + } + + + bool operator>=(const Big & ss2) const + { + return !operator<( ss2 ); + } + + + bool operator<=(const Big & ss2) const + { + return !operator>( ss2 ); + } + + + bool operator!=(const Big & ss2) const + { + return !operator==(ss2); + } + + + + + + /*! + * + * standard mathematical operators + * + */ + + + /*! + an operator for changing the sign + + this method is not changing 'this' but the changed value is returned + */ + Big operator-() const + { + Big temp(*this); + + temp.ChangeSign(); + + return temp; + } + + + Big operator-(const Big & ss2) const + { + Big temp(*this); + + temp.Sub(ss2); + + return temp; + } + + Big & operator-=(const Big & ss2) + { + Sub(ss2); + + return *this; + } + + + Big operator+(const Big & ss2) const + { + Big temp(*this); + + temp.Add(ss2); + + return temp; + } + + + Big & operator+=(const Big & ss2) + { + Add(ss2); + + return *this; + } + + + Big operator*(const Big & ss2) const + { + Big temp(*this); + + temp.Mul(ss2); + + return temp; + } + + + Big & operator*=(const Big & ss2) + { + Mul(ss2); + + return *this; + } + + + Big operator/(const Big & ss2) const + { + Big temp(*this); + + temp.Div(ss2); + + return temp; + } + + + Big & operator/=(const Big & ss2) + { + Div(ss2); + + return *this; + } + + + /*! + Prefix operator e.g ++variable + */ + Big & operator++() + { + AddOne(); + + return *this; + } + + + /*! + Postfix operator e.g variable++ + */ + Big operator++(int) + { + Big temp( *this ); + + AddOne(); + + return temp; + } + + + Big & operator--() + { + SubOne(); + + return *this; + } + + + Big operator--(int) + { + Big temp( *this ); + + SubOne(); + + return temp; + } + + + + /*! + * + * bitwise operators + * (we do not define bitwise not) + */ + + + Big operator&(const Big & p2) const + { + Big temp( *this ); + + temp.BitAnd(p2); + + return temp; + } + + + Big & operator&=(const Big & p2) + { + BitAnd(p2); + + return *this; + } + + + Big operator|(const Big & p2) const + { + Big temp( *this ); + + temp.BitOr(p2); + + return temp; + } + + + Big & operator|=(const Big & p2) + { + BitOr(p2); + + return *this; + } + + + Big operator^(const Big & p2) const + { + Big temp( *this ); + + temp.BitXor(p2); + + return temp; + } + + + Big & operator^=(const Big & p2) + { + BitXor(p2); + + return *this; + } + + + + + + + /*! + this method makes an integer value by skipping any fractions + + for example: + 10.7 will be 10 + 12.1 -- 12 + -20.2 -- 20 + -20.9 -- 20 + -0.7 -- 0 + 0.8 -- 0 + */ + void SkipFraction() + { + if( IsNan() || IsZero() ) + return; + + if( !exponent.IsSign() ) + // exponent >=0 -- the value don't have any fractions + return; + + if( exponent <= -sint(man*TTMATH_BITS_PER_UINT) ) + { + // the value is from (-1,1), we return zero + SetZero(); + return; + } + + // exponent is in range (-man*TTMATH_BITS_PER_UINT, 0) + sint e = exponent.ToInt(); + + mantissa.ClearFirstBits( -e ); + + // we don't have to standardize 'Standardizing()' the value because + // there's at least one bit in the mantissa + // (the highest bit which we didn't touch) + } + + + /*! + this method remains only a fraction from the value + + for example: + 30.56 will be 0.56 + -12.67 -- -0.67 + */ + void RemainFraction() + { + if( IsNan() || IsZero() ) + return; + + if( !exponent.IsSign() ) + { + // exponent >= 0 -- the value doesn't have any fractions + // we return zero + SetZero(); + return; + } + + if( exponent <= -sint(man*TTMATH_BITS_PER_UINT) ) + { + // the value is from (-1,1) + // we don't make anything with the value + return; + } + + // e will be from (-man*TTMATH_BITS_PER_UINT, 0) + sint e = exponent.ToInt(); + + sint how_many_bits_leave = sint(man*TTMATH_BITS_PER_UINT) + e; // there'll be a subtraction -- e is negative + mantissa.Rcl( how_many_bits_leave, 0); + + // there'll not be a carry because the exponent is too small + exponent.Sub( how_many_bits_leave ); + + // we must call Standardizing() here + Standardizing(); + } + + + + /*! + this method returns true if the value is integer + (there is no a fraction) + + (we don't check nan) + */ + bool IsInteger() const + { + if( IsZero() ) + return true; + + if( !exponent.IsSign() ) + // exponent >=0 -- the value don't have any fractions + return true; + + if( exponent <= -sint(man*TTMATH_BITS_PER_UINT) ) + // the value is from (-1,1) + return false; + + // exponent is in range (-man*TTMATH_BITS_PER_UINT, 0) + sint e = exponent.ToInt(); + e = -e; // e means how many bits we must check + + uint len = e / TTMATH_BITS_PER_UINT; + uint rest = e % TTMATH_BITS_PER_UINT; + uint i = 0; + + for( ; i 0 ) + { + uint rest_mask = TTMATH_UINT_MAX_VALUE >> (TTMATH_BITS_PER_UINT - rest); + if( (mantissa.table[i] & rest_mask) != 0 ) + return false; + } + + return true; + } + + + /*! + this method rounds to the nearest integer value + (it returns a carry if it was) + + for example: + 2.3 = 2 + 2.8 = 3 + + -2.3 = -2 + -2.8 = 3 + */ + uint Round() + { + Big half; + uint c; + + if( IsNan() ) + return 1; + + if( IsZero() ) + return 0; + + half.Set05(); + + if( IsSign() ) + { + // 'this' is < 0 + c = Sub( half ); + } + else + { + // 'this' is > 0 + c = Add( half ); + } + + SkipFraction(); + + return CheckCarry(c); + } + + + + /*! + * + * input/output operators for standard streams + * + */ + +private: + + /*! + an auxiliary method for outputing to standard streams + */ + template + static ostream_type & OutputToStream(ostream_type & s, const Big & l) + { + string_type ss; + + l.ToString(ss); + s << ss; + + return s; + } + + +public: + + + /*! + output to standard streams + */ + friend std::ostream & operator<<(std::ostream & s, const Big & l) + { + return OutputToStream(s, l); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + output to standard streams + */ + friend std::wostream & operator<<(std::wostream & s, const Big & l) + { + return OutputToStream(s, l); + } + +#endif + + + +private: + + /*! + an auxiliary method for converting from a string + */ + template + static istream_type & InputFromStream(istream_type & s, Big & l) + { + string_type ss; + + // char or wchar_t for operator>> + char_type z, old_z; + bool was_comma = false; + bool was_e = false; + + + // operator>> omits white characters if they're set for ommiting + s >> z; + + if( z=='-' || z=='+' ) + { + ss += z; + s >> z; // we're reading a next character (white characters can be ommited) + } + + old_z = 0; + + // we're reading only digits (base=10) and only one comma operator + for( ; s.good() ; z=static_cast(s.get()) ) + { + if( z=='.' || z==',' ) + { + if( was_comma || was_e ) + // second comma operator or comma operator after 'e' character + break; + + was_comma = true; + } + else + if( z == 'e' || z == 'E' ) + { + if( was_e ) + // second 'e' character + break; + + was_e = true; + } + else + if( z == '+' || z == '-' ) + { + if( old_z != 'e' && old_z != 'E' ) + // '+' or '-' is allowed only after 'e' character + break; + } + else + if( Misc::CharToDigit(z, 10) < 0 ) + break; + + + ss += z; + old_z = z; + } + + // we're leaving the last read character + // (it's not belonging to the value) + s.unget(); + + l.FromString( ss ); + + return s; + } + + + +public: + + /*! + input from standard streams + */ + friend std::istream & operator>>(std::istream & s, Big & l) + { + return InputFromStream(s, l); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + input from standard streams + */ + friend std::wistream & operator>>(std::wistream & s, Big & l) + { + return InputFromStream(s, l); + } + +#endif + +}; + + +} // namespace + +#endif diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmathdec.h b/3rd_party/ttmath-0.9.3/ttmath/ttmathdec.h new file mode 100644 index 00000000..92d3e398 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmathdec.h @@ -0,0 +1,419 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2012, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef headerfilettmathdec +#define headerfilettmathdec + +#include "ttmathtypes.h" +#include "ttmaththreads.h" +#include "ttmathuint.h" + + + +namespace ttmath +{ + +template +class Dec +{ +public: + + UInt value; + unsigned char info; + + + /*! + Sign + the mask of a bit from 'info' which means that there is a sign + (when the bit is set) + */ + #define TTMATH_DEC_SIGN 128 + + + /*! + Not a number + if this bit is set that there is not a valid number + */ + #define TTMATH_DEC_NAN 64 + + + + + Dec() + { + info = TTMATH_DEC_NAN; + } + + + Dec(const char * s) + { + info = TTMATH_DEC_NAN; + FromString(s); + } + + + Dec & operator=(const char * s) + { + FromString(s); + + return *this; + } + + + uint FromString(const char * s, const char ** after_source = 0, bool * value_read = 0) + { + return FromStringBase(s, after_source, value_read); + } + + + void ToString(std::string & result) const + { + ToStringBase(result); + } + + + /*! + this method clears a specific bit in the 'info' variable + + bit is one of: + */ + void ClearInfoBit(unsigned char bit) + { + info = info & (~bit); + } + + + /*! + this method sets a specific bit in the 'info' variable + + bit is one of: + + */ + void SetInfoBit(unsigned char bit) + { + info = info | bit; + } + + + /*! + this method returns true if a specific bit in the 'info' variable is set + + bit is one of: + */ + bool IsInfoBit(unsigned char bit) const + { + return (info & bit) != 0; + } + + + bool IsNan() const + { + return IsInfoBit(TTMATH_DEC_NAN); + } + + + bool IsSign() const + { + return IsInfoBit(TTMATH_DEC_SIGN); + } + + + /*! + this method sets the sign + + e.g. + -1 -> -1 + 2 -> -2 + + we do not check whether there is a zero or not, if you're using this method + you must be sure that the value is (or will be afterwards) different from zero + */ + void SetSign() + { + SetInfoBit(TTMATH_DEC_SIGN); + } + + + void SetNaN() + { + SetInfoBit(TTMATH_DEC_NAN); + } + + + void Abs() + { + ClearInfoBit(TTMATH_DEC_SIGN); + } + + + + uint Add(const Dec & arg) + { + uint c = 0; + + if( IsSign() == arg.IsSign() ) + { + c += value.Add(arg.value); + } + else + { + bool is_sign; + + if( value > arg.value ) + { + is_sign = IsSign(); + value.Sub(arg.value); + } + else + { + is_sign = arg.IsSign(); + UInt temp(this->value); + value = arg.value; + value.Sub(temp); + } + + is_sign ? SetSign() : Abs(); + } + + if( c ) + SetNaN(); + + return (c==0)? 0 : 1; + } + +/* + uint Sub(const Dec & arg) + { + } +*/ + +private: + + + + + + +#ifndef TTMATH_MULTITHREADS + + /*! + */ + void SetMultipler(UInt & result) + { + // this guardian is initialized before the program runs (static POD type) + static int guardian = 0; + static UInt multipler; + + if( guardian == 0 ) + { + multipler = 10; + multipler.Pow(dec_digits); + guardian = 1; + } + + result = multipler; + } + +#else + + /*! + */ + void SetMultipler(UInt & result) + { + // this guardian is initialized before the program runs (static POD type) + volatile static sig_atomic_t guardian = 0; + static UInt * pmultipler; + + // double-checked locking + if( guardian == 0 ) + { + ThreadLock thread_lock; + + // locking + if( thread_lock.Lock() ) + { + static UInt multipler; + + if( guardian == 0 ) + { + pmultipler = &multipler; + multipler = 10; + multipler.Pow(dec_digits); + guardian = 1; + } + } + else + { + // there was a problem with locking, we store the result directly in 'result' object + result = 10; + result.Pow(dec_digits); + + return; + } + + // automatically unlocking + } + + result = *pmultipler; + } + +#endif + + + + /*! + an auxiliary method for converting from a string + */ + template + uint FromStringBase(const char_type * s, const char_type ** after_source = 0, bool * value_read = 0) + { + UInt multipler; + const char_type * after; + uint c = 0; + info = 0; + + Misc::SkipWhiteCharacters(s); + + if( *s == '-' ) + { + s += 1; + SetSign(); + } + else + if( *s == '+' ) + { + s += 1; + } + + c += value.FromString(s, 10, &after, value_read); + + if( after_source ) + *after_source = after; + + SetMultipler(multipler); + c += value.Mul(multipler); + + if( *after == '.' ) + c += FromStringBaseAfterComma(after+1, after_source); + + if( c ) + SetInfoBit(TTMATH_DEC_NAN); + + return (c==0)? 0 : 1; + } + + + template + uint FromStringBaseAfterComma(const char_type * s, const char_type ** after_source = 0, bool * value_read = 0) + { + UInt temp; + UInt multipler; + sint z; + uint c = 0; + size_t i = dec_digits; + + SetMultipler(multipler); + + for( ; i>0 && (z=Misc::CharToDigit(*s, 10)) != -1 ; --i, ++s ) + { + multipler.DivInt(10); + temp.SetZero(); + + if( value_read ) + *value_read = true; + + if( c == 0 ) + { + temp.table[0] = z; + c += temp.Mul(multipler); + c += value.Add(temp); + } + } + + if( i == 0 && (z=Misc::CharToDigit(*s, 10)) != -1 && z >= 5 ) + c += value.AddOne(); + + if( after_source ) + { + while( (z=Misc::CharToDigit(*s, 10)) != -1 ) + s += 1; + + *after_source = s; + } + + return c; + } + + + + template + void ToStringBase(string_type & result) const + { + if( IsNan() ) + { + result = "NaN"; + return; + } + + value.ToStringBase(result, 10, IsSign()); + + if( dec_digits > 0 ) + { + size_t size = result.size(); + + if( IsSign() && size > 0 ) + size -= 1; + + if( dec_digits >= size ) + { + size_t zeroes = dec_digits - size + 1; + size_t start = IsSign() ? 1 : 0; + result.insert(start, zeroes, '0'); + } + + result.insert(result.end() - dec_digits, '.'); + } + } + + + +}; + + +} // namespace + +#endif diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmathint.h b/3rd_party/ttmath-0.9.3/ttmath/ttmathint.h new file mode 100644 index 00000000..ad306f01 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmathint.h @@ -0,0 +1,1922 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2011, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef headerfilettmathint +#define headerfilettmathint + +/*! + \file ttmathint.h + \brief template class Int +*/ + +#include "ttmathuint.h" + +namespace ttmath +{ + + +/*! + \brief Int implements a big integer value with a sign + + value_size - how many bytes specify our value + on 32bit platforms: value_size=1 -> 4 bytes -> 32 bits + on 64bit platforms: value_size=1 -> 8 bytes -> 64 bits + value_size = 1,2,3,4,5,6.... +*/ +template +class Int : public UInt +{ +public: + + /*! + this method sets the max value which this class can hold + (all bits will be one besides the last one) + */ + void SetMax() + { + UInt::SetMax(); + UInt::table[value_size-1] = ~ TTMATH_UINT_HIGHEST_BIT; + } + + + /*! + this method sets the min value which this class can hold + (all bits will be zero besides the last one which is one) + */ + void SetMin() + { + UInt::SetZero(); + UInt::table[value_size-1] = TTMATH_UINT_HIGHEST_BIT; + } + + + /*! + this method sets -1 as the value + (-1 is equal the max value in an unsigned type) + */ + void SetSignOne() + { + UInt::SetMax(); + } + + + /*! + we change the sign of the value + + if it isn't possible to change the sign this method returns 1 + else return 0 and changing the sign + */ + uint ChangeSign() + { + /* + if the value is equal that one which has been returned from SetMin + (only the highest bit is set) that means we can't change sign + because the value is too big (bigger about one) + + e.g. when value_size = 1 and value is -2147483648 we can't change it to the + 2147483648 because the max value which can be held is 2147483647 + + we don't change the value and we're using this fact somewhere in some methods + (if we look on our value without the sign we get the correct value + eg. -2147483648 in Int<1> will be 2147483648 on the UInt<1> type) + */ + if( UInt::IsOnlyTheHighestBitSet() ) + return 1; + + UInt temp(*this); + UInt::SetZero(); + UInt::Sub(temp); + + return 0; + } + + + + /*! + this method sets the sign + + e.g. 1 -> -1 + -2 -> -2 + + from a positive value we make a negative value, + if the value is negative we do nothing + */ + void SetSign() + { + if( IsSign() ) + return; + + ChangeSign(); + } + + + + /*! + this method returns true if there's the sign + + (the highest bit will be converted to the bool) + */ + bool IsSign() const + { + return UInt::IsTheHighestBitSet(); + } + + + + /*! + it sets an absolute value + + it can return carry (1) (look on ChangeSign() for details) + */ + uint Abs() + { + if( !IsSign() ) + return 0; + + return ChangeSign(); + } + + + + + /*! + * + * basic mathematic functions + * + */ + +private: + + uint CorrectCarryAfterAdding(bool p1_is_sign, bool p2_is_sign) + { + if( !p1_is_sign && !p2_is_sign ) + { + if( UInt::IsTheHighestBitSet() ) + return 1; + } + + if( p1_is_sign && p2_is_sign ) + { + if( ! UInt::IsTheHighestBitSet() ) + return 1; + } + + return 0; + } + + +public: + + /*! + this method adds two value with a sign and returns a carry + + we're using methods from the base class because values are stored with U2 + we must only make the carry correction + + this = p1(=this) + p2 + + when p1>=0 i p2>=0 carry is set when the highest bit of value is set + when p1<0 i p2<0 carry is set when the highest bit of value is clear + when p1>=0 i p2<0 carry will never be set + when p1<0 i p2>=0 carry will never be set + */ + uint Add(const Int & ss2) + { + bool p1_is_sign = IsSign(); + bool p2_is_sign = ss2.IsSign(); + + UInt::Add(ss2); + + return CorrectCarryAfterAdding(p1_is_sign, p2_is_sign); + } + + + /*! + this method adds one *unsigned* word (at a specific position) + and returns a carry (if it was) + + look at a description in UInt<>::AddInt(...) + */ + uint AddInt(uint value, uint index = 0) + { + bool p1_is_sign = IsSign(); + + UInt::AddInt(value, index); + + return CorrectCarryAfterAdding(p1_is_sign, false); + } + + + /*! + this method adds two *unsigned* words to the existing value + and these words begin on the 'index' position + + index should be equal or smaller than value_size-2 (index <= value_size-2) + x1 - lower word, x2 - higher word + + look at a description in UInt<>::AddTwoInts(...) + */ + uint AddTwoInts(uint x2, uint x1, uint index) + { + bool p1_is_sign = IsSign(); + + UInt::AddTwoInts(x2, x1, index); + + return CorrectCarryAfterAdding(p1_is_sign, false); + } + +private: + + uint CorrectCarryAfterSubtracting(bool p1_is_sign, bool p2_is_sign) + { + if( !p1_is_sign && p2_is_sign ) + { + if( UInt::IsTheHighestBitSet() ) + return 1; + } + + if( p1_is_sign && !p2_is_sign ) + { + if( ! UInt::IsTheHighestBitSet() ) + return 1; + } + + return 0; + } + +public: + + /*! + this method subtracts two values with a sign + + we don't use the previous Add because the method ChangeSign can + sometimes return carry + + this = p1(=this) - p2 + + when p1>=0 i p2>=0 carry will never be set + when p1<0 i p2<0 carry will never be set + when p1>=0 i p2<0 carry is set when the highest bit of value is set + when p1<0 i p2>=0 carry is set when the highest bit of value is clear + */ + uint Sub(const Int & ss2) + { + bool p1_is_sign = IsSign(); + bool p2_is_sign = ss2.IsSign(); + + UInt::Sub(ss2); + + return CorrectCarryAfterSubtracting(p1_is_sign, p2_is_sign); + } + + + /*! + this method subtracts one *unsigned* word (at a specific position) + and returns a carry (if it was) + */ + uint SubInt(uint value, uint index = 0) + { + bool p1_is_sign = IsSign(); + + UInt::SubInt(value, index); + + return CorrectCarryAfterSubtracting(p1_is_sign, false); + } + + + /*! + this method adds one to the value and returns carry + */ + uint AddOne() + { + bool p1_is_sign = IsSign(); + + UInt::AddOne(); + + return CorrectCarryAfterAdding(p1_is_sign, false); + } + + + /*! + this method subtracts one from the value and returns carry + */ + uint SubOne() + { + bool p1_is_sign = IsSign(); + + UInt::SubOne(); + + return CorrectCarryAfterSubtracting(p1_is_sign, false); + } + + +private: + + + uint CheckMinCarry(bool ss1_is_sign, bool ss2_is_sign) + { + /* + we have to examine the sign of the result now + but if the result is with the sign then: + 1. if the signs were the same that means the result is too big + (the result must be without a sign) + 2. if the signs were different that means if the result + is different from that one which has been returned from SetMin() + that is carry (result too big) but if the result is equal SetMin() + there'll be ok (and the next SetSign will has no effect because + the value is actually negative -- look at description of that case + in ChangeSign()) + */ + if( IsSign() ) + { + if( ss1_is_sign != ss2_is_sign ) + { + /* + there can be one case where signs are different and + the result will be equal the value from SetMin() (only the highest bit is set) + (this situation is ok) + */ + if( !UInt::IsOnlyTheHighestBitSet() ) + return 1; + } + else + { + // signs were the same + return 1; + } + } + + return 0; + } + + +public: + + + /*! + multiplication: this = this * ss2 + + it can return a carry + */ + uint MulInt(sint ss2) + { + bool ss1_is_sign, ss2_is_sign; + uint c; + + ss1_is_sign = IsSign(); + + /* + we don't have to check the carry from Abs (values will be correct + because next we're using the method MulInt from the base class UInt + which is without a sign) + */ + Abs(); + + if( ss2 < 0 ) + { + ss2 = -ss2; + ss2_is_sign = true; + } + else + { + ss2_is_sign = false; + } + + c = UInt::MulInt((uint)ss2); + c += CheckMinCarry(ss1_is_sign, ss2_is_sign); + + if( ss1_is_sign != ss2_is_sign ) + SetSign(); + + return c; + } + + + + /*! + multiplication this = this * ss2 + + it returns carry if the result is too big + (we're using the method from the base class but we have to make + one correction in account of signs) + */ + uint Mul(Int ss2) + { + bool ss1_is_sign, ss2_is_sign; + uint c; + + ss1_is_sign = IsSign(); + ss2_is_sign = ss2.IsSign(); + + /* + we don't have to check the carry from Abs (values will be correct + because next we're using the method Mul from the base class UInt + which is without a sign) + */ + Abs(); + ss2.Abs(); + + c = UInt::Mul(ss2); + c += CheckMinCarry(ss1_is_sign, ss2_is_sign); + + if( ss1_is_sign != ss2_is_sign ) + SetSign(); + + return c; + } + + + /*! + division this = this / ss2 + returned values: + 0 - ok + 1 - division by zero + + for example: (result means 'this') + 20 / 3 --> result: 6 remainder: 2 + -20 / 3 --> result: -6 remainder: -2 + 20 / -3 --> result: -6 remainder: 2 + -20 / -3 --> result: 6 remainder: -2 + + in other words: this(old) = ss2 * this(new)(result) + remainder + */ + uint Div(Int ss2, Int * remainder = 0) + { + bool ss1_is_sign, ss2_is_sign; + + ss1_is_sign = IsSign(); + ss2_is_sign = ss2.IsSign(); + + /* + we don't have to test the carry from Abs as well as in Mul + */ + Abs(); + ss2.Abs(); + + uint c = UInt::Div(ss2, remainder); + + if( ss1_is_sign != ss2_is_sign ) + SetSign(); + + if( ss1_is_sign && remainder ) + remainder->SetSign(); + + return c; + } + + uint Div(const Int & ss2, Int & remainder) + { + return Div(ss2, &remainder); + } + + + /*! + division this = this / ss2 (ss2 is int) + returned values: + 0 - ok + 1 - division by zero + + for example: (result means 'this') + 20 / 3 --> result: 6 remainder: 2 + -20 / 3 --> result: -6 remainder: -2 + 20 / -3 --> result: -6 remainder: 2 + -20 / -3 --> result: 6 remainder: -2 + + in other words: this(old) = ss2 * this(new)(result) + remainder + */ + uint DivInt(sint ss2, sint * remainder = 0) + { + bool ss1_is_sign, ss2_is_sign; + + ss1_is_sign = IsSign(); + + /* + we don't have to test the carry from Abs as well as in Mul + */ + Abs(); + + if( ss2 < 0 ) + { + ss2 = -ss2; + ss2_is_sign = true; + } + else + { + ss2_is_sign = false; + } + + uint rem; + uint c = UInt::DivInt((uint)ss2, &rem); + + if( ss1_is_sign != ss2_is_sign ) + SetSign(); + + if( remainder ) + { + if( ss1_is_sign ) + *remainder = -sint(rem); + else + *remainder = sint(rem); + } + + return c; + } + + + uint DivInt(sint ss2, sint & remainder) + { + return DivInt(ss2, &remainder); + } + + +private: + + + /*! + power this = this ^ pow + this can be negative + pow is >= 0 + */ + uint Pow2(const Int & pow) + { + bool was_sign = IsSign(); + uint c = 0; + + if( was_sign ) + c += Abs(); + + uint c_temp = UInt::Pow(pow); + if( c_temp > 0 ) + return c_temp; // c_temp can be: 0, 1 or 2 + + if( was_sign && (pow.table[0] & 1) == 1 ) + // negative value to the power of odd number is negative + c += ChangeSign(); + + return (c==0)? 0 : 1; + } + + +public: + + + /*! + power this = this ^ pow + + return values: + 0 - ok + 1 - carry + 2 - incorrect arguments 0^0 or 0^(-something) + */ + uint Pow(Int pow) + { + if( !pow.IsSign() ) + return Pow2(pow); + + if( UInt::IsZero() ) + // if 'pow' is negative then + // 'this' must be different from zero + return 2; + + if( pow.ChangeSign() ) + return 1; + + Int t(*this); + uint c_temp = t.Pow2(pow); + if( c_temp > 0 ) + return c_temp; + + UInt::SetOne(); + if( Div(t) ) + return 1; + + return 0; + } + + + + /*! + * + * convertion methods + * + */ +private: + + + /*! + an auxiliary method for converting both from UInt and Int + */ + template + uint FromUIntOrInt(const UInt & p, bool UInt_type) + { + uint min_size = (value_size < argument_size)? value_size : argument_size; + uint i; + + for(i=0 ; i::table[i] = p.table[i]; + + + if( value_size > argument_size ) + { + uint fill; + + if( UInt_type ) + fill = 0; + else + fill = (p.table[argument_size-1] & TTMATH_UINT_HIGHEST_BIT)? + TTMATH_UINT_MAX_VALUE : 0; + + // 'this' is longer than 'p' + for( ; i::table[i] = fill; + } + else + { + uint test = (UInt::table[value_size-1] & TTMATH_UINT_HIGHEST_BIT)? + TTMATH_UINT_MAX_VALUE : 0; + + if( UInt_type && test!=0 ) + return 1; + + for( ; i type into this class + + this operation has mainly sense if the value from p + can be held in this type + + it returns a carry if the value 'p' is too big + */ + template + uint FromInt(const Int & p) + { + return FromUIntOrInt(p, false); + } + + + /*! + this method converts the sint type into this class + */ + uint FromInt(sint value) + { + uint fill = ( value<0 ) ? TTMATH_UINT_MAX_VALUE : 0; + + for(uint i=1 ; i::table[i] = fill; + + UInt::table[0] = uint(value); + + // there'll never be a carry here + return 0; + } + + + /*! + this method converts UInt into this class + */ + template + uint FromUInt(const UInt & p) + { + return FromUIntOrInt(p, true); + } + + + /*! + this method converts UInt into this class + */ + template + uint FromInt(const UInt & p) + { + return FromUIntOrInt(p, true); + } + + + /*! + this method converts the uint type into this class + */ + uint FromUInt(uint value) + { + for(uint i=1 ; i::table[i] = 0; + + UInt::table[0] = value; + + // there can be a carry here when the size of this value is equal one word + // and the 'value' has the highest bit set + if( value_size==1 && (value & TTMATH_UINT_HIGHEST_BIT)!=0 ) + return 1; + + return 0; + } + + + /*! + this method converts the uint type into this class + */ + uint FromInt(uint value) + { + return FromUInt(value); + } + + + /*! + the default assignment operator + */ + Int & operator=(const Int & p) + { + FromInt(p); + + return *this; + } + + + /*! + this operator converts an Int type to this class + + it doesn't return a carry + */ + template + Int & operator=(const Int & p) + { + FromInt(p); + + return *this; + } + + + /*! + this method converts the sint type to this class + */ + Int & operator=(sint i) + { + FromInt(i); + + return *this; + } + + + /*! + a constructor for converting the uint to this class + */ + Int(sint i) + { + FromInt(i); + } + + + /*! + a copy constructor + */ + Int(const Int & u) + { + FromInt(u); + } + + + /*! + a constructor for copying from another types + */ + template + Int(const Int & u) + { + // look that 'size' we still set as 'value_size' and not as u.value_size + FromInt(u); + } + + + + /*! + this operator converts an UInt type to this class + + it doesn't return a carry + */ + template + Int & operator=(const UInt & p) + { + FromUInt(p); + + return *this; + } + + + /*! + this method converts the Uint type to this class + */ + Int & operator=(uint i) + { + FromUInt(i); + + return *this; + } + + + /*! + a constructor for converting the uint to this class + */ + Int(uint i) + { + FromUInt(i); + } + + + /*! + a constructor for copying from another types + */ + template + Int(const UInt & u) + { + // look that 'size' we still set as 'value_size' and not as u.value_size + FromUInt(u); + } + + + +#ifdef TTMATH_PLATFORM32 + + + /*! + this method converts unsigned 64 bit int type to this class + ***this method is created only on a 32bit platform*** + */ + uint FromUInt(ulint n) + { + uint c = UInt::FromUInt(n); + + if( c ) + return 1; + + if( value_size == 1 ) + return ((UInt::table[0] & TTMATH_UINT_HIGHEST_BIT) == 0) ? 0 : 1; + + if( value_size == 2 ) + return ((UInt::table[1] & TTMATH_UINT_HIGHEST_BIT) == 0) ? 0 : 1; + + return 0; + } + + + /*! + this method converts unsigned 64 bit int type to this class + ***this method is created only on a 32bit platform*** + */ + uint FromInt(ulint n) + { + return FromUInt(n); + } + + + /*! + this method converts signed 64 bit int type to this class + ***this method is created only on a 32bit platform*** + */ + uint FromInt(slint n) + { + uint mask = (n < 0) ? TTMATH_UINT_MAX_VALUE : 0; + + UInt::table[0] = (uint)(ulint)n; + + if( value_size == 1 ) + { + if( uint(ulint(n) >> 32) != mask ) + return 1; + + return ((UInt::table[0] & TTMATH_UINT_HIGHEST_BIT) == (mask & TTMATH_UINT_HIGHEST_BIT)) ? 0 : 1; + } + + UInt::table[1] = (uint)(ulint(n) >> 32); + + for(uint i=2 ; i::table[i] = mask; + + return 0; + } + + + /*! + this operator converts unsigned 64 bit int type to this class + ***this operator is created only on a 32bit platform*** + */ + Int & operator=(ulint n) + { + FromUInt(n); + + return *this; + } + + + /*! + a constructor for converting unsigned 64 bit int to this class + ***this constructor is created only on a 32bit platform*** + */ + Int(ulint n) + { + FromUInt(n); + } + + + /*! + this operator converts signed 64 bit int type to this class + ***this operator is created only on a 32bit platform*** + */ + Int & operator=(slint n) + { + FromInt(n); + + return *this; + } + + + /*! + a constructor for converting signed 64 bit int to this class + ***this constructor is created only on a 32bit platform*** + */ + Int(slint n) + { + FromInt(n); + } + +#endif + + + + +#ifdef TTMATH_PLATFORM64 + + /*! + this method converts 32 bit unsigned int type to this class + ***this operator is created only on a 64bit platform*** + */ + uint FromUInt(unsigned int i) + { + return FromUInt(uint(i)); + } + + + /*! + this method converts 32 bit unsigned int type to this class + ***this operator is created only on a 64bit platform*** + */ + uint FromInt(unsigned int i) + { + return FromUInt(i); + } + + + /*! + this method converts 32 bit signed int type to this class + ***this operator is created only on a 64bit platform*** + */ + uint FromInt(signed int i) + { + return FromInt(sint(i)); + } + + + /*! + this method converts 32 bit unsigned int type to this class + ***this operator is created only on a 64bit platform*** + */ + Int & operator=(unsigned int i) + { + FromUInt(i); + + return *this; + } + + + /*! + a constructor for converting 32 bit unsigned int to this class + ***this constructor is created only on a 64bit platform*** + */ + Int(unsigned int i) + { + FromUInt(i); + } + + + /*! + this operator converts 32 bit signed int type to this class + ***this operator is created only on a 64bit platform*** + */ + Int & operator=(signed int i) + { + FromInt(i); + + return *this; + } + + + /*! + a constructor for converting 32 bit signed int to this class + ***this constructor is created only on a 64bit platform*** + */ + Int(signed int i) + { + FromInt(i); + } + +#endif + + + + /*! + a constructor for converting string to this class (with the base=10) + */ + Int(const char * s) + { + FromString(s); + } + + + /*! + a constructor for converting a string to this class (with the base=10) + */ + Int(const std::string & s) + { + FromString( s.c_str() ); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + a constructor for converting string to this class (with the base=10) + */ + Int(const wchar_t * s) + { + FromString(s); + } + + + /*! + a constructor for converting a string to this class (with the base=10) + */ + Int(const std::wstring & s) + { + FromString( s.c_str() ); + } + +#endif + + + /*! + a default constructor + + we don't clear table etc. + */ + Int() + { + } + + + /*! + the destructor + */ + ~Int() + { + } + + + /*! + this method returns the lowest value from table with a sign + + we must be sure when we using this method whether the value + will hold in an sint type or not (the rest value from table must be zero or -1) + */ + sint ToInt() const + { + return sint( UInt::table[0] ); + } + + + /*! + this method converts the value to uint type + can return a carry if the value is too long to store it in uint type + */ + uint ToUInt(uint & result) const + { + uint c = UInt::ToUInt(result); + + if( value_size == 1 ) + return (result & TTMATH_UINT_HIGHEST_BIT) == 0 ? 0 : 1; + + return c; + } + + + /*! + this method converts the value to uint type + can return a carry if the value is too long to store it in uint type + */ + uint ToInt(uint & result) const + { + return ToUInt(result); + } + + + /*! + this method converts the value to sint type + can return a carry if the value is too long to store it in sint type + */ + uint ToInt(sint & result) const + { + result = sint( UInt::table[0] ); + uint mask = IsSign() ? TTMATH_UINT_MAX_VALUE : 0; + + if( (result & TTMATH_UINT_HIGHEST_BIT) != (mask & TTMATH_UINT_HIGHEST_BIT) ) + return 1; + + for(uint i=1 ; i::table[i] != mask ) + return 1; + + return 0; + } + + +#ifdef TTMATH_PLATFORM32 + + /*! + this method converts the value to ulint type (64 bit unsigned integer) + can return a carry if the value is too long to store it in ulint type + *** this method is created only on a 32 bit platform *** + */ + uint ToUInt(ulint & result) const + { + uint c = UInt::ToUInt(result); + + if( value_size == 1 ) + return (UInt::table[0] & TTMATH_UINT_HIGHEST_BIT) == 0 ? 0 : 1; + + if( value_size == 2 ) + return (UInt::table[1] & TTMATH_UINT_HIGHEST_BIT) == 0 ? 0 : 1; + + return c; + } + + + /*! + this method converts the value to ulint type (64 bit unsigned integer) + can return a carry if the value is too long to store it in ulint type + *** this method is created only on a 32 bit platform *** + */ + uint ToInt(ulint & result) const + { + return ToUInt(result); + } + + + /*! + this method converts the value to slint type (64 bit signed integer) + can return a carry if the value is too long to store it in slint type + *** this method is created only on a 32 bit platform *** + */ + uint ToInt(slint & result) const + { + if( value_size == 1 ) + { + result = slint(sint(UInt::table[0])); + } + else + { + uint low = UInt::table[0]; + uint high = UInt::table[1]; + + result = low; + result |= (ulint(high) << TTMATH_BITS_PER_UINT); + + uint mask = IsSign() ? TTMATH_UINT_MAX_VALUE : 0; + + if( (high & TTMATH_UINT_HIGHEST_BIT) != (mask & TTMATH_UINT_HIGHEST_BIT) ) + return 1; + + for(uint i=2 ; i::table[i] != mask ) + return 1; + } + + return 0; + } + +#endif + + + +#ifdef TTMATH_PLATFORM64 + + /*! + this method converts the value to a 32 bit unsigned integer + can return a carry if the value is too long to store it in this type + *** this method is created only on a 64 bit platform *** + */ + uint ToUInt(unsigned int & result) const + { + uint c = UInt::ToUInt(result); + + if( c || IsSign() ) + return 1; + + return 0; + } + + + /*! + this method converts the value to a 32 bit unsigned integer + can return a carry if the value is too long to store it in this type + *** this method is created only on a 64 bit platform *** + */ + uint ToInt(unsigned int & result) const + { + return ToUInt(result); + } + + + /*! + this method converts the value to a 32 bit signed integer + can return a carry if the value is too long to store it in this type + *** this method is created only on a 64 bit platform *** + */ + uint ToInt(int & result) const + { + uint first = UInt::table[0]; + + result = int(first); + uint mask = IsSign() ? TTMATH_UINT_MAX_VALUE : 0; + + if( (first >> 31) != (mask >> 31) ) + return 1; + + for(uint i=1 ; i::table[i] != mask ) + return 1; + + return 0; + } + +#endif + + + + +private: + + /*! + an auxiliary method for converting to a string + */ + template + void ToStringBase(string_type & result, uint b = 10) const + { + if( IsSign() ) + { + Int temp(*this); + temp.Abs(); + temp.UInt::ToStringBase(result, b, true); + } + else + { + UInt::ToStringBase(result, b, false); + } + } + +public: + + /*! + this method converts the value to a string with a base equal 'b' + */ + void ToString(std::string & result, uint b = 10) const + { + return ToStringBase(result, b); + } + + + /*! + this method converts the value to a string with a base equal 'b' + */ + std::string ToString(uint b = 10) const + { + std::string result; + ToStringBase(result, b); + + return result; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method converts the value to a string with a base equal 'b' + */ + void ToString(std::wstring & result, uint b = 10) const + { + return ToStringBase(result, b); + } + + + /*! + this method converts the value to a string with a base equal 'b' + */ + std::wstring ToWString(uint b = 10) const + { + std::wstring result; + ToStringBase(result, b); + + return result; + } + +#endif + + + +private: + + /*! + an auxiliary method for converting from a string + */ + template + uint FromStringBase(const char_type * s, uint b = 10, const char_type ** after_source = 0, bool * value_read = 0) + { + bool is_sign = false; + + Misc::SkipWhiteCharacters(s); + + if( *s == '-' ) + { + is_sign = true; + Misc::SkipWhiteCharacters(++s); + } + else + if( *s == '+' ) + { + Misc::SkipWhiteCharacters(++s); + } + + if( UInt::FromString(s,b,after_source,value_read) ) + return 1; + + if( is_sign ) + { + Int mmin; + + mmin.SetMin(); + + /* + the reference to mmin will be automatically converted to the reference + to UInt type + (this value can be equal mmin -- look at a description in ChangeSign()) + */ + if( UInt::operator>( mmin ) ) + return 1; + + /* + if the value is equal mmin the method ChangeSign() does nothing (only returns 1 but we ignore it) + */ + ChangeSign(); + } + else + { + Int mmax; + + mmax.SetMax(); + + if( UInt::operator>( mmax ) ) + return 1; + } + + return 0; + } + + +public: + + /*! + this method converts a string into its value + it returns carry=1 if the value will be too big or an incorrect base 'b' is given + + string is ended with a non-digit value, for example: + "-12" will be translated to -12 + as well as: + "- 12foo" will be translated to -12 too + + existing first white characters will be ommited + (between '-' and a first digit can be white characters too) + + after_source (if exists) is pointing at the end of the parsed string + + value_read (if exists) tells whether something has actually been read (at least one digit) + */ + uint FromString(const char * s, uint b = 10, const char ** after_source = 0, bool * value_read = 0) + { + return FromStringBase(s, b, after_source, value_read); + } + + + /*! + this method converts a string into its value + */ + uint FromString(const wchar_t * s, uint b = 10, const wchar_t ** after_source = 0, bool * value_read = 0) + { + return FromStringBase(s, b, after_source, value_read); + } + + + /*! + this method converts a string into its value + it returns carry=1 if the value will be too big or an incorrect base 'b' is given + */ + uint FromString(const std::string & s, uint b = 10) + { + return FromString( s.c_str(), b ); + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + Int & operator=(const char * s) + { + FromString(s); + + return *this; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + + /*! + this method converts a string into its value + it returns carry=1 if the value will be too big or an incorrect base 'b' is given + */ + uint FromString(const std::wstring & s, uint b = 10) + { + return FromString( s.c_str(), b ); + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + Int & operator=(const wchar_t * s) + { + FromString(s); + + return *this; + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + Int & operator=(const std::wstring & s) + { + FromString( s.c_str() ); + + return *this; + } + +#endif + + + /*! + this operator converts a string into its value (with base = 10) + */ + Int & operator=(const std::string & s) + { + FromString( s.c_str() ); + + return *this; + } + + + + /*! + * + * methods for comparing + * + * + */ + + bool operator==(const Int & l) const + { + return UInt::operator==(l); + } + + bool operator!=(const Int & l) const + { + return UInt::operator!=(l); + } + + bool operator<(const Int & l) const + { + sint i=value_size-1; + + sint a1 = sint(UInt::table[i]); + sint a2 = sint(l.table[i]); + + if( a1 != a2 ) + return a1 < a2; + + + for(--i ; i>=0 ; --i) + { + if( UInt::table[i] != l.table[i] ) + // comparison as unsigned int + return UInt::table[i] < l.table[i]; + } + + // they're equal + return false; + } + + + bool operator>(const Int & l) const + { + sint i=value_size-1; + + sint a1 = sint(UInt::table[i]); + sint a2 = sint(l.table[i]); + + if( a1 != a2 ) + return a1 > a2; + + + for(--i ; i>=0 ; --i) + { + if( UInt::table[i] != l.table[i] ) + // comparison as unsigned int + return UInt::table[i] > l.table[i]; + } + + // they're equal + return false; + } + + + bool operator<=(const Int & l) const + { + sint i=value_size-1; + + sint a1 = sint(UInt::table[i]); + sint a2 = sint(l.table[i]); + + if( a1 != a2 ) + return a1 < a2; + + + for(--i ; i>=0 ; --i) + { + if( UInt::table[i] != l.table[i] ) + // comparison as unsigned int + return UInt::table[i] < l.table[i]; + } + + // they're equal + return true; + } + + + bool operator>=(const Int & l) const + { + sint i=value_size-1; + + sint a1 = sint(UInt::table[i]); + sint a2 = sint(l.table[i]); + + if( a1 != a2 ) + return a1 > a2; + + + for(--i ; i>=0 ; --i) + { + if( UInt::table[i] != l.table[i] ) + // comparison as unsigned int + return UInt::table[i] > l.table[i]; + } + + // they're equal + return true; + } + + + + /*! + * + * standard mathematical operators + * + */ + + + /*! + an operator for changing the sign + + it's not changing 'this' but the changed value will be returned + */ + Int operator-() const + { + Int temp(*this); + + temp.ChangeSign(); + + return temp; + } + + + Int operator-(const Int & p2) const + { + Int temp(*this); + + temp.Sub(p2); + + return temp; + } + + + Int & operator-=(const Int & p2) + { + Sub(p2); + + return *this; + } + + + Int operator+(const Int & p2) const + { + Int temp(*this); + + temp.Add(p2); + + return temp; + } + + + Int & operator+=(const Int & p2) + { + Add(p2); + + return *this; + } + + + Int operator*(const Int & p2) const + { + Int temp(*this); + + temp.Mul(p2); + + return temp; + } + + + Int & operator*=(const Int & p2) + { + Mul(p2); + + return *this; + } + + + Int operator/(const Int & p2) const + { + Int temp(*this); + + temp.Div(p2); + + return temp; + } + + + Int & operator/=(const Int & p2) + { + Div(p2); + + return *this; + } + + + Int operator%(const Int & p2) const + { + Int temp(*this); + Int remainder; + + temp.Div(p2, remainder); + + return remainder; + } + + + Int & operator%=(const Int & p2) + { + Int remainder; + + Div(p2, remainder); + operator=(remainder); + + return *this; + } + + + /*! + Prefix operator e.g. ++variable + */ + UInt & operator++() + { + AddOne(); + + return *this; + } + + + /*! + Postfix operator e.g. variable++ + */ + UInt operator++(int) + { + UInt temp( *this ); + + AddOne(); + + return temp; + } + + + UInt & operator--() + { + SubOne(); + + return *this; + } + + + UInt operator--(int) + { + UInt temp( *this ); + + SubOne(); + + return temp; + } + + + + /*! + * + * input/output operators for standard streams + * + */ + +private: + + /*! + an auxiliary method for outputing to standard streams + */ + template + static ostream_type & OutputToStream(ostream_type & s, const Int & l) + { + string_type ss; + + l.ToString(ss); + s << ss; + + return s; + } + + + +public: + + + /*! + output to standard streams + */ + friend std::ostream & operator<<(std::ostream & s, const Int & l) + { + return OutputToStream(s, l); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + output to standard streams + */ + friend std::wostream & operator<<(std::wostream & s, const Int & l) + { + return OutputToStream(s, l); + } + +#endif + + + +private: + + /*! + an auxiliary method for converting from a string + */ + template + static istream_type & InputFromStream(istream_type & s, Int & l) + { + string_type ss; + + // char or wchar_t for operator>> + char_type z; + + // operator>> omits white characters if they're set for ommiting + s >> z; + + if( z=='-' || z=='+' ) + { + ss += z; + s >> z; // we're reading a next character (white characters can be ommited) + } + + // we're reading only digits (base=10) + while( s.good() && Misc::CharToDigit(z, 10)>=0 ) + { + ss += z; + z = static_cast(s.get()); + } + + // we're leaving the last readed character + // (it's not belonging to the value) + s.unget(); + + l.FromString(ss); + + return s; + } + + +public: + + /*! + input from standard streams + */ + friend std::istream & operator>>(std::istream & s, Int & l) + { + return InputFromStream(s, l); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + input from standard streams + */ + friend std::wistream & operator>>(std::wistream & s, Int & l) + { + return InputFromStream(s, l); + } +#endif + + +}; + +} // namespace + +#endif diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmathmisc.h b/3rd_party/ttmath-0.9.3/ttmath/ttmathmisc.h new file mode 100644 index 00000000..330a43a4 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmathmisc.h @@ -0,0 +1,250 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2010, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef headerfilettmathmisc +#define headerfilettmathmisc + + +/*! + \file ttmathmisc.h + \brief some helpful functions +*/ + + +#include + + +namespace ttmath +{ + +/*! + some helpful functions +*/ +class Misc +{ +public: + + +/* + * + * AssignString(result, str) + * result = str + * + */ + +/*! + result = str +*/ +static void AssignString(std::string & result, const char * str) +{ + result = str; +} + + +#ifndef TTMATH_DONT_USE_WCHAR + +/*! + result = str +*/ +static void AssignString(std::wstring & result, const char * str) +{ + result.clear(); + + for( ; *str ; ++str ) + result += *str; +} + + +/*! + result = str +*/ +static void AssignString(std::wstring & result, const std::string & str) +{ + return AssignString(result, str.c_str()); +} + + +/*! + result = str +*/ +static void AssignString(std::string & result, const wchar_t * str) +{ + result.clear(); + + for( ; *str ; ++str ) + result += static_cast(*str); +} + + +/*! + result = str +*/ +static void AssignString(std::string & result, const std::wstring & str) +{ + return AssignString(result, str.c_str()); +} + +#endif + + +/* + * + * AddString(result, str) + * result += str + * + */ + + +/*! + result += str +*/ +static void AddString(std::string & result, const char * str) +{ + result += str; +} + + +#ifndef TTMATH_DONT_USE_WCHAR + +/*! + result += str +*/ +static void AddString(std::wstring & result, const char * str) +{ + for( ; *str ; ++str ) + result += *str; +} + +#endif + + +/* + this method omits any white characters from the string + char_type is char or wchar_t +*/ +template +static void SkipWhiteCharacters(const char_type * & c) +{ + // 13 is at the end in a DOS text file (\r\n) + while( (*c==' ' ) || (*c=='\t') || (*c==13 ) || (*c=='\n') ) + ++c; +} + + + + +/*! + this static method converts one character into its value + + for example: + 1 -> 1 + 8 -> 8 + A -> 10 + f -> 15 + + this method don't check whether c is correct or not +*/ +static uint CharToDigit(uint c) +{ + if(c>='0' && c<='9') + return c-'0'; + + if(c>='a' && c<='z') + return c-'a'+10; + +return c-'A'+10; +} + + +/*! + this method changes a character 'c' into its value + (if there can't be a correct value it returns -1) + + for example: + c=2, base=10 -> function returns 2 + c=A, base=10 -> function returns -1 + c=A, base=16 -> function returns 10 +*/ +static sint CharToDigit(uint c, uint base) +{ + if( c>='0' && c<='9' ) + c=c-'0'; + else + if( c>='a' && c<='z' ) + c=c-'a'+10; + else + if( c>='A' && c<='Z' ) + c=c-'A'+10; + else + return -1; + + + if( c >= base ) + return -1; + + +return sint(c); +} + + + +/*! + this method converts a digit into a char + digit should be from <0,F> + (we don't have to get a base) + + for example: + 1 -> 1 + 8 -> 8 + 10 -> A + 15 -> F +*/ +static uint DigitToChar(uint digit) +{ + if( digit < 10 ) + return digit + '0'; + +return digit - 10 + 'A'; +} + + +}; // struct Misc + +} + + +#endif diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmathobjects.h b/3rd_party/ttmath-0.9.3/ttmath/ttmathobjects.h new file mode 100644 index 00000000..6e204fee --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmathobjects.h @@ -0,0 +1,809 @@ +/* + * This file is a part of TTMath Mathematical Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2010, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef headerfilettmathobject +#define headerfilettmathobject + +/*! + \file ttmathobjects.h + \brief Mathematic functions. +*/ + +#include +#include +#include +#include + +#include "ttmathtypes.h" +#include "ttmathmisc.h" + + +namespace ttmath +{ + +/*! + objects of this class are used with the mathematical parser + they hold variables or functions defined by a user + + each object has its own table in which we're keeping variables or functions +*/ +class Objects +{ +public: + + + /*! + one item (variable or function) + 'items' will be on the table + */ + struct Item + { + // name of a variable of a function + // internally we store variables and funcions as std::string (not std::wstring even when wide characters are used) + std::string value; + + // number of parameters required by the function + // (if there's a variable this 'param' is ignored) + int param; + + Item() {} + Item(const std::string & v, int p) : value(v), param(p) {} + }; + + // 'Table' is the type of our table + typedef std::map Table; + typedef Table::iterator Iterator; + typedef Table::const_iterator CIterator; + + + + /*! + this method returns true if a character 'c' is a character + which can be in a name + + if 'can_be_digit' is true that means when the 'c' is a digit this + method returns true otherwise it returns false + */ + static bool CorrectCharacter(int c, bool can_be_digit) + { + if( (c>='a' && c<='z') || (c>='A' && c<='Z') ) + return true; + + if( can_be_digit && ((c>='0' && c<='9') || c=='_') ) + return true; + + return false; + } + + + /*! + this method returns true if the name can be as a name of an object + */ + template + static bool IsNameCorrect(const string_type & name) + { + if( name.empty() ) + return false; + + if( !CorrectCharacter(name[0], false) ) + return false; + + typename string_type::const_iterator i = name.begin(); + + for(++i ; i!=name.end() ; ++i) + if( !CorrectCharacter(*i, true) ) + return false; + + return true; + } + + + /*! + this method returns true if such an object is defined (name exists) + */ + bool IsDefined(const std::string & name) + { + Iterator i = table.find(name); + + if( i != table.end() ) + // we have this object in our table + return true; + + return false; + } + + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method returns true if such an object is defined (name exists) + */ + bool IsDefined(const std::wstring & name) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return false; + + Misc::AssignString(str_tmp1, name); + + return IsDefined(str_tmp1); + } + +#endif + + + /*! + this method adds one object (variable of function) into the table + */ + ErrorCode Add(const std::string & name, const std::string & value, int param = 0) + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Iterator i = table.find(name); + + if( i != table.end() ) + // we have this object in our table + return err_object_exists; + + table.insert( std::make_pair(name, Item(value, param)) ); + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method adds one object (variable of function) into the table + */ + ErrorCode Add(const std::wstring & name, const std::wstring & value, int param = 0) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + Misc::AssignString(str_tmp2, value); + + return Add(str_tmp1, str_tmp2, param); + } + +#endif + + + /*! + this method returns 'true' if the table is empty + */ + bool Empty() const + { + return table.empty(); + } + + + /*! + this method clears the table + */ + void Clear() + { + return table.clear(); + } + + + /*! + this method returns 'const_iterator' on the first item on the table + */ + CIterator Begin() const + { + return table.begin(); + } + + + /*! + this method returns 'const_iterator' pointing at the space after last item + (returns table.end()) + */ + CIterator End() const + { + return table.end(); + } + + + /*! + this method changes the value and the number of parameters for a specific object + */ + ErrorCode EditValue(const std::string & name, const std::string & value, int param = 0) + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Iterator i = table.find(name); + + if( i == table.end() ) + return err_unknown_object; + + i->second.value = value; + i->second.param = param; + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + + /*! + this method changes the value and the number of parameters for a specific object + */ + ErrorCode EditValue(const std::wstring & name, const std::wstring & value, int param = 0) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + Misc::AssignString(str_tmp2, value); + + return EditValue(str_tmp1, str_tmp2, param); + } + +#endif + + + /*! + this method changes the name of a specific object + */ + ErrorCode EditName(const std::string & old_name, const std::string & new_name) + { + if( !IsNameCorrect(old_name) || !IsNameCorrect(new_name) ) + return err_incorrect_name; + + Iterator old_i = table.find(old_name); + if( old_i == table.end() ) + return err_unknown_object; + + if( old_name == new_name ) + // the new name is the same as the old one + // we treat it as a normal situation + return err_ok; + + ErrorCode err = Add(new_name, old_i->second.value, old_i->second.param); + + if( err == err_ok ) + { + old_i = table.find(old_name); + TTMATH_ASSERT( old_i != table.end() ) + + table.erase(old_i); + } + + return err; + } + + + +#ifndef TTMATH_DONT_USE_WCHAR + + + /*! + this method changes the name of a specific object + */ + ErrorCode EditName(const std::wstring & old_name, const std::wstring & new_name) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(old_name) || !IsNameCorrect(new_name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, old_name); + Misc::AssignString(str_tmp2, new_name); + + return EditName(str_tmp1, str_tmp2); + } + +#endif + + + /*! + this method deletes an object + */ + ErrorCode Delete(const std::string & name) + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Iterator i = table.find(name); + + if( i == table.end() ) + return err_unknown_object; + + table.erase( i ); + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + + /*! + this method deletes an object + */ + ErrorCode Delete(const std::wstring & name) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + + return Delete(str_tmp1); + } + +#endif + + + /*! + this method gets the value of a specific object + */ + ErrorCode GetValue(const std::string & name, std::string & value) const + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + CIterator i = table.find(name); + + if( i == table.end() ) + { + value.clear(); + return err_unknown_object; + } + + value = i->second.value; + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method gets the value of a specific object + */ + ErrorCode GetValue(const std::wstring & name, std::wstring & value) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + ErrorCode err = GetValue(str_tmp1, str_tmp2); + Misc::AssignString(value, str_tmp2); + + return err; + } + +#endif + + + /*! + this method gets the value of a specific object + (this version is used for not copying the whole string) + */ + ErrorCode GetValue(const std::string & name, const char ** value) const + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + CIterator i = table.find(name); + + if( i == table.end() ) + { + *value = 0; + return err_unknown_object; + } + + *value = i->second.value.c_str(); + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method gets the value of a specific object + (this version is used for not copying the whole string) + */ + ErrorCode GetValue(const std::wstring & name, const char ** value) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + + return GetValue(str_tmp1, value); + } + +#endif + + + /*! + this method gets the value and the number of parameters + of a specific object + */ + ErrorCode GetValueAndParam(const std::string & name, std::string & value, int * param) const + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + CIterator i = table.find(name); + + if( i == table.end() ) + { + value = ""; + *param = 0; + return err_unknown_object; + } + + value = i->second.value; + *param = i->second.param; + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method gets the value and the number of parameters + of a specific object + */ + ErrorCode GetValueAndParam(const std::wstring & name, std::wstring & value, int * param) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + ErrorCode err = GetValueAndParam(str_tmp1, str_tmp2, param); + Misc::AssignString(value, str_tmp2); + + return err; + } + +#endif + + + /*! + this method sets the value and the number of parameters + of a specific object + (this version is used for not copying the whole string) + */ + ErrorCode GetValueAndParam(const std::string & name, const char ** value, int * param) const + { + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + CIterator i = table.find(name); + + if( i == table.end() ) + { + *value = 0; + *param = 0; + return err_unknown_object; + } + + *value = i->second.value.c_str(); + *param = i->second.param; + + return err_ok; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + + /*! + this method sets the value and the number of parameters + of a specific object + (this version is used for not copying the whole string + but in fact we make one copying during AssignString()) + */ + ErrorCode GetValueAndParam(const std::wstring & name, const char ** value, int * param) + { + // we should check whether the name (in wide characters) are correct + // before calling AssignString() function + if( !IsNameCorrect(name) ) + return err_incorrect_name; + + Misc::AssignString(str_tmp1, name); + + return GetValueAndParam(str_tmp1, value, param); + } + + +#endif + + + /*! + this method returns a pointer into the table + */ + Table * GetTable() + { + return &table; + } + + +private: + + Table table; + std::string str_tmp1, str_tmp2; + +}; // end of class Objects + + + + + + + +/*! + objects of the class History are used to keep values in functions + which take a lot of time during calculating, for instance in the + function Factorial(x) + + it means that when we're calculating e.g. Factorial(1000) and the + Factorial finds that we have calculated it before, the value (result) + is taken from the history +*/ +template +class History +{ + /*! + one item in the History's object holds a key, a value for the key + and a corresponding error code + */ + struct Item + { + ValueType key, value; + ErrorCode err; + }; + + + /*! + we use std::list for simply deleting the first item + but because we're searching through the whole container + (in the method Get) the container should not be too big + (linear time of searching) + */ + typedef std::list buffer_type; + buffer_type buffer; + typename buffer_type::size_type buffer_max_size; + +public: + + /*! + default constructor + default max size of the History's container is 15 items + */ + History() + { + buffer_max_size = 15; + } + + + /*! + a constructor which takes another value of the max size + of the History's container + */ + History(typename buffer_type::size_type new_size) + { + buffer_max_size = new_size; + } + + + /*! + this method adds one item into the History + if the size of the container is greater than buffer_max_size + the first item will be removed + */ + void Add(const ValueType & key, const ValueType & value, ErrorCode err) + { + Item item; + item.key = key; + item.value = value; + item.err = err; + + buffer.insert( buffer.end(), item ); + + if( buffer.size() > buffer_max_size ) + buffer.erase(buffer.begin()); + } + + + /*! + this method checks whether we have an item which has the key equal 'key' + + if there's such item the method sets the 'value' and the 'err' + and returns true otherwise it returns false and 'value' and 'err' + remain unchanged + */ + bool Get(const ValueType & key, ValueType & value, ErrorCode & err) + { + typename buffer_type::iterator i = buffer.begin(); + + for( ; i != buffer.end() ; ++i ) + { + if( i->key == key ) + { + value = i->value; + err = i->err; + return true; + } + } + + return false; + } + + + /*! + this methods deletes an item + + we assume that there is only one item with the 'key' + (this methods removes the first one) + */ + bool Remove(const ValueType & key) + { + typename buffer_type::iterator i = buffer.begin(); + + for( ; i != buffer.end() ; ++i ) + { + if( i->key == key ) + { + buffer.erase(i); + return true; + } + } + + return false; + } + + +}; // end of class History + + + +/*! + this is an auxiliary class used when calculating Gamma() or Factorial() + + in multithreaded environment you can provide an object of this class to + the Gamma() or Factorial() function, e.g; + typedef Big<1, 3> MyBig; + MyBig x = 123456; + CGamma cgamma; + std::cout << Gamma(x, cgamma); + each thread should have its own CGamma<> object + + in a single-thread environment a CGamma<> object is a static variable + in a second version of Gamma() and you don't have to explicitly use it, e.g. + typedef Big<1, 3> MyBig; + MyBig x = 123456; + std::cout << Gamma(x); +*/ +template +struct CGamma +{ + /*! + this table holds factorials + 1 + 1 + 2 + 6 + 24 + 120 + 720 + ....... + */ + std::vector fact; + + + /*! + this table holds Bernoulli numbers + 1 + -0.5 + 0.166666666666666666666666667 + 0 + -0.0333333333333333333333333333 + 0 + 0.0238095238095238095238095238 + 0 + -0.0333333333333333333333333333 + 0 + 0.075757575757575757575757576 + ..... + */ + std::vector bern; + + + /*! + here we store some calculated values + (this is for speeding up, if the next argument of Gamma() or Factorial() + is in the 'history' then the result we are not calculating but simply + return from the 'history' object) + */ + History history; + + + /*! + this method prepares some coefficients: factorials and Bernoulli numbers + stored in 'fact' and 'bern' objects + + how many values should be depends on the size of the mantissa - if + the mantissa is larger then we must calculate more values + for a mantissa which consists of 256 bits (8 words on a 32bit platform) + we have to calculate about 30 values (the size of fact and bern will be 30), + and for a 2048 bits mantissa we have to calculate 306 coefficients + + you don't have to call this method, these coefficients will be automatically calculated + when they are needed + + you must note that calculating these coefficients is a little time-consuming operation, + (especially when the mantissa is large) and first call to Gamma() or Factorial() + can take more time than next calls, and in the end this is the point when InitAll() + comes in handy: you can call this method somewhere at the beginning of your program + */ + void InitAll(); + // definition is in ttmath.h +}; + + + + +} // namespace + +#endif diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmathparser.h b/3rd_party/ttmath-0.9.3/ttmath/ttmathparser.h new file mode 100644 index 00000000..ce07120f --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmathparser.h @@ -0,0 +1,2777 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2010, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef headerfilettmathparser +#define headerfilettmathparser + +/*! + \file ttmathparser.h + \brief A mathematical parser +*/ + +#include +#include +#include +#include + +#include "ttmath.h" +#include "ttmathobjects.h" +#include "ttmathmisc.h" + + + +namespace ttmath +{ + +/*! + \brief Mathematical parser + + let x will be an input string meaning an expression for converting: + + x = [+|-]Value[operator[+|-]Value][operator[+|-]Value]... + where: + an operator can be: + ^ (pow) (the heighest priority) + + * (mul) (or multiplication without an operator -- short mul) + / (div) (* and / have the same priority) + + + (add) + - (sub) (+ and - have the same priority) + + < (lower than) + > (greater than) + <= (lower or equal than) + >= (greater or equal than) + == (equal) + != (not equal) (all above logical operators have the same priority) + + && (logical and) + + || (logical or) (the lowest priority) + + short mul: + if the second Value (Var below) is either a variable or function there might not be + an operator between them, e.g. + "[+|-]Value Var" is treated as "[+|-]Value * Var" and the multiplication + has the same priority as a normal multiplication: + 4x = 4 * x + 2^3m = (2^3)* m + 6h^3 = 6 * (h^3) + 2sin(pi) = 2 * sin(pi) + etc. + + Value can be: + constant e.g. 100, can be preceded by operators for changing the base (radix): [#|&] + # - hex + & - bin + sample: #10 = 16 + &10 = 2 + variable e.g. pi + another expression between brackets e.g (x) + function e.g. sin(x) + + for example a correct input string can be: + "1" + "2.1234" + "2,1234" (they are the same, by default we can either use a comma or a dot) + "1 + 2" + "(1 + 2) * 3" + "pi" + "sin(pi)" + "(1+2)*(2+3)" + "log(2;1234)" there's a semicolon here (not a comma), we use it in functions + for separating parameters + "1 < 2" (the result will be: 1) + "4 < 3" (the result will be: 0) + "2+x" (of course if the variable 'x' is defined) + "4x+10" + "#20+10" = 32 + 10 = 42 + "10 ^ -&101" = 10 ^ -5 = 0.00001 + "8 * -&10" = 8 * -2 = -16 + etc. + + we can also use a semicolon for separating any 'x' input strings + for example: + "1+2;4+5" + the result will be on the stack as follows: + stack[0].value=3 + stack[1].value=9 +*/ +template +class Parser +{ +private: + +/*! + there are 5 mathematical operators as follows (with their standard priorities): + add (+) + sub (-) + mul (*) + div (/) + pow (^) + and 'shortmul' used when there is no any operators between + a first parameter and a variable or function + (the 'shortmul' has the same priority as the normal multiplication ) +*/ + class MatOperator + { + public: + + enum Type + { + none,add,sub,mul,div,pow,lt,gt,let,get,eq,neq,lor,land,shortmul + }; + + enum Assoc + { + right, // right-associative + non_right // associative or left-associative + }; + + Type GetType() const { return type; } + int GetPriority() const { return priority; } + Assoc GetAssoc() const { return assoc; } + + void SetType(Type t) + { + type = t; + assoc = non_right; + + switch( type ) + { + case lor: + priority = 4; + break; + + case land: + priority = 5; + break; + + case eq: + case neq: + case lt: + case gt: + case let: + case get: + priority = 7; + break; + + case add: + case sub: + priority = 10; + break; + + case mul: + case shortmul: + case div: + priority = 12; + break; + + case pow: + priority = 14; + assoc = right; + break; + + default: + Error( err_internal_error ); + break; + } + } + + MatOperator(): type(none), priority(0), assoc(non_right) + { + } + + private: + + Type type; + int priority; + Assoc assoc; + }; // end of MatOperator class + + + +public: + + + + /*! + Objects of type 'Item' we are keeping on our stack + */ + struct Item + { + enum Type + { + none, numerical_value, mat_operator, first_bracket, + last_bracket, variable, semicolon + }; + + // The kind of type which we're keeping + Type type; + + // if type == numerical_value + ValueType value; + + // if type == mat_operator + MatOperator moperator; + + /* + if type == first_bracket + + if 'function' is set to true it means that the first recognized bracket + was the bracket from function in other words we must call a function when + we'll find the 'last' bracket + */ + bool function; + + // if function is true + std::string function_name; + + /* + the sign of value + + it can be for type==numerical_value or type==first_bracket + when it's true it means e.g. that value is equal -value + */ + bool sign; + + Item(): type(none), function(false), sign(false) + { + } + + }; // end of Item struct + + +/*! + stack on which we're keeping the Items + + at the end of parsing we'll have the result here + the result don't have to be one value, it can be + more than one if we have used a semicolon in the global space + e.g. such input string "1+2;3+4" will generate a result: + stack[0].value=3 + stack[1].value=7 + + you should check if the stack is not empty, because if there was + a syntax error in the input string then we do not have any results + on the stack +*/ +std::vector stack; + + +private: + + +/*! + size of the stack when we're starting parsing of the string + + if it's to small while parsing the stack will be automatically resized +*/ +const int default_stack_size; + + + +/*! + index of an object in our stack + it's pointing on the place behind the last element + for example at the beginning of parsing its value is zero +*/ +unsigned int stack_index; + + +/*! + code of the last error +*/ +ErrorCode error; + + +/*! + pointer to the currently reading char + when an error has occured it may be used to count the index of the wrong character +*/ +const char * pstring; + + +/*! + the base (radix) of the mathematic system (for example it may be '10') +*/ +int base; + + +/*! + the unit of angles used in: sin,cos,tan,cot,asin,acos,atan,acot + 0 - deg + 1 - rad (default) + 2 - grad +*/ +int deg_rad_grad; + + + +/*! + a pointer to an object which tell us whether we should stop calculating or not +*/ +const volatile StopCalculating * pstop_calculating; + + + +/*! + a pointer to the user-defined variables' table +*/ +const Objects * puser_variables; + +/*! + a pointer to the user-defined functions' table +*/ +const Objects * puser_functions; + + +typedef std::map FunctionLocalVariables; + +/*! + a pointer to the local variables of a function +*/ +const FunctionLocalVariables * pfunction_local_variables; + + +/*! + a temporary set using during parsing user defined variables +*/ +std::set visited_variables; + + +/*! + a temporary set using during parsing user defined functions +*/ +std::set visited_functions; + + + + +/*! + pfunction is the type of pointer to a mathematic function + + these mathematic functions are private members of this class, + they are the wrappers for standard mathematics function + + 'pstack' is the pointer to the first argument on our stack + 'amount_of_arg' tell us how many argument there are in our stack + 'result' is the reference for result of function +*/ +typedef void (Parser::*pfunction)(int pstack, int amount_of_arg, ValueType & result); + + +/*! + pfunction is the type of pointer to a method which returns value of variable +*/ +typedef void (ValueType::*pfunction_var)(); + + +/*! + table of mathematic functions + + this map consists of: + std::string - function's name + pfunction - pointer to specific function +*/ +typedef std::map FunctionsTable; +FunctionsTable functions_table; + + +/*! + table of mathematic operators + + this map consists of: + std::string - operators's name + MatOperator::Type - type of the operator +*/ +typedef std::map OperatorsTable; +OperatorsTable operators_table; + + +/*! + table of mathematic variables + + this map consists of: + std::string - variable's name + pfunction_var - pointer to specific function which returns value of variable +*/ +typedef std::map VariablesTable; +VariablesTable variables_table; + + +/*! + some coefficients used when calculating the gamma (or factorial) function +*/ +CGamma cgamma; + + +/*! + temporary object for a whole string when Parse(std::wstring) is used +*/ +std::string wide_to_ansi; + + +/*! + group character (used when parsing) + default zero (not used) +*/ +int group; + + +/*! + characters used as a comma + default: '.' and ',' + comma2 can be zero (it means it is not used) +*/ +int comma, comma2; + + +/*! + an additional character used as a separator between function parameters + (semicolon is used always) +*/ +int param_sep; + + +/*! + true if something was calculated (at least one mathematical operator was used or a function or a variable) +*/ +bool calculated; + + + +/*! + we're using this method for reporting an error +*/ +static void Error(ErrorCode code) +{ + throw code; +} + + +/*! + this method skips the white character from the string + + it's moving the 'pstring' to the first no-white character +*/ +void SkipWhiteCharacters() +{ + while( (*pstring==' ' ) || (*pstring=='\t') ) + ++pstring; +} + + +/*! + an auxiliary method for RecurrenceParsingVariablesOrFunction(...) +*/ +void RecurrenceParsingVariablesOrFunction_CheckStopCondition(bool variable, const std::string & name) +{ + if( variable ) + { + if( visited_variables.find(name) != visited_variables.end() ) + Error( err_variable_loop ); + } + else + { + if( visited_functions.find(name) != visited_functions.end() ) + Error( err_functions_loop ); + } +} + + +/*! + an auxiliary method for RecurrenceParsingVariablesOrFunction(...) +*/ +void RecurrenceParsingVariablesOrFunction_AddName(bool variable, const std::string & name) +{ + if( variable ) + visited_variables.insert( name ); + else + visited_functions.insert( name ); +} + + +/*! + an auxiliary method for RecurrenceParsingVariablesOrFunction(...) +*/ +void RecurrenceParsingVariablesOrFunction_DeleteName(bool variable, const std::string & name) +{ + if( variable ) + visited_variables.erase( name ); + else + visited_functions.erase( name ); +} + + +/*! + this method returns the value of a variable or function + by creating a new instance of the mathematical parser + and making the standard parsing algorithm on the given string + + this method is used only during parsing user defined variables or functions + + (there can be a recurrence here therefore we're using 'visited_variables' + and 'visited_functions' sets to make a stop condition) +*/ +ValueType RecurrenceParsingVariablesOrFunction(bool variable, const std::string & name, const char * new_string, + FunctionLocalVariables * local_variables = 0) +{ + RecurrenceParsingVariablesOrFunction_CheckStopCondition(variable, name); + RecurrenceParsingVariablesOrFunction_AddName(variable, name); + + Parser NewParser(*this); + ErrorCode err; + + NewParser.pfunction_local_variables = local_variables; + + try + { + err = NewParser.Parse(new_string); + } + catch(...) + { + RecurrenceParsingVariablesOrFunction_DeleteName(variable, name); + + throw; + } + + RecurrenceParsingVariablesOrFunction_DeleteName(variable, name); + + if( err != err_ok ) + Error( err ); + + if( NewParser.stack.size() != 1 ) + Error( err_must_be_only_one_value ); + + if( NewParser.stack[0].type != Item::numerical_value ) + // I think there shouldn't be this error here + Error( err_incorrect_value ); + +return NewParser.stack[0].value; +} + + +public: + + +/*! + this method returns the user-defined value of a variable +*/ +bool GetValueOfUserDefinedVariable(const std::string & variable_name,ValueType & result) +{ + if( !puser_variables ) + return false; + + const char * string_value; + + if( puser_variables->GetValue(variable_name, &string_value) != err_ok ) + return false; + + result = RecurrenceParsingVariablesOrFunction(true, variable_name, string_value); + calculated = true; + +return true; +} + + +/*! + this method returns the value of a local variable of a function +*/ +bool GetValueOfFunctionLocalVariable(const std::string & variable_name, ValueType & result) +{ + if( !pfunction_local_variables ) + return false; + + typename FunctionLocalVariables::const_iterator i = pfunction_local_variables->find(variable_name); + + if( i == pfunction_local_variables->end() ) + return false; + + result = i->second; + +return true; +} + + +/*! + this method returns the value of a variable from variables' table + + we make an object of type ValueType then call a method which + sets the correct value in it and finally we'll return the object +*/ +ValueType GetValueOfVariable(const std::string & variable_name) +{ +ValueType result; + + if( GetValueOfFunctionLocalVariable(variable_name, result) ) + return result; + + if( GetValueOfUserDefinedVariable(variable_name, result) ) + return result; + + + typename std::map::iterator i = + variables_table.find(variable_name); + + if( i == variables_table.end() ) + Error( err_unknown_variable ); + + (result.*(i->second))(); + calculated = true; + +return result; +} + + +private: + +/*! + wrappers for mathematic functions + + 'sindex' is pointing on the first argument on our stack + (the second argument has 'sindex+2' + because 'sindex+1' is guaranted for the 'semicolon' operator) + the third artument has of course 'sindex+4' etc. + + 'result' will be the result of the function + + (we're using exceptions here for example when function gets an improper argument) +*/ + + +/*! + used by: sin,cos,tan,cot +*/ +ValueType ConvertAngleToRad(const ValueType & input) +{ + if( deg_rad_grad == 1 ) // rad + return input; + + ValueType result; + ErrorCode err; + + if( deg_rad_grad == 0 ) // deg + result = ttmath::DegToRad(input, &err); + else // grad + result = ttmath::GradToRad(input, &err); + + if( err != err_ok ) + Error( err ); + +return result; +} + + +/*! + used by: asin,acos,atan,acot +*/ +ValueType ConvertRadToAngle(const ValueType & input) +{ + if( deg_rad_grad == 1 ) // rad + return input; + + ValueType result; + ErrorCode err; + + if( deg_rad_grad == 0 ) // deg + result = ttmath::RadToDeg(input, &err); + else // grad + result = ttmath::RadToGrad(input, &err); + + if( err != err_ok ) + Error( err ); + +return result; +} + + +void Gamma(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + ErrorCode err; + + result = ttmath::Gamma(stack[sindex].value, cgamma, &err, pstop_calculating); + + if(err != err_ok) + Error( err ); +} + + +/*! + factorial + result = 1 * 2 * 3 * 4 * .... * x +*/ +void Factorial(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + ErrorCode err; + + result = ttmath::Factorial(stack[sindex].value, cgamma, &err, pstop_calculating); + + if(err != err_ok) + Error( err ); +} + + +void Abs(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + result = ttmath::Abs(stack[sindex].value); +} + +void Sin(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + ErrorCode err; + result = ttmath::Sin( ConvertAngleToRad(stack[sindex].value), &err ); + + if(err != err_ok) + Error( err ); +} + +void Cos(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + ErrorCode err; + result = ttmath::Cos( ConvertAngleToRad(stack[sindex].value), &err ); + + if(err != err_ok) + Error( err ); +} + +void Tan(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + ErrorCode err; + result = ttmath::Tan(ConvertAngleToRad(stack[sindex].value), &err); + + if(err != err_ok) + Error( err ); +} + +void Cot(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + ErrorCode err; + result = ttmath::Cot(ConvertAngleToRad(stack[sindex].value), &err); + + if(err != err_ok) + Error( err ); +} + +void Int(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + result = ttmath::SkipFraction(stack[sindex].value); +} + + +void Round(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + result = stack[sindex].value; + + if( result.Round() ) + Error( err_overflow ); +} + + +void Ln(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + ErrorCode err; + result = ttmath::Ln(stack[sindex].value, &err); + + if(err != err_ok) + Error( err ); +} + +void Log(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 2 ) + Error( err_improper_amount_of_arguments ); + + ErrorCode err; + result = ttmath::Log(stack[sindex].value, stack[sindex+2].value, &err); + + if(err != err_ok) + Error( err ); +} + +void Exp(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + ErrorCode err; + result = ttmath::Exp(stack[sindex].value, &err); + + if(err != err_ok) + Error( err ); +} + + +void Max(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args == 0 ) + { + result.SetMax(); + + return; + } + + result = stack[sindex].value; + + for(int i=1 ; i stack[sindex + i*2].value ) + result = stack[sindex + i*2].value; + } +} + + +void ASin(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + ErrorCode err; + ValueType temp = ttmath::ASin(stack[sindex].value, &err); + + if(err != err_ok) + Error( err ); + + result = ConvertRadToAngle(temp); +} + + +void ACos(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + ErrorCode err; + ValueType temp = ttmath::ACos(stack[sindex].value, &err); + + if(err != err_ok) + Error( err ); + + result = ConvertRadToAngle(temp); +} + + +void ATan(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + result = ConvertRadToAngle(ttmath::ATan(stack[sindex].value)); +} + + +void ACot(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + result = ConvertRadToAngle(ttmath::ACot(stack[sindex].value)); +} + + +void Sgn(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 1 ) + Error( err_improper_amount_of_arguments ); + + result = ttmath::Sgn(stack[sindex].value); +} + + +void Mod(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 2 ) + Error( err_improper_amount_of_arguments ); + + if( stack[sindex+2].value.IsZero() ) + Error( err_improper_argument ); + + result = stack[sindex].value; + uint c = result.Mod(stack[sindex+2].value); + + if( c ) + Error( err_overflow ); +} + + +void If(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args != 3 ) + Error( err_improper_amount_of_arguments ); + + + if( !stack[sindex].value.IsZero() ) + result = stack[sindex+2].value; + else + result = stack[sindex+4].value; +} + + +void Or(int sindex, int amount_of_args, ValueType & result) +{ + if( amount_of_args < 2 ) + Error( err_improper_amount_of_arguments ); + + for(int i=0 ; iGetValueAndParam(function_name, &string_value, ¶m) != err_ok ) + return false; + + if( param != amount_of_args ) + Error( err_improper_amount_of_arguments ); + + + FunctionLocalVariables local_variables; + + if( amount_of_args > 0 ) + { + char buffer[30]; + + // x = x1 + buffer[0] = 'x'; + buffer[1] = 0; + local_variables.insert( std::make_pair(buffer, stack[sindex].value) ); + + for(int i=0 ; i*(i->second))(sindex, amount_of_args, stack[sindex-1].value); + calculated = true; +} + + + + + +/*! + inserting a function to the functions' table + + function_name - name of the function + pf - pointer to the function (to the wrapper) +*/ +void InsertFunctionToTable(const char * function_name, pfunction pf) +{ + std::string str; + Misc::AssignString(str, function_name); + + functions_table.insert( std::make_pair(str, pf) ); +} + + + +/*! + inserting a function to the variables' table + (this function returns value of variable) + + variable_name - name of the function + pf - pointer to the function +*/ +void InsertVariableToTable(const char * variable_name, pfunction_var pf) +{ + std::string str; + Misc::AssignString(str, variable_name); + + variables_table.insert( std::make_pair(str, pf) ); +} + + +/*! + this method creates the table of functions +*/ +void CreateFunctionsTable() +{ + InsertFunctionToTable("gamma", &Parser::Gamma); + InsertFunctionToTable("factorial", &Parser::Factorial); + InsertFunctionToTable("abs", &Parser::Abs); + InsertFunctionToTable("sin", &Parser::Sin); + InsertFunctionToTable("cos", &Parser::Cos); + InsertFunctionToTable("tan", &Parser::Tan); + InsertFunctionToTable("tg", &Parser::Tan); + InsertFunctionToTable("cot", &Parser::Cot); + InsertFunctionToTable("ctg", &Parser::Cot); + InsertFunctionToTable("int", &Parser::Int); + InsertFunctionToTable("round", &Parser::Round); + InsertFunctionToTable("ln", &Parser::Ln); + InsertFunctionToTable("log", &Parser::Log); + InsertFunctionToTable("exp", &Parser::Exp); + InsertFunctionToTable("max", &Parser::Max); + InsertFunctionToTable("min", &Parser::Min); + InsertFunctionToTable("asin", &Parser::ASin); + InsertFunctionToTable("acos", &Parser::ACos); + InsertFunctionToTable("atan", &Parser::ATan); + InsertFunctionToTable("atg", &Parser::ATan); + InsertFunctionToTable("acot", &Parser::ACot); + InsertFunctionToTable("actg", &Parser::ACot); + InsertFunctionToTable("sgn", &Parser::Sgn); + InsertFunctionToTable("mod", &Parser::Mod); + InsertFunctionToTable("if", &Parser::If); + InsertFunctionToTable("or", &Parser::Or); + InsertFunctionToTable("and", &Parser::And); + InsertFunctionToTable("not", &Parser::Not); + InsertFunctionToTable("degtorad", &Parser::DegToRad); + InsertFunctionToTable("radtodeg", &Parser::RadToDeg); + InsertFunctionToTable("degtodeg", &Parser::DegToDeg); + InsertFunctionToTable("gradtorad", &Parser::GradToRad); + InsertFunctionToTable("radtograd", &Parser::RadToGrad); + InsertFunctionToTable("degtograd", &Parser::DegToGrad); + InsertFunctionToTable("gradtodeg", &Parser::GradToDeg); + InsertFunctionToTable("ceil", &Parser::Ceil); + InsertFunctionToTable("floor", &Parser::Floor); + InsertFunctionToTable("sqrt", &Parser::Sqrt); + InsertFunctionToTable("sinh", &Parser::Sinh); + InsertFunctionToTable("cosh", &Parser::Cosh); + InsertFunctionToTable("tanh", &Parser::Tanh); + InsertFunctionToTable("tgh", &Parser::Tanh); + InsertFunctionToTable("coth", &Parser::Coth); + InsertFunctionToTable("ctgh", &Parser::Coth); + InsertFunctionToTable("root", &Parser::Root); + InsertFunctionToTable("asinh", &Parser::ASinh); + InsertFunctionToTable("acosh", &Parser::ACosh); + InsertFunctionToTable("atanh", &Parser::ATanh); + InsertFunctionToTable("atgh", &Parser::ATanh); + InsertFunctionToTable("acoth", &Parser::ACoth); + InsertFunctionToTable("actgh", &Parser::ACoth); + InsertFunctionToTable("bitand", &Parser::BitAnd); + InsertFunctionToTable("bitor", &Parser::BitOr); + InsertFunctionToTable("bitxor", &Parser::BitXor); + InsertFunctionToTable("band", &Parser::BitAnd); + InsertFunctionToTable("bor", &Parser::BitOr); + InsertFunctionToTable("bxor", &Parser::BitXor); + InsertFunctionToTable("sum", &Parser::Sum); + InsertFunctionToTable("avg", &Parser::Avg); + InsertFunctionToTable("frac", &Parser::Frac); +} + + +/*! + this method creates the table of variables +*/ +void CreateVariablesTable() +{ + InsertVariableToTable("pi", &ValueType::SetPi); + InsertVariableToTable("e", &ValueType::SetE); +} + + +/*! + converting from a big letter to a small one +*/ +int ToLowerCase(int c) +{ + if( c>='A' && c<='Z' ) + return c - 'A' + 'a'; + +return c; +} + + +/*! + this method read the name of a variable or a function + + 'result' will be the name of a variable or a function + function return 'false' if this name is the name of a variable + or function return 'true' if this name is the name of a function + + what should be returned is tested just by a '(' character that means if there's + a '(' character after a name that function returns 'true' +*/ +bool ReadName(std::string & result) +{ +int character; + + + result.erase(); + character = *pstring; + + /* + the first letter must be from range 'a' - 'z' or 'A' - 'Z' + */ + if( ! (( character>='a' && character<='z' ) || ( character>='A' && character<='Z' )) ) + Error( err_unknown_character ); + + + do + { + result += static_cast( character ); + character = * ++pstring; + } + while( (character>='a' && character<='z') || + (character>='A' && character<='Z') || + (character>='0' && character<='9') || + character=='_' ); + + + SkipWhiteCharacters(); + + + /* + if there's a character '(' that means this name is a name of a function + */ + if( *pstring == '(' ) + { + ++pstring; + return true; + } + + +return false; +} + + +/*! + we're checking whether the first character is '-' or '+' + if it is we'll return 'true' and if it is equally '-' we'll set the 'sign' member of 'result' +*/ +bool TestSign(Item & result) +{ + SkipWhiteCharacters(); + result.sign = false; + + if( *pstring == '-' || *pstring == '+' ) + { + if( *pstring == '-' ) + result.sign = true; + + ++pstring; + + return true; + } + +return false; +} + + +/*! + we're reading the name of a variable or a function + if is there a function we'll return 'true' +*/ +bool ReadVariableOrFunction(Item & result) +{ +std::string name; +bool is_it_name_of_function = ReadName(name); + + if( is_it_name_of_function ) + { + /* + we've read the name of a function + */ + result.function_name = name; + result.type = Item::first_bracket; + result.function = true; + } + else + { + /* + we've read the name of a variable and we're getting its value now + */ + result.value = GetValueOfVariable( name ); + } + +return is_it_name_of_function; +} + + + + +/*! + we're reading a numerical value directly from the string +*/ +void ReadValue(Item & result, int reading_base) +{ +const char * new_stack_pointer; +bool value_read; +Conv conv; + + conv.base = reading_base; + conv.comma = comma; + conv.comma2 = comma2; + conv.group = group; + + uint carry = result.value.FromString(pstring, conv, &new_stack_pointer, &value_read); + pstring = new_stack_pointer; + + if( carry ) + Error( err_overflow ); + + if( !value_read ) + Error( err_unknown_character ); +} + + +/*! + this method returns true if 'character' is a proper first digit for the value (or a comma -- can be first too) +*/ +bool ValueStarts(int character, int base) +{ + if( character == comma ) + return true; + + if( comma2!=0 && character==comma2 ) + return true; + + if( Misc::CharToDigit(character, base) != -1 ) + return true; + +return false; +} + + +/*! + we're reading the item + + return values: + 0 - all ok, the item is successfully read + 1 - the end of the string (the item is not read) + 2 - the final bracket ')' +*/ +int ReadValueVariableOrFunction(Item & result) +{ +bool it_was_sign = false; +int character; + + + if( TestSign(result) ) + // 'result.sign' was set as well + it_was_sign = true; + + SkipWhiteCharacters(); + character = ToLowerCase( *pstring ); + + + if( character == 0 ) + { + if( it_was_sign ) + // at the end of the string a character like '-' or '+' has left + Error( err_unexpected_end ); + + // there's the end of the string here + return 1; + } + else + if( character == '(' ) + { + // we've got a normal bracket (not a function) + result.type = Item::first_bracket; + result.function = false; + ++pstring; + + return 0; + } + else + if( character == ')' ) + { + // we've got a final bracket + // (in this place we can find a final bracket only when there are empty brackets + // without any values inside or with a sign '-' or '+' inside) + + if( it_was_sign ) + Error( err_unexpected_final_bracket ); + + result.type = Item::last_bracket; + + // we don't increment 'pstring', this final bracket will be read next by the + // 'ReadOperatorAndCheckFinalBracket(...)' method + + return 2; + } + else + if( character == '#' ) + { + ++pstring; + SkipWhiteCharacters(); + + // after '#' character we do not allow '-' or '+' (can be white characters) + if( ValueStarts(*pstring, 16) ) + ReadValue( result, 16 ); + else + Error( err_unknown_character ); + } + else + if( character == '&' ) + { + ++pstring; + SkipWhiteCharacters(); + + // after '&' character we do not allow '-' or '+' (can be white characters) + if( ValueStarts(*pstring, 2) ) + ReadValue( result, 2 ); + else + Error( err_unknown_character ); + } + else + if( ValueStarts(character, base) ) + { + ReadValue( result, base ); + } + else + if( character>='a' && character<='z' ) + { + if( ReadVariableOrFunction(result) ) + // we've read the name of a function + return 0; + } + else + Error( err_unknown_character ); + + + + /* + we've got a value in the 'result' + this value is from a variable or directly from the string + */ + result.type = Item::numerical_value; + + if( result.sign ) + { + result.value.ChangeSign(); + result.sign = false; + } + + +return 0; +} + + +void InsertOperatorToTable(const char * name, typename MatOperator::Type type) +{ + operators_table.insert( std::make_pair(std::string(name), type) ); +} + + +/*! + this method creates the table of operators +*/ +void CreateMathematicalOperatorsTable() +{ + InsertOperatorToTable("||", MatOperator::lor); + InsertOperatorToTable("&&", MatOperator::land); + InsertOperatorToTable("!=", MatOperator::neq); + InsertOperatorToTable("==", MatOperator::eq); + InsertOperatorToTable(">=", MatOperator::get); + InsertOperatorToTable("<=", MatOperator::let); + InsertOperatorToTable(">", MatOperator::gt); + InsertOperatorToTable("<", MatOperator::lt); + InsertOperatorToTable("-", MatOperator::sub); + InsertOperatorToTable("+", MatOperator::add); + InsertOperatorToTable("/", MatOperator::div); + InsertOperatorToTable("*", MatOperator::mul); + InsertOperatorToTable("^", MatOperator::pow); +} + + +/*! + returns true if 'str2' is the substring of str1 + + e.g. + true when str1="test" and str2="te" +*/ +bool IsSubstring(const std::string & str1, const std::string & str2) +{ + if( str2.length() > str1.length() ) + return false; + + for(typename std::string::size_type i=0 ; ifirst, oper) ) + { + oper.erase( --oper.end() ); // we've got mininum one element + + if( iter_old != operators_table.end() && iter_old->first == oper ) + { + result.type = Item::mat_operator; + result.moperator.SetType( iter_old->second ); + break; + } + + Error( err_unknown_operator ); + } + + iter_old = iter_new; + } +} + + +/*! + this method makes a calculation for the percentage operator + e.g. + 1000-50% = 1000-(1000*0,5) = 500 +*/ +void OperatorPercentage() +{ + if( stack_index < 3 || + stack[stack_index-1].type != Item::numerical_value || + stack[stack_index-2].type != Item::mat_operator || + stack[stack_index-3].type != Item::numerical_value ) + Error(err_percent_from); + + ++pstring; + SkipWhiteCharacters(); + + uint c = 0; + c += stack[stack_index-1].value.Div(100); + c += stack[stack_index-1].value.Mul(stack[stack_index-3].value); + + if( c ) + Error(err_overflow); +} + + +/*! + this method reads a mathematic operators + or the final bracket or the semicolon operator + + return values: + 0 - ok + 1 - the string is finished +*/ +int ReadOperator(Item & result) +{ + SkipWhiteCharacters(); + + if( *pstring == '%' ) + OperatorPercentage(); + + + if( *pstring == 0 ) + return 1; + else + if( *pstring == ')' ) + { + result.type = Item::last_bracket; + ++pstring; + } + else + if( *pstring == ';' || (param_sep!=0 && *pstring==param_sep) ) + { + result.type = Item::semicolon; + ++pstring; + } + else + if( (*pstring>='a' && *pstring<='z') || (*pstring>='A' && *pstring<='Z') ) + { + // short mul (without any operators) + + result.type = Item::mat_operator; + result.moperator.SetType( MatOperator::shortmul ); + } + else + ReadMathematicalOperator(result); + +return 0; +} + + + +/*! + this method is making the standard mathematic operation like '-' '+' '*' '/' and '^' + + the operation is made between 'value1' and 'value2' + the result of this operation is stored in the 'value1' +*/ +void MakeStandardMathematicOperation(ValueType & value1, typename MatOperator::Type mat_operator, + const ValueType & value2) +{ +uint res; + + calculated = true; + + switch( mat_operator ) + { + case MatOperator::land: + (!value1.IsZero() && !value2.IsZero()) ? value1.SetOne() : value1.SetZero(); + break; + + case MatOperator::lor: + (!value1.IsZero() || !value2.IsZero()) ? value1.SetOne() : value1.SetZero(); + break; + + case MatOperator::eq: + (value1 == value2) ? value1.SetOne() : value1.SetZero(); + break; + + case MatOperator::neq: + (value1 != value2) ? value1.SetOne() : value1.SetZero(); + break; + + case MatOperator::lt: + (value1 < value2) ? value1.SetOne() : value1.SetZero(); + break; + + case MatOperator::gt: + (value1 > value2) ? value1.SetOne() : value1.SetZero(); + break; + + case MatOperator::let: + (value1 <= value2) ? value1.SetOne() : value1.SetZero(); + break; + + case MatOperator::get: + (value1 >= value2) ? value1.SetOne() : value1.SetZero(); + break; + + case MatOperator::sub: + if( value1.Sub(value2) ) Error( err_overflow ); + break; + + case MatOperator::add: + if( value1.Add(value2) ) Error( err_overflow ); + break; + + case MatOperator::mul: + case MatOperator::shortmul: + if( value1.Mul(value2) ) Error( err_overflow ); + break; + + case MatOperator::div: + if( value2.IsZero() ) Error( err_division_by_zero ); + if( value1.Div(value2) ) Error( err_overflow ); + break; + + case MatOperator::pow: + res = value1.Pow( value2 ); + + if( res == 1 ) Error( err_overflow ); + else + if( res == 2 ) Error( err_improper_argument ); + + break; + + default: + /* + on the stack left an unknown operator but we had to recognize its before + that means there's an error in our algorithm + */ + Error( err_internal_error ); + } +} + + + + +/*! + this method is trying to roll the stack up with the operator's priority + + for example if there are: + "1 - 2 +" + we can subtract "1-2" and the result store on the place where is '1' and copy the last + operator '+', that means there'll be '-1+' on our stack + + but if there are: + "1 - 2 *" + we can't roll the stack up because the operator '*' has greater priority than '-' +*/ +void TryRollingUpStackWithOperatorPriority() +{ + while( stack_index>=4 && + stack[stack_index-4].type == Item::numerical_value && + stack[stack_index-3].type == Item::mat_operator && + stack[stack_index-2].type == Item::numerical_value && + stack[stack_index-1].type == Item::mat_operator && + ( + ( + // the first operator has greater priority + stack[stack_index-3].moperator.GetPriority() > stack[stack_index-1].moperator.GetPriority() + ) || + ( + // or both operators have the same priority and the first operator is not right associative + stack[stack_index-3].moperator.GetPriority() == stack[stack_index-1].moperator.GetPriority() && + stack[stack_index-3].moperator.GetAssoc() == MatOperator::non_right + ) + ) + ) + { + MakeStandardMathematicOperation(stack[stack_index-4].value, + stack[stack_index-3].moperator.GetType(), + stack[stack_index-2].value); + + + /* + copying the last operator and setting the stack pointer to the correct value + */ + stack[stack_index-3] = stack[stack_index-1]; + stack_index -= 2; + } +} + + +/*! + this method is trying to roll the stack up without testing any operators + + for example if there are: + "1 - 2" + there'll be "-1" on our stack +*/ +void TryRollingUpStack() +{ + while( stack_index >= 3 && + stack[stack_index-3].type == Item::numerical_value && + stack[stack_index-2].type == Item::mat_operator && + stack[stack_index-1].type == Item::numerical_value ) + { + MakeStandardMathematicOperation( stack[stack_index-3].value, + stack[stack_index-2].moperator.GetType(), + stack[stack_index-1].value ); + + stack_index -= 2; + } +} + + +/*! + this method is reading a value or a variable or a function + (the normal first bracket as well) and push it into the stack +*/ +int ReadValueVariableOrFunctionAndPushItIntoStack(Item & temp) +{ +int code = ReadValueVariableOrFunction( temp ); + + if( code == 0 ) + { + if( stack_index < stack.size() ) + stack[stack_index] = temp; + else + stack.push_back( temp ); + + ++stack_index; + } + + if( code == 2 ) + // there was a final bracket, we didn't push it into the stack + // (it'll be read by the 'ReadOperatorAndCheckFinalBracket' method next) + code = 0; + + +return code; +} + + + +/*! + this method calculate how many parameters there are on the stack + and the index of the first parameter + + if there aren't any parameters on the stack this method returns + 'size' equals zero and 'index' pointing after the first bracket + (on non-existend element) +*/ +void HowManyParameters(int & size, int & index) +{ + size = 0; + index = stack_index; + + if( index == 0 ) + // we haven't put a first bracket on the stack + Error( err_unexpected_final_bracket ); + + + if( stack[index-1].type == Item::first_bracket ) + // empty brackets + return; + + for( --index ; index>=1 ; index-=2 ) + { + if( stack[index].type != Item::numerical_value ) + { + /* + this element must be 'numerical_value', if not that means + there's an error in our algorithm + */ + Error( err_internal_error ); + } + + ++size; + + if( stack[index-1].type != Item::semicolon ) + break; + } + + if( index<1 || stack[index-1].type != Item::first_bracket ) + { + /* + we haven't put a first bracket on the stack + */ + Error( err_unexpected_final_bracket ); + } +} + + +/*! + this method is being called when the final bracket ')' is being found + + this method's rolling the stack up, counting how many parameters there are + on the stack and if there was a function it's calling the function +*/ +void RollingUpFinalBracket() +{ +int amount_of_parameters; +int index; + + + if( stack_index<1 || + (stack[stack_index-1].type != Item::numerical_value && + stack[stack_index-1].type != Item::first_bracket) + ) + Error( err_unexpected_final_bracket ); + + + TryRollingUpStack(); + HowManyParameters(amount_of_parameters, index); + + // 'index' will be greater than zero + // 'amount_of_parameters' can be zero + + + if( amount_of_parameters==0 && !stack[index-1].function ) + Error( err_unexpected_final_bracket ); + + + bool was_sign = stack[index-1].sign; + + + if( stack[index-1].function ) + { + // the result of a function will be on 'stack[index-1]' + // and then at the end we'll set the correct type (numerical value) of this element + CallFunction(stack[index-1].function_name, amount_of_parameters, index); + } + else + { + /* + there was a normal bracket (not a funcion) + */ + if( amount_of_parameters != 1 ) + Error( err_unexpected_semicolon_operator ); + + + /* + in the place where is the bracket we put the result + */ + stack[index-1] = stack[index]; + } + + + /* + if there was a '-' character before the first bracket + we change the sign of the expression + */ + stack[index-1].sign = false; + + if( was_sign ) + stack[index-1].value.ChangeSign(); + + stack[index-1].type = Item::numerical_value; + + + /* + the pointer of the stack will be pointing on the next (non-existing now) element + */ + stack_index = index; +} + + +/*! + this method is putting the operator on the stack +*/ + +void PushOperatorIntoStack(Item & temp) +{ + if( stack_index < stack.size() ) + stack[stack_index] = temp; + else + stack.push_back( temp ); + + ++stack_index; +} + + + +/*! + this method is reading a operator and if it's a final bracket + it's calling RollingUpFinalBracket() and reading a operator again +*/ +int ReadOperatorAndCheckFinalBracket(Item & temp) +{ + do + { + if( ReadOperator(temp) == 1 ) + { + /* + the string is finished + */ + return 1; + } + + if( temp.type == Item::last_bracket ) + RollingUpFinalBracket(); + + } + while( temp.type == Item::last_bracket ); + +return 0; +} + + +/*! + we check wheter there are only numerical value's or 'semicolon' operators on the stack +*/ +void CheckIntegrityOfStack() +{ + for(unsigned int i=0 ; iWasStopSignal() ) + Error( err_interrupt ); + + result_code = ReadValueVariableOrFunctionAndPushItIntoStack( item ); + + if( result_code == 0 ) + { + if( item.type == Item::first_bracket ) + continue; + + result_code = ReadOperatorAndCheckFinalBracket( item ); + } + + + if( result_code==1 || item.type==Item::semicolon ) + { + /* + the string is finished or the 'semicolon' operator has appeared + */ + + if( stack_index == 0 ) + Error( err_nothing_has_read ); + + TryRollingUpStack(); + + if( result_code == 1 ) + { + CheckIntegrityOfStack(); + + return; + } + } + + + PushOperatorIntoStack( item ); + TryRollingUpStackWithOperatorPriority(); + } +} + +/*! + this method is called at the end of the parsing process + + on our stack we can have another value than 'numerical_values' for example + when someone use the operator ';' in the global scope or there was an error during + parsing and the parser hasn't finished its job + + if there was an error the stack is cleaned up now + otherwise we resize stack and leave on it only 'numerical_value' items +*/ +void NormalizeStack() +{ + if( error!=err_ok || stack_index==0 ) + { + stack.clear(); + return; + } + + + /* + 'stack_index' tell us how many elements there are on the stack, + we must resize the stack now because 'stack_index' is using only for parsing + and stack has more (or equal) elements than value of 'stack_index' + */ + stack.resize( stack_index ); + + for(uint i=stack_index-1 ; i!=uint(-1) ; --i) + { + if( stack[i].type != Item::numerical_value ) + stack.erase( stack.begin() + i ); + } +} + + +public: + + +/*! + the default constructor +*/ +Parser(): default_stack_size(100) +{ + pstop_calculating = 0; + puser_variables = 0; + puser_functions = 0; + pfunction_local_variables = 0; + base = 10; + deg_rad_grad = 1; + error = err_ok; + group = 0; + comma = '.'; + comma2 = ','; + param_sep = 0; + + CreateFunctionsTable(); + CreateVariablesTable(); + CreateMathematicalOperatorsTable(); +} + + +/*! + the assignment operator +*/ +Parser & operator=(const Parser & p) +{ + pstop_calculating = p.pstop_calculating; + puser_variables = p.puser_variables; + puser_functions = p.puser_functions; + pfunction_local_variables = 0; + base = p.base; + deg_rad_grad = p.deg_rad_grad; + error = p.error; + group = p.group; + comma = p.comma; + comma2 = p.comma2; + param_sep = p.param_sep; + + /* + we don't have to call 'CreateFunctionsTable()' etc. + we can only copy these tables + */ + functions_table = p.functions_table; + variables_table = p.variables_table; + operators_table = p.operators_table; + + visited_variables = p.visited_variables; + visited_functions = p.visited_functions; + +return *this; +} + + +/*! + the copying constructor +*/ +Parser(const Parser & p): default_stack_size(p.default_stack_size) +{ + operator=(p); +} + + +/*! + the new base of mathematic system + default is 10 +*/ +void SetBase(int b) +{ + if( b>=2 && b<=16 ) + base = b; +} + + +/*! + the unit of angles used in: sin,cos,tan,cot,asin,acos,atan,acot + 0 - deg + 1 - rad (default) + 2 - grad +*/ +void SetDegRadGrad(int angle) +{ + if( angle >= 0 || angle <= 2 ) + deg_rad_grad = angle; +} + +/*! + this method sets a pointer to the object which tell us whether we should stop + calculations +*/ +void SetStopObject(const volatile StopCalculating * ps) +{ + pstop_calculating = ps; +} + + +/*! + this method sets the new table of user-defined variables + if you don't want any other variables just put zero value into the 'puser_variables' variable + + (you can have only one table at the same time) +*/ +void SetVariables(const Objects * pv) +{ + puser_variables = pv; +} + + +/*! + this method sets the new table of user-defined functions + if you don't want any other functions just put zero value into the 'puser_functions' variable + + (you can have only one table at the same time) +*/ +void SetFunctions(const Objects * pf) +{ + puser_functions = pf; +} + + +/*! + setting the group character + default zero (not used) +*/ +void SetGroup(int g) +{ + group = g; +} + + +/*! + setting the main comma operator and the additional comma operator + the additional operator can be zero (which means it is not used) + default are: '.' and ',' +*/ +void SetComma(int c, int c2 = 0) +{ + comma = c; + comma2 = c2; +} + + +/*! + setting an additional character which is used as a parameters separator + the main parameters separator is a semicolon (is used always) + + this character is used also as a global separator +*/ +void SetParamSep(int s) +{ + param_sep = s; +} + + +/*! + the main method using for parsing string +*/ +ErrorCode Parse(const char * str) +{ + stack_index = 0; + pstring = str; + error = err_ok; + calculated = false; + + stack.resize( default_stack_size ); + + try + { + Parse(); + } + catch(ErrorCode c) + { + error = c; + calculated = false; + } + + NormalizeStack(); + +return error; +} + + +/*! + the main method using for parsing string +*/ +ErrorCode Parse(const std::string & str) +{ + return Parse(str.c_str()); +} + + +#ifndef TTMATH_DONT_USE_WCHAR + +/*! + the main method using for parsing string +*/ +ErrorCode Parse(const wchar_t * str) +{ + Misc::AssignString(wide_to_ansi, str); + +return Parse(wide_to_ansi.c_str()); + + // !! wide_to_ansi clearing can be added here +} + + +/*! + the main method using for parsing string +*/ +ErrorCode Parse(const std::wstring & str) +{ + return Parse(str.c_str()); +} + +#endif + + +/*! + this method returns true is something was calculated + (at least one mathematical operator was used or a function or variable) + e.g. true if the string to Parse() looked like this: + "1+1" + "2*3" + "sin(5)" + + if the string was e.g. "678" the result is false +*/ +bool Calculated() +{ + return calculated; +} + + +/*! + initializing coefficients used when calculating the gamma (or factorial) function + this speed up the next calculations + you don't have to call this method explicitly + these coefficients will be calculated when needed +*/ +void InitCGamma() +{ + cgamma.InitAll(); +} + + +}; + + + +} // namespace + + +#endif diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmaththreads.h b/3rd_party/ttmath-0.9.3/ttmath/ttmaththreads.h new file mode 100644 index 00000000..586227f2 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmaththreads.h @@ -0,0 +1,250 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2009, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef headerfilettmaththreads +#define headerfilettmaththreads + +#include "ttmathtypes.h" + +#ifdef TTMATH_WIN32_THREADS +#include +#include +#endif + +#ifdef TTMATH_POSIX_THREADS +#include +#endif + + + +/*! + \file ttmaththreads.h + \brief Some objects used in multithreads environment +*/ + + +/* + this is a simple skeleton of a program in multithreads environment: + + #define TTMATH_MULTITHREADS + #include + + TTMATH_MULTITHREADS_HELPER + + int main() + { + [...] + } + + make sure that macro TTMATH_MULTITHREADS is defined and (somewhere in *.cpp file) + use TTMATH_MULTITHREADS_HELPER macro (outside of any classes/functions/namespaces scope) +*/ + + +namespace ttmath +{ + + +#ifdef TTMATH_WIN32_THREADS + + /* + we use win32 threads + */ + + + /*! + in multithreads environment you should use TTMATH_MULTITHREADS_HELPER macro + somewhere in *.cpp file + + (at the moment in win32 this macro does nothing) + */ + #define TTMATH_MULTITHREADS_HELPER + + + /*! + objects of this class are used to synchronize + */ + class ThreadLock + { + HANDLE mutex_handle; + + + void CreateName(char * buffer) const + { + #ifdef _MSC_VER + #pragma warning (disable : 4996) + // warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. + #endif + + sprintf(buffer, "TTMATH_LOCK_%ul", (unsigned long)GetCurrentProcessId()); + + #ifdef _MSC_VER + #pragma warning (default : 4996) + #endif + } + + + public: + + bool Lock() + { + char buffer[50]; + + CreateName(buffer); + mutex_handle = CreateMutexA(0, false, buffer); + + if( mutex_handle == 0 ) + return false; + + WaitForSingleObject(mutex_handle, INFINITE); + + return true; + } + + + ThreadLock() + { + mutex_handle = 0; + } + + + ~ThreadLock() + { + if( mutex_handle != 0 ) + { + ReleaseMutex(mutex_handle); + CloseHandle(mutex_handle); + } + } + }; + +#endif // #ifdef TTMATH_WIN32_THREADS + + + + + +#ifdef TTMATH_POSIX_THREADS + + /* + we use posix threads + */ + + + /*! + in multithreads environment you should use TTMATH_MULTITHREADS_HELPER macro + somewhere in *.cpp file + (this macro defines a pthread_mutex_t object used by TTMath library) + */ + #define TTMATH_MULTITHREADS_HELPER \ + namespace ttmath \ + { \ + pthread_mutex_t ttmath_mutex = PTHREAD_MUTEX_INITIALIZER; \ + } + + + /*! + ttmath_mutex will be defined by TTMATH_MULTITHREADS_HELPER macro + */ + extern pthread_mutex_t ttmath_mutex; + + + /*! + objects of this class are used to synchronize + */ + class ThreadLock + { + public: + + bool Lock() + { + if( pthread_mutex_lock(&ttmath_mutex) != 0 ) + return false; + + return true; + } + + + ~ThreadLock() + { + pthread_mutex_unlock(&ttmath_mutex); + } + }; + +#endif // #ifdef TTMATH_POSIX_THREADS + + + + +#if !defined(TTMATH_POSIX_THREADS) && !defined(TTMATH_WIN32_THREADS) + + /*! + we don't use win32 and pthreads + */ + + /*! + */ + #define TTMATH_MULTITHREADS_HELPER + + + /*! + objects of this class are used to synchronize + actually we don't synchronize, the method Lock() returns always 'false' + */ + class ThreadLock + { + public: + + bool Lock() + { + return false; + } + }; + + +#endif // #if !defined(TTMATH_POSIX_THREADS) && !defined(TTMATH_WIN32_THREADS) + + + + + +} // namespace + +#endif + diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmathtypes.h b/3rd_party/ttmath-0.9.3/ttmath/ttmathtypes.h new file mode 100644 index 00000000..3d9ddbe7 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmathtypes.h @@ -0,0 +1,676 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2012, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef headerfilettmathtypes +#define headerfilettmathtypes + +/*! + \file ttmathtypes.h + \brief constants used in the library + + As our library is written in header files (templates) we cannot use + constants like 'const int' etc. because we should have some source files + *.cpp to define this variables. Only what we can have are constants + defined by #define preprocessor macros. + + All macros are preceded by TTMATH_ prefix +*/ + + +#include +#include +#include + +#ifndef _MSC_VER +#include +// for uint64_t and int64_t on a 32 bit platform +#endif + + + +/*! + the version of the library + + TTMATH_PRERELEASE_VER is either zero or one + zero means that this is the release version of the library + (one means something like beta) +*/ +#define TTMATH_MAJOR_VER 0 +#define TTMATH_MINOR_VER 9 +#define TTMATH_REVISION_VER 3 + +#define TTMATH_PRERELEASE_VER 0 + + + +/*! + you can define a platform explicitly by defining either + TTMATH_PLATFORM32 or TTMATH_PLATFORM64 macro +*/ +#if !defined TTMATH_PLATFORM32 && !defined TTMATH_PLATFORM64 + + #if !defined _M_X64 && !defined __x86_64__ + + /* + other platforms than x86 and amd64 are not recognized at the moment + so you should set TTMATH_PLATFORMxx manually + */ + + // we're using a 32bit platform + #define TTMATH_PLATFORM32 + + #else + + // we're using a 64bit platform + #define TTMATH_PLATFORM64 + + #endif + +#endif + + +/*! + asm version of the library is available by default only for: + x86 and amd64 platforms and for Microsoft Visual and GCC compilers + + but you can force using asm version (the same asm as for Microsoft Visual) + by defining TTMATH_FORCEASM macro + you have to be sure that your compiler accept such an asm format +*/ +#ifndef TTMATH_FORCEASM + + #if !defined __i386__ && !defined _X86_ && !defined _M_IX86 && !defined __x86_64__ && !defined _M_X64 + /*! + x86 architecture: + __i386__ defined by GNU C + _X86_ defined by MinGW32 + _M_IX86 defined by Visual Studio, Intel C/C++, Digital Mars and Watcom C/C++ + + amd64 architecture: + __x86_64__ defined by GNU C, CLANG (LLVM) and Sun Studio + _M_X64 defined by Visual Studio + + asm version is available only for x86 or amd64 platforms + */ + #define TTMATH_NOASM + #endif + + + + #if !defined _MSC_VER && !defined __GNUC__ + /*! + another compilers than MS VC or GCC or CLANG (LLVM) by default use no asm version + (CLANG defines __GNUC__ too) + */ + #define TTMATH_NOASM + #endif + +#endif + + +namespace ttmath +{ + + +#ifdef TTMATH_PLATFORM32 + + /*! + on 32bit platforms one word (uint, sint) will be equal 32bits + */ + typedef unsigned int uint; + typedef signed int sint; + + /*! + on 32 bit platform ulint and slint will be equal 64 bits + */ + #ifdef _MSC_VER + // long long on MS Windows (Visual and GCC mingw compilers) have 64 bits + // stdint.h is not available on Visual Studio prior to VS 2010 version + typedef unsigned long long int ulint; + typedef signed long long int slint; + #else + // we do not use 'long' here because there is a difference in unix and windows + // environments: in unix 'long' has 64 bits but in windows it has only 32 bits + typedef uint64_t ulint; + typedef int64_t slint; + #endif + + /*! + how many bits there are in the uint type + */ + #define TTMATH_BITS_PER_UINT 32u + + /*! + the mask for the highest bit in the unsigned 32bit word (2^31) + */ + #define TTMATH_UINT_HIGHEST_BIT 2147483648u + + /*! + the max value of the unsigned 32bit word (2^32 - 1) + (all bits equal one) + */ + #define TTMATH_UINT_MAX_VALUE 4294967295u + + /*! + the number of words (32bit words on 32bit platform) + which are kept in built-in variables for a Big<> type + (these variables are defined in ttmathbig.h) + */ + #define TTMATH_BUILTIN_VARIABLES_SIZE 256u + + /*! + this macro returns the number of machine words + capable to hold min_bits bits + e.g. TTMATH_BITS(128) returns 4 + */ + #define TTMATH_BITS(min_bits) ((min_bits-1)/32 + 1) + +#else + + /*! + on 64bit platforms one word (uint, sint) will be equal 64bits + */ + #ifdef _MSC_VER + /* in VC 'long' type has 32 bits, __int64 is VC extension */ + typedef unsigned __int64 uint; + typedef signed __int64 sint; + #else + typedef unsigned long uint; + typedef signed long sint; + #endif + + /*! + on 64bit platforms we do not define ulint and slint + */ + + /*! + how many bits there are in the uint type + */ + #define TTMATH_BITS_PER_UINT 64ul + + /*! + the mask for the highest bit in the unsigned 64bit word (2^63) + */ + #define TTMATH_UINT_HIGHEST_BIT 9223372036854775808ul + + /*! + the max value of the unsigned 64bit word (2^64 - 1) + (all bits equal one) + */ + #define TTMATH_UINT_MAX_VALUE 18446744073709551615ul + + /*! + the number of words (64bit words on 64bit platforms) + which are kept in built-in variables for a Big<> type + (these variables are defined in ttmathbig.h) + */ + #define TTMATH_BUILTIN_VARIABLES_SIZE 128ul + + /*! + this macro returns the number of machine words + capable to hold min_bits bits + e.g. TTMATH_BITS(128) returns 2 + */ + #define TTMATH_BITS(min_bits) ((min_bits-1)/64 + 1) + +#endif +} + + +#if defined(TTMATH_MULTITHREADS) && !defined(TTMATH_MULTITHREADS_NOSYNC) + #if !defined(TTMATH_POSIX_THREADS) && !defined(TTMATH_WIN32_THREADS) + + #if defined(_WIN32) + #define TTMATH_WIN32_THREADS + #elif defined(unix) || defined(__unix__) || defined(__unix) + #define TTMATH_POSIX_THREADS + #endif + + #endif +#endif + + + +/*! + this variable defines how many iterations are performed + during some kind of calculating when we're making any long formulas + (for example Taylor series) + + it's used in ExpSurrounding0(...), LnSurrounding1(...), Sin0pi05(...), etc. + + note! there'll not be so many iterations, iterations are stopped when + there is no sense to continue calculating (for example when the result + still remains unchanged after adding next series and we know that the next + series are smaller than previous ones) +*/ +#define TTMATH_ARITHMETIC_MAX_LOOP 10000 + + + +/*! + this is a limit when calculating Karatsuba multiplication + if the size of a vector is smaller than TTMATH_USE_KARATSUBA_MULTIPLICATION_FROM_SIZE + the Karatsuba algorithm will use standard schoolbook multiplication +*/ +#ifdef TTMATH_DEBUG_LOG + // if TTMATH_DEBUG_LOG is defined then we should use the same size regardless of the compiler + #define TTMATH_USE_KARATSUBA_MULTIPLICATION_FROM_SIZE 3 +#else + #ifdef __GNUC__ + #define TTMATH_USE_KARATSUBA_MULTIPLICATION_FROM_SIZE 3 + #else + #define TTMATH_USE_KARATSUBA_MULTIPLICATION_FROM_SIZE 5 + #endif +#endif + + +/*! + this is a special value used when calculating the Gamma(x) function + if x is greater than this value then the Gamma(x) will be calculated using + some kind of series + + don't use smaller values than about 100 +*/ +#define TTMATH_GAMMA_BOUNDARY 2000 + + + + + +namespace ttmath +{ + + /*! + lib type codes: + asm_vc_32 - with asm code designed for Microsoft Visual C++ (32 bits) + asm_gcc_32 - with asm code designed for GCC (32 bits) + asm_vc_64 - with asm for VC (64 bit) + asm_gcc_64 - with asm for GCC (64 bit) + no_asm_32 - pure C++ version (32 bit) - without any asm code + no_asm_64 - pure C++ version (64 bit) - without any asm code + */ + enum LibTypeCode + { + asm_vc_32 = 0, + asm_gcc_32, + asm_vc_64, + asm_gcc_64, + no_asm_32, + no_asm_64 + }; + + + /*! + error codes + */ + enum ErrorCode + { + err_ok = 0, + err_nothing_has_read, + err_unknown_character, + err_unexpected_final_bracket, + err_stack_not_clear, + err_unknown_variable, + err_division_by_zero, + err_interrupt, + err_overflow, + err_unknown_function, + err_unknown_operator, + err_unexpected_semicolon_operator, + err_improper_amount_of_arguments, + err_improper_argument, + err_unexpected_end, + err_internal_error, + err_incorrect_name, + err_incorrect_value, + err_variable_exists, + err_variable_loop, + err_functions_loop, + err_must_be_only_one_value, + err_object_exists, + err_unknown_object, + err_still_calculating, + err_in_short_form_used_function, + err_percent_from + }; + + + /*! + this struct is used when converting to/from a string + /temporarily only in Big::ToString() and Big::FromString()/ + */ + struct Conv + { + /*! + base (radix) on which the value will be shown (or read) + default: 10 + */ + uint base; + + + /*! + used only in Big::ToString() + if true the value will be always shown in the scientific mode, e.g: 123e+30 + default: false + */ + bool scient; + + + /*! + used only in Big::ToString() + if scient is false then the value will be printed in the scientific mode + only if the exponent is greater than scien_from + default: 15 + */ + sint scient_from; + + + /*! + if 'base_round' is true and 'base' is different from 2, 4, 8, or 16 + and the result value is not an integer then we make an additional rounding + (after converting the last digit from the result is skipped) + default: true + + e.g. + Conv c; + c.base_round = false; + Big<1, 1> a = "0.1"; // decimal input + std::cout << a.ToString(c) << std::endl; // the result is: 0.099999999 + */ + bool base_round; + + + /*! + used only in Big::ToString() + tells how many digits after comma are possible + default: -1 which means all digits are printed + + set it to zero if you want integer value only + + for example when the value is: + 12.345678 and 'round' is 4 + then the result will be + 12.3457 (the last digit was rounded) + */ + sint round; + + + /*! + if true that not mattered digits in the mantissa will be cut off + (zero characters at the end -- after the comma operator) + e.g. 1234,78000 will be: 1234,78 + default: true + */ + bool trim_zeroes; + + + /*! + the main comma operator (used when reading and writing) + default is a dot '.' + */ + uint comma; + + + /*! + additional comma operator (used only when reading) + if you don't want it just set it to zero + default is a comma ',' + + this allowes you to convert from a value: + 123.45 as well as from 123,45 + */ + uint comma2; + + + /*! + it sets the character which is used for grouping + if group=' ' then: 1234,56789 will be printed as: 1 234,567 89 + + if you don't want grouping just set it to zero (which is default) + */ + uint group; + + + /*! + how many digits should be grouped (it is used if 'group' is non zero) + default: 3 + */ + uint group_digits; + + + /*! + */ + uint group_exp; // not implemented yet + + + + + Conv() + { + // default values + base = 10; + scient = false; + scient_from = 15; + base_round = true; + round = -1; + trim_zeroes = true; + comma = '.'; + comma2 = ','; + group = 0; + group_digits = 3; + group_exp = 0; + } + }; + + + + /*! + this simple class can be used in multithreading model + (you can write your own class derived from this one) + + for example: in some functions like Factorial() + /at the moment only Factorial/ you can give a pointer to + the 'stop object', if the method WasStopSignal() of this + object returns true that means we should break the calculating + and return + */ + class StopCalculating + { + public: + virtual bool WasStopSignal() const volatile { return false; } + virtual ~StopCalculating(){} + }; + + + /*! + a small class which is useful when compiling with gcc + + object of this type holds the name and the line of a file + in which the macro TTMATH_ASSERT or TTMATH_REFERENCE_ASSERT was used + */ + class ExceptionInfo + { + const char * file; + int line; + + public: + ExceptionInfo() : file(0), line(0) {} + ExceptionInfo(const char * f, int l) : file(f), line(l) {} + + std::string Where() const + { + if( !file ) + return "unknown"; + + std::ostringstream result; + result << file << ":" << line; + + return result.str(); + } + }; + + + /*! + A small class used for reporting 'reference' errors + + In the library is used macro TTMATH_REFERENCE_ASSERT which + can throw an exception of this type + + ** from version 0.9.2 this macro is removed from all methods + in public interface so you don't have to worry about it ** + + If you compile with gcc you can get a small benefit + from using method Where() (it returns std::string) with + the name and the line of a file where the macro TTMATH_REFERENCE_ASSERT + was used) + */ + class ReferenceError : public std::logic_error, public ExceptionInfo + { + public: + + ReferenceError() : std::logic_error("reference error") + { + } + + ReferenceError(const char * f, int l) : + std::logic_error("reference error"), ExceptionInfo(f,l) + { + } + + std::string Where() const + { + return ExceptionInfo::Where(); + } + }; + + + /*! + a small class used for reporting errors + + in the library is used macro TTMATH_ASSERT which + (if the condition in it is false) throw an exception + of this type + + if you compile with gcc you can get a small benefit + from using method Where() (it returns std::string) with + the name and the line of a file where the macro TTMATH_ASSERT + was used) + */ + class RuntimeError : public std::runtime_error, public ExceptionInfo + { + public: + + RuntimeError() : std::runtime_error("internal error") + { + } + + RuntimeError(const char * f, int l) : + std::runtime_error("internal error"), ExceptionInfo(f,l) + { + } + + std::string Where() const + { + return ExceptionInfo::Where(); + } + }; + + + + /*! + TTMATH_DEBUG + this macro enables further testing during writing your code + you don't have to define it in a release mode + + if this macro is set then macros TTMATH_ASSERT and TTMATH_REFERENCE_ASSERT + are set as well and these macros can throw an exception if a condition in it + is not fulfilled (look at the definition of TTMATH_ASSERT and TTMATH_REFERENCE_ASSERT) + + TTMATH_DEBUG is set automatically if DEBUG or _DEBUG are defined + */ + #if defined DEBUG || defined _DEBUG + #define TTMATH_DEBUG + #endif + + + #ifdef TTMATH_DEBUG + + #if defined(__FILE__) && defined(__LINE__) + + #define TTMATH_REFERENCE_ASSERT(expression) \ + if( &(expression) == this ) throw ttmath::ReferenceError(__FILE__, __LINE__); + + #define TTMATH_ASSERT(expression) \ + if( !(expression) ) throw ttmath::RuntimeError(__FILE__, __LINE__); + + #else + + #define TTMATH_REFERENCE_ASSERT(expression) \ + if( &(expression) == this ) throw ReferenceError(); + + #define TTMATH_ASSERT(expression) \ + if( !(expression) ) throw RuntimeError(); + #endif + + #else + #define TTMATH_REFERENCE_ASSERT(expression) + #define TTMATH_ASSERT(expression) + #endif + + + + #ifdef TTMATH_DEBUG_LOG + #define TTMATH_LOG(msg) PrintLog(msg, std::cout); + #define TTMATH_LOGC(msg, carry) PrintLog(msg, carry, std::cout); + #define TTMATH_VECTOR_LOG(msg, vector, len) PrintVectorLog(msg, std::cout, vector, len); + #define TTMATH_VECTOR_LOGC(msg, carry, vector, len) PrintVectorLog(msg, carry, std::cout, vector, len); + #else + #define TTMATH_LOG(msg) + #define TTMATH_LOGC(msg, carry) + #define TTMATH_VECTOR_LOG(msg, vector, len) + #define TTMATH_VECTOR_LOGC(msg, carry, vector, len) + #endif + + + + +} // namespace + + +#endif + diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmathuint.h b/3rd_party/ttmath-0.9.3/ttmath/ttmathuint.h new file mode 100644 index 00000000..63dc6394 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmathuint.h @@ -0,0 +1,4165 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2011, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef headerfilettmathuint +#define headerfilettmathuint + + +/*! + \file ttmathuint.h + \brief template class UInt +*/ + +#include +#include + + +#include "ttmathtypes.h" +#include "ttmathmisc.h" + + + +/*! + \brief a namespace for the TTMath library +*/ +namespace ttmath +{ + +/*! + \brief UInt implements a big integer value without a sign + + value_size - how many bytes specify our value + on 32bit platforms: value_size=1 -> 4 bytes -> 32 bits + on 64bit platforms: value_size=1 -> 8 bytes -> 64 bits + value_size = 1,2,3,4,5,6.... +*/ +template +class UInt +{ +public: + + /*! + buffer for the integer value + table[0] - the lowest word of the value + */ + uint table[value_size]; + + + + /*! + some methods used for debugging purposes + */ + + + /*! + this method is only for debugging purposes or when we want to make + a table of a variable (constant) in ttmathbig.h + + it prints the table in a nice form of several columns + */ + template + void PrintTable(ostream_type & output) const + { + // how many columns there'll be + const int columns = 8; + + int c = 1; + for(int i=value_size-1 ; i>=0 ; --i) + { + output << "0x" << std::setfill('0'); + + #ifdef TTMATH_PLATFORM32 + output << std::setw(8); + #else + output << std::setw(16); + #endif + + output << std::hex << table[i]; + + if( i>0 ) + { + output << ", "; + + if( ++c > columns ) + { + output << std::endl; + c = 1; + } + } + } + + output << std::dec << std::endl; + } + + + /*! + this method is used when macro TTMATH_DEBUG_LOG is defined + */ + template + static void PrintVectorLog(const char_type * msg, ostream_type & output, const uint * vector, uint vector_len) + { + output << msg << std::endl; + + for(uint i=0 ; i + static void PrintVectorLog(const char_type * msg, uint carry, ostream_type & output, const uint * vector, uint vector_len) + { + PrintVectorLog(msg, output, vector, vector_len); + output << " carry: " << carry << std::endl; + } + + + /*! + this method is used when macro TTMATH_DEBUG_LOG is defined + */ + template + void PrintLog(const char_type * msg, ostream_type & output) const + { + PrintVectorLog(msg, output, table, value_size); + } + + + /*! + this method is used when macro TTMATH_DEBUG_LOG is defined + */ + template + void PrintLog(const char_type * msg, uint carry, ostream_type & output) const + { + PrintVectorLog(msg, output, table, value_size); + output << " carry: " << carry << std::endl; + } + + + /*! + this method returns the size of the table + */ + uint Size() const + { + return value_size; + } + + + /*! + this method sets zero + */ + void SetZero() + { + // in the future here can be 'memset' + + for(uint i=0 ; i & ss2) + { + for(uint i=0 ; i=0 && temp_table_index=0 ; --i) + table[i] = 0; + + + TTMATH_LOG("UInt::SetFromTable") + } + +#endif + + +#ifdef TTMATH_PLATFORM64 + /*! + this method copies the value stored in an another table + (warning: first values in temp_table are the highest words -- it's different + from our table) + + ***this method is created only on a 64bit platform*** + + we copy as many words as it is possible + + if temp_table_len is bigger than value_size we'll try to round + the lowest word from table depending on the last not used bit in temp_table + (this rounding isn't a perfect rounding -- look at the description below) + + and if temp_table_len is smaller than value_size we'll clear the rest words + in the table + + warning: we're using 'temp_table' as a pointer at 32bit words + */ + void SetFromTable(const unsigned int * temp_table, uint temp_table_len) + { + uint temp_table_index = 0; + sint i; // 'i' with a sign + + for(i=value_size-1 ; i>=0 && temp_table_index= 0 ; --i) + table[i] = 0; + + TTMATH_LOG("UInt::SetFromTable") + } + +#endif + + + + + + /*! + * + * basic mathematic functions + * + */ + + + + + /*! + this method adds one to the existing value + */ + uint AddOne() + { + return AddInt(1); + } + + + /*! + this method subtracts one from the existing value + */ + uint SubOne() + { + return SubInt(1); + } + + +private: + + + /*! + an auxiliary method for moving bits into the left hand side + + this method moves only words + */ + void RclMoveAllWords(uint & rest_bits, uint & last_c, uint bits, uint c) + { + rest_bits = bits % TTMATH_BITS_PER_UINT; + uint all_words = bits / TTMATH_BITS_PER_UINT; + uint mask = ( c ) ? TTMATH_UINT_MAX_VALUE : 0; + + + if( all_words >= value_size ) + { + if( all_words == value_size && rest_bits == 0 ) + last_c = table[0] & 1; + // else: last_c is default set to 0 + + // clearing + for(uint i = 0 ; i 0 ) + { + // 0 < all_words < value_size + + sint first, second; + last_c = table[value_size - all_words] & 1; // all_words is greater than 0 + + // copying the first part of the value + for(first = value_size-1, second=first-all_words ; second>=0 ; --first, --second) + table[first] = table[second]; + + // setting the rest to 'c' + for( ; first>=0 ; --first ) + table[first] = mask; + } + + TTMATH_LOG("UInt::RclMoveAllWords") + } + +public: + + /*! + moving all bits into the left side 'bits' times + return value <- this <- C + + bits is from a range of <0, man * TTMATH_BITS_PER_UINT> + or it can be even bigger then all bits will be set to 'c' + + the value c will be set into the lowest bits + and the method returns state of the last moved bit + */ + uint Rcl(uint bits, uint c=0) + { + uint last_c = 0; + uint rest_bits = bits; + + if( bits == 0 ) + return 0; + + if( bits >= TTMATH_BITS_PER_UINT ) + RclMoveAllWords(rest_bits, last_c, bits, c); + + if( rest_bits == 0 ) + { + TTMATH_LOG("UInt::Rcl") + return last_c; + } + + // rest_bits is from 1 to TTMATH_BITS_PER_UINT-1 now + if( rest_bits == 1 ) + { + last_c = Rcl2_one(c); + } + else if( rest_bits == 2 ) + { + // performance tests showed that for rest_bits==2 it's better to use Rcl2_one twice instead of Rcl2(2,c) + Rcl2_one(c); + last_c = Rcl2_one(c); + } + else + { + last_c = Rcl2(rest_bits, c); + } + + TTMATH_LOGC("UInt::Rcl", last_c) + + return last_c; + } + +private: + + /*! + an auxiliary method for moving bits into the right hand side + + this method moves only words + */ + void RcrMoveAllWords(uint & rest_bits, uint & last_c, uint bits, uint c) + { + rest_bits = bits % TTMATH_BITS_PER_UINT; + uint all_words = bits / TTMATH_BITS_PER_UINT; + uint mask = ( c ) ? TTMATH_UINT_MAX_VALUE : 0; + + + if( all_words >= value_size ) + { + if( all_words == value_size && rest_bits == 0 ) + last_c = (table[value_size-1] & TTMATH_UINT_HIGHEST_BIT) ? 1 : 0; + // else: last_c is default set to 0 + + // clearing + for(uint i = 0 ; i 0 ) + { + // 0 < all_words < value_size + + uint first, second; + last_c = (table[all_words - 1] & TTMATH_UINT_HIGHEST_BIT) ? 1 : 0; // all_words is > 0 + + // copying the first part of the value + for(first=0, second=all_words ; second this -> return value + + bits is from a range of <0, man * TTMATH_BITS_PER_UINT> + or it can be even bigger then all bits will be set to 'c' + + the value c will be set into the highest bits + and the method returns state of the last moved bit + */ + uint Rcr(uint bits, uint c=0) + { + uint last_c = 0; + uint rest_bits = bits; + + if( bits == 0 ) + return 0; + + if( bits >= TTMATH_BITS_PER_UINT ) + RcrMoveAllWords(rest_bits, last_c, bits, c); + + if( rest_bits == 0 ) + { + TTMATH_LOG("UInt::Rcr") + return last_c; + } + + // rest_bits is from 1 to TTMATH_BITS_PER_UINT-1 now + if( rest_bits == 1 ) + { + last_c = Rcr2_one(c); + } + else if( rest_bits == 2 ) + { + // performance tests showed that for rest_bits==2 it's better to use Rcr2_one twice instead of Rcr2(2,c) + Rcr2_one(c); + last_c = Rcr2_one(c); + } + else + { + last_c = Rcr2(rest_bits, c); + } + + TTMATH_LOGC("UInt::Rcr", last_c) + + return last_c; + } + + + /*! + this method moves all bits into the left side + (it returns value how many bits have been moved) + */ + uint CompensationToLeft() + { + uint moving = 0; + + // a - index a last word which is different from zero + sint a; + for(a=value_size-1 ; a>=0 && table[a]==0 ; --a); + + if( a < 0 ) + return moving; // all words in table have zero + + if( a != value_size-1 ) + { + moving += ( value_size-1 - a ) * TTMATH_BITS_PER_UINT; + + // moving all words + sint i; + for(i=value_size-1 ; a>=0 ; --i, --a) + table[i] = table[a]; + + // setting the rest word to zero + for(; i>=0 ; --i) + table[i] = 0; + } + + uint moving2 = FindLeadingBitInWord( table[value_size-1] ); + // moving2 is different from -1 because the value table[value_size-1] + // is not zero + + moving2 = TTMATH_BITS_PER_UINT - moving2 - 1; + Rcl(moving2); + + TTMATH_LOG("UInt::CompensationToLeft") + + return moving + moving2; + } + + + /*! + this method looks for the highest set bit + + result: + if 'this' is not zero: + return value - true + 'table_id' - the index of a word <0..value_size-1> + 'index' - the index of this set bit in the word <0..TTMATH_BITS_PER_UINT) + + if 'this' is zero: + return value - false + both 'table_id' and 'index' are zero + */ + bool FindLeadingBit(uint & table_id, uint & index) const + { + for(table_id=value_size-1 ; table_id!=0 && table[table_id]==0 ; --table_id); + + if( table_id==0 && table[table_id]==0 ) + { + // is zero + index = 0; + + return false; + } + + // table[table_id] is different from 0 + index = FindLeadingBitInWord( table[table_id] ); + + return true; + } + + + /*! + this method looks for the smallest set bit + + result: + if 'this' is not zero: + return value - true + 'table_id' - the index of a word <0..value_size-1> + 'index' - the index of this set bit in the word <0..TTMATH_BITS_PER_UINT) + + if 'this' is zero: + return value - false + both 'table_id' and 'index' are zero + */ + bool FindLowestBit(uint & table_id, uint & index) const + { + for(table_id=0 ; table_id= value_size ) + { + // is zero + index = 0; + table_id = 0; + + return false; + } + + // table[table_id] is different from 0 + index = FindLowestBitInWord( table[table_id] ); + + return true; + } + + + /*! + getting the 'bit_index' bit + + bit_index bigger or equal zero + */ + uint GetBit(uint bit_index) const + { + TTMATH_ASSERT( bit_index < value_size * TTMATH_BITS_PER_UINT ) + + uint index = bit_index / TTMATH_BITS_PER_UINT; + uint bit = bit_index % TTMATH_BITS_PER_UINT; + + uint temp = table[index]; + uint res = SetBitInWord(temp, bit); + + return res; + } + + + /*! + setting the 'bit_index' bit + and returning the last state of the bit + + bit_index bigger or equal zero + */ + uint SetBit(uint bit_index) + { + TTMATH_ASSERT( bit_index < value_size * TTMATH_BITS_PER_UINT ) + + uint index = bit_index / TTMATH_BITS_PER_UINT; + uint bit = bit_index % TTMATH_BITS_PER_UINT; + uint res = SetBitInWord(table[index], bit); + + TTMATH_LOG("UInt::SetBit") + + return res; + } + + + /*! + this method performs a bitwise operation AND + */ + void BitAnd(const UInt & ss2) + { + for(uint x=0 ; x & ss2) + { + for(uint x=0 ; x & ss2) + { + for(uint x=0 ; x + + for example: + BitNot2(8) = BitNot2( 1000(bin) ) = 111(bin) = 7 + */ + void BitNot2() + { + uint table_id, index; + + if( FindLeadingBit(table_id, index) ) + { + for(uint x=0 ; x>= shift; + + table[table_id] ^= mask; + } + else + table[0] = 1; + + + TTMATH_LOG("UInt::BitNot2") + } + + + + /*! + * + * Multiplication + * + * + */ + +public: + + /*! + multiplication: this = this * ss2 + + it can return a carry + */ + uint MulInt(uint ss2) + { + uint r1, r2, x1; + uint c = 0; + + UInt u(*this); + SetZero(); + + if( ss2 == 0 ) + { + TTMATH_LOGC("UInt::MulInt(uint)", 0) + return 0; + } + + for(x1=0 ; x1 + void MulInt(uint ss2, UInt & result) const + { + TTMATH_ASSERT( result_size > value_size ) + + uint r2,r1; + uint x1size=value_size; + uint x1start=0; + + result.SetZero(); + + if( ss2 == 0 ) + { + TTMATH_VECTOR_LOG("UInt::MulInt(uint, UInt<>)", result.table, result_size) + return; + } + + if( value_size > 2 ) + { + // if the value_size is smaller than or equal to 2 + // there is no sense to set x1size and x1start to another values + + for(x1size=value_size ; x1size>0 && table[x1size-1]==0 ; --x1size); + + if( x1size == 0 ) + { + TTMATH_VECTOR_LOG("UInt::MulInt(uint, UInt<>)", result.table, result_size) + return; + } + + for(x1start=0 ; x1start)", result.table, result_size) + + return; + } + + + + /*! + the multiplication 'this' = 'this' * ss2 + + algorithm: 100 - means automatically choose the fastest algorithm + */ + uint Mul(const UInt & ss2, uint algorithm = 100) + { + switch( algorithm ) + { + case 1: + return Mul1(ss2); + + case 2: + return Mul2(ss2); + + case 3: + return Mul3(ss2); + + case 100: + default: + return MulFastest(ss2); + } + } + + + /*! + the multiplication 'result' = 'this' * ss2 + + since the 'result' is twice bigger than 'this' and 'ss2' + this method never returns a carry + + algorithm: 100 - means automatically choose the fastest algorithm + */ + void MulBig(const UInt & ss2, + UInt & result, + uint algorithm = 100) + { + switch( algorithm ) + { + case 1: + return Mul1Big(ss2, result); + + case 2: + return Mul2Big(ss2, result); + + case 3: + return Mul3Big(ss2, result); + + case 100: + default: + return MulFastestBig(ss2, result); + } + } + + + + /*! + the first version of the multiplication algorithm + */ + +private: + + /*! + multiplication: this = this * ss2 + + it returns carry if it has been + */ + uint Mul1Ref(const UInt & ss2) + { + TTMATH_REFERENCE_ASSERT( ss2 ) + + UInt ss1( *this ); + SetZero(); + + for(uint i=0; i < value_size*TTMATH_BITS_PER_UINT ; ++i) + { + if( Add(*this) ) + { + TTMATH_LOGC("UInt::Mul1", 1) + return 1; + } + + if( ss1.Rcl(1) ) + if( Add(ss2) ) + { + TTMATH_LOGC("UInt::Mul1", 1) + return 1; + } + } + + TTMATH_LOGC("UInt::Mul1", 0) + + return 0; + } + + +public: + + /*! + multiplication: this = this * ss2 + can return carry + */ + uint Mul1(const UInt & ss2) + { + if( this == &ss2 ) + { + UInt copy_ss2(ss2); + return Mul1Ref(copy_ss2); + } + else + { + return Mul1Ref(ss2); + } + } + + + /*! + multiplication: result = this * ss2 + + result is twice bigger than 'this' and 'ss2' + this method never returns carry + */ + void Mul1Big(const UInt & ss2_, UInt & result) + { + UInt ss2; + uint i; + + // copying *this into result and ss2_ into ss2 + for(i=0 ; i & ss2) + { + UInt result; + uint i, c = 0; + + Mul2Big(ss2, result); + + // copying result + for(i=0 ; i & ss2, UInt & result) + { + Mul2Big2(table, ss2.table, result); + + TTMATH_LOG("UInt::Mul2Big") + } + + +private: + + /*! + an auxiliary method for calculating the multiplication + + arguments we're taking as pointers (this is to improve the Mul3Big2()- avoiding + unnecessary copying objects), the result should be taken as a pointer too, + but at the moment there is no method AddTwoInts() which can operate on pointers + */ + template + void Mul2Big2(const uint * ss1, const uint * ss2, UInt & result) + { + uint x1size = ss_size, x2size = ss_size; + uint x1start = 0, x2start = 0; + + if( ss_size > 2 ) + { + // if the ss_size is smaller than or equal to 2 + // there is no sense to set x1size (and others) to another values + + for(x1size=ss_size ; x1size>0 && ss1[x1size-1]==0 ; --x1size); + for(x2size=ss_size ; x2size>0 && ss2[x2size-1]==0 ; --x2size); + + for(x1start=0 ; x1start(ss1, ss2, result, x1start, x1size, x2start, x2size); + } + + + + /*! + an auxiliary method for calculating the multiplication + */ + template + void Mul2Big3(const uint * ss1, const uint * ss2, UInt & result, uint x1start, uint x1size, uint x2start, uint x2size) + { + uint r2, r1; + + result.SetZero(); + + if( x1size==0 || x2size==0 ) + return; + + for(uint x1=x1start ; x1 & ss2) + { + UInt result; + uint i, c = 0; + + Mul3Big(ss2, result); + + // copying result + for(i=0 ; i & ss2, UInt & result) + { + Mul3Big2(table, ss2.table, result.table); + + TTMATH_LOG("UInt::Mul3Big") + } + + + +private: + + /*! + an auxiliary method for calculating the Karatsuba multiplication + + result_size is equal ss_size*2 + */ + template + void Mul3Big2(const uint * ss1, const uint * ss2, uint * result) + { + const uint * x1, * x0, * y1, * y0; + + + if( ss_size>1 && ss_size res; + Mul2Big2(ss1, ss2, res); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#endif + + for(uint i=0 ; i(x1, x0, y1, y0, result); + } + else + { + // ss_size is even + x0 = ss1; + y0 = ss2; + x1 = ss1 + ss_size / 2; + y1 = ss2 + ss_size / 2; + + // all four vectors (x0 x1 y0 y1) are equal in size + Mul3Big3(x1, x0, y1, y0, result); + } + } + + + +#ifdef _MSC_VER +#pragma warning (disable : 4717) +//warning C4717: recursive on all control paths, function will cause runtime stack overflow +//we have the stop point in Mul3Big2() method +#endif + + + /*! + an auxiliary method for calculating the Karatsuba multiplication + + x = x1*B^m + x0 + y = y1*B^m + y0 + + first_size - is the size of vectors: x0 and y0 + second_size - is the size of vectors: x1 and y1 (can be either equal first_size or smaller about one from first_size) + + x*y = (x1*B^m + x0)(y1*B^m + y0) = z2*B^(2m) + z1*B^m + z0 + where + z0 = x0*y0 + z2 = x1*y1 + z1 = (x1 + x0)*(y1 + y0) - z2 - z0 + */ + template + void Mul3Big3(const uint * x1, const uint * x0, const uint * y1, const uint * y0, uint * result) + { + uint i, c, xc, yc; + + UInt temp, temp2; + UInt z1; + + // z0 and z2 we store directly in the result (we don't use any temporary variables) + Mul3Big2(x0, y0, result); // z0 + Mul3Big2(x1, y1, result+first_size*2); // z2 + + // now we calculate z1 + // temp = (x0 + x1) + // temp2 = (y0 + y1) + // we're using temp and temp2 with UInt, although there can be a carry but + // we simple remember it in xc and yc (xc and yc can be either 0 or 1), + // and (x0 + x1)*(y0 + y1) we calculate in this way (schoolbook algorithm): + // + // xc | temp + // yc | temp2 + // -------------------- + // (temp * temp2) + // xc*temp2 | + // yc*temp | + // xc*yc | + // ---------- z1 -------- + // + // and the result is never larger in size than 3*first_size + + xc = AddVector(x0, x1, first_size, second_size, temp.table); + yc = AddVector(y0, y1, first_size, second_size, temp2.table); + + Mul3Big2(temp.table, temp2.table, z1.table); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#endif + + // clearing the rest of z1 + for(i=first_size*2 ; i second_size ) + { + uint z1_size = result_size - first_size; + TTMATH_ASSERT( z1_size <= first_size*3 ) + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#endif + + for(i=z1_size ; i & ss2) + { + UInt result; + uint i, c = 0; + + MulFastestBig(ss2, result); + + // copying result + for(i=0 ; i & ss2, UInt & result) + { + if( value_size < TTMATH_USE_KARATSUBA_MULTIPLICATION_FROM_SIZE ) + return Mul2Big(ss2, result); + + uint x1size = value_size, x2size = value_size; + uint x1start = 0, x2start = 0; + + for(x1size=value_size ; x1size>0 && table[x1size-1]==0 ; --x1size); + for(x2size=value_size ; x2size>0 && ss2.table[x2size-1]==0 ; --x2size); + + if( x1size==0 || x2size==0 ) + { + // either 'this' or 'ss2' is equal zero - the result is zero too + result.SetZero(); + return; + } + + for(x1start=0 ; x1start(table, ss2.table, result, x1start, x1size, x2start, x2size); + + + // Karatsuba multiplication + Mul3Big(ss2, result); + + TTMATH_LOG("UInt::MulFastestBig") + } + + + /*! + * + * Division + * + * + */ + +public: + + + /*! + division by one unsigned word + + returns 1 when divisor is zero + */ + uint DivInt(uint divisor, uint * remainder = 0) + { + if( divisor == 0 ) + { + if( remainder ) + *remainder = 0; // this is for convenience, without it the compiler can report that 'remainder' is uninitialized + + TTMATH_LOG("UInt::DivInt") + + return 1; + } + + if( divisor == 1 ) + { + if( remainder ) + *remainder = 0; + + TTMATH_LOG("UInt::DivInt") + + return 0; + } + + UInt dividend(*this); + SetZero(); + + sint i; // i must be with a sign + uint r = 0; + + // we're looking for the last word in ss1 + for(i=value_size-1 ; i>0 && dividend.table[i]==0 ; --i); + + for( ; i>=0 ; --i) + DivTwoWords(r, dividend.table[i], divisor, &table[i], &r); + + if( remainder ) + *remainder = r; + + TTMATH_LOG("UInt::DivInt") + + return 0; + } + + uint DivInt(uint divisor, uint & remainder) + { + return DivInt(divisor, &remainder); + } + + + + /*! + division this = this / ss2 + + return values: + 0 - ok + 1 - division by zero + 'this' will be the quotient + 'remainder' - remainder + */ + uint Div( const UInt & divisor, + UInt * remainder = 0, + uint algorithm = 3) + { + switch( algorithm ) + { + case 1: + return Div1(divisor, remainder); + + case 2: + return Div2(divisor, remainder); + + case 3: + default: + return Div3(divisor, remainder); + } + } + + uint Div(const UInt & divisor, UInt & remainder, uint algorithm = 3) + { + return Div(divisor, &remainder, algorithm); + } + + + +private: + + /*! + return values: + 0 - none has to be done + 1 - division by zero + 2 - division should be made + */ + uint Div_StandardTest( const UInt & v, + uint & m, uint & n, + UInt * remainder = 0) + { + switch( Div_CalculatingSize(v, m, n) ) + { + case 4: // 'this' is equal v + if( remainder ) + remainder->SetZero(); + + SetOne(); + TTMATH_LOG("UInt::Div_StandardTest") + return 0; + + case 3: // 'this' is smaller than v + if( remainder ) + *remainder = *this; + + SetZero(); + TTMATH_LOG("UInt::Div_StandardTest") + return 0; + + case 2: // 'this' is zero + if( remainder ) + remainder->SetZero(); + + SetZero(); + TTMATH_LOG("UInt::Div_StandardTest") + return 0; + + case 1: // v is zero + TTMATH_LOG("UInt::Div_StandardTest") + return 1; + } + + TTMATH_LOG("UInt::Div_StandardTest") + + return 2; + } + + + + /*! + return values: + 0 - ok + 'm' - is the index (from 0) of last non-zero word in table ('this') + 'n' - is the index (from 0) of last non-zero word in v.table + 1 - v is zero + 2 - 'this' is zero + 3 - 'this' is smaller than v + 4 - 'this' is equal v + + if the return value is different than zero the 'm' and 'n' are undefined + */ + uint Div_CalculatingSize(const UInt & v, uint & m, uint & n) + { + m = n = value_size-1; + + for( ; n!=0 && v.table[n]==0 ; --n); + + if( n==0 && v.table[n]==0 ) + return 1; + + for( ; m!=0 && table[m]==0 ; --m); + + if( m==0 && table[m]==0 ) + return 2; + + if( m < n ) + return 3; + else + if( m == n ) + { + uint i; + for(i = n ; i!=0 && table[i]==v.table[i] ; --i); + + if( table[i] < v.table[i] ) + return 3; + else + if (table[i] == v.table[i] ) + return 4; + } + + return 0; + } + + +public: + + /*! + the first division algorithm + radix 2 + */ + uint Div1(const UInt & divisor, UInt * remainder = 0) + { + uint m,n, test; + + test = Div_StandardTest(divisor, m, n, remainder); + if( test < 2 ) + return test; + + if( !remainder ) + { + UInt rem; + + return Div1_Calculate(divisor, rem); + } + + return Div1_Calculate(divisor, *remainder); + } + + + /*! + the first division algorithm + radix 2 + */ + uint Div1(const UInt & divisor, UInt & remainder) + { + return Div1(divisor, &remainder); + } + + +private: + + uint Div1_Calculate(const UInt & divisor, UInt & rest) + { + if( this == &divisor ) + { + UInt divisor_copy(divisor); + return Div1_CalculateRef(divisor_copy, rest); + } + else + { + return Div1_CalculateRef(divisor, rest); + } + } + + + uint Div1_CalculateRef(const UInt & divisor, UInt & rest) + { + TTMATH_REFERENCE_ASSERT( divisor ) + + sint loop; + sint c; + + rest.SetZero(); + loop = value_size * TTMATH_BITS_PER_UINT; + c = 0; + + + div_a: + c = Rcl(1, c); + c = rest.Add(rest,c); + c = rest.Sub(divisor,c); + + c = !c; + + if(!c) + goto div_d; + + + div_b: + --loop; + if(loop) + goto div_a; + + c = Rcl(1, c); + TTMATH_LOG("UInt::Div1_Calculate") + return 0; + + + div_c: + c = Rcl(1, c); + c = rest.Add(rest,c); + c = rest.Add(divisor); + + if(c) + goto div_b; + + + div_d: + --loop; + if(loop) + goto div_c; + + c = Rcl(1, c); + c = rest.Add(divisor); + + TTMATH_LOG("UInt::Div1_Calculate") + + return 0; + } + + +public: + + /*! + the second division algorithm + + return values: + 0 - ok + 1 - division by zero + */ + uint Div2(const UInt & divisor, UInt * remainder = 0) + { + if( this == &divisor ) + { + UInt divisor_copy(divisor); + return Div2Ref(divisor_copy, remainder); + } + else + { + return Div2Ref(divisor, remainder); + } + } + + + /*! + the second division algorithm + + return values: + 0 - ok + 1 - division by zero + */ + uint Div2(const UInt & divisor, UInt & remainder) + { + return Div2(divisor, &remainder); + } + + +private: + + /*! + the second division algorithm + + return values: + 0 - ok + 1 - division by zero + */ + uint Div2Ref(const UInt & divisor, UInt * remainder = 0) + { + uint bits_diff; + uint status = Div2_Calculate(divisor, remainder, bits_diff); + if( status < 2 ) + return status; + + if( CmpBiggerEqual(divisor) ) + { + Div2(divisor, remainder); + SetBit(bits_diff); + } + else + { + if( remainder ) + *remainder = *this; + + SetZero(); + SetBit(bits_diff); + } + + TTMATH_LOG("UInt::Div2") + + return 0; + } + + + /*! + return values: + 0 - we've calculated the division + 1 - division by zero + 2 - we have to still calculate + + */ + uint Div2_Calculate(const UInt & divisor, UInt * remainder, + uint & bits_diff) + { + uint table_id, index; + uint divisor_table_id, divisor_index; + + uint status = Div2_FindLeadingBitsAndCheck( divisor, remainder, + table_id, index, + divisor_table_id, divisor_index); + + if( status < 2 ) + { + TTMATH_LOG("UInt::Div2_Calculate") + return status; + } + + // here we know that 'this' is greater than divisor + // then 'index' is greater or equal 'divisor_index' + bits_diff = index - divisor_index; + + UInt divisor_copy(divisor); + divisor_copy.Rcl(bits_diff, 0); + + if( CmpSmaller(divisor_copy, table_id) ) + { + divisor_copy.Rcr(1); + --bits_diff; + } + + Sub(divisor_copy, 0); + + TTMATH_LOG("UInt::Div2_Calculate") + + return 2; + } + + + /*! + return values: + 0 - we've calculated the division + 1 - division by zero + 2 - we have to still calculate + */ + uint Div2_FindLeadingBitsAndCheck( const UInt & divisor, + UInt * remainder, + uint & table_id, uint & index, + uint & divisor_table_id, uint & divisor_index) + { + if( !divisor.FindLeadingBit(divisor_table_id, divisor_index) ) + { + // division by zero + TTMATH_LOG("UInt::Div2_FindLeadingBitsAndCheck") + return 1; + } + + if( !FindLeadingBit(table_id, index) ) + { + // zero is divided by something + + SetZero(); + + if( remainder ) + remainder->SetZero(); + + TTMATH_LOG("UInt::Div2_FindLeadingBitsAndCheck") + + return 0; + } + + divisor_index += divisor_table_id * TTMATH_BITS_PER_UINT; + index += table_id * TTMATH_BITS_PER_UINT; + + if( divisor_table_id == 0 ) + { + // dividor has only one 32-bit word + + uint r; + DivInt(divisor.table[0], &r); + + if( remainder ) + { + remainder->SetZero(); + remainder->table[0] = r; + } + + TTMATH_LOG("UInt::Div2_FindLeadingBitsAndCheck") + + return 0; + } + + + if( Div2_DivisorGreaterOrEqual( divisor, remainder, + table_id, index, + divisor_index) ) + { + TTMATH_LOG("UInt::Div2_FindLeadingBitsAndCheck") + return 0; + } + + + TTMATH_LOG("UInt::Div2_FindLeadingBitsAndCheck") + + return 2; + } + + + /*! + return values: + true if divisor is equal or greater than 'this' + */ + bool Div2_DivisorGreaterOrEqual( const UInt & divisor, + UInt * remainder, + uint table_id, uint index, + uint divisor_index ) + { + if( divisor_index > index ) + { + // divisor is greater than this + + if( remainder ) + *remainder = *this; + + SetZero(); + + TTMATH_LOG("UInt::Div2_DivisorGreaterOrEqual") + + return true; + } + + if( divisor_index == index ) + { + // table_id == divisor_table_id as well + + uint i; + for(i = table_id ; i!=0 && table[i]==divisor.table[i] ; --i); + + if( table[i] < divisor.table[i] ) + { + // divisor is greater than 'this' + + if( remainder ) + *remainder = *this; + + SetZero(); + + TTMATH_LOG("UInt::Div2_DivisorGreaterOrEqual") + + return true; + } + else + if( table[i] == divisor.table[i] ) + { + // divisor is equal 'this' + + if( remainder ) + remainder->SetZero(); + + SetOne(); + + TTMATH_LOG("UInt::Div2_DivisorGreaterOrEqual") + + return true; + } + } + + TTMATH_LOG("UInt::Div2_DivisorGreaterOrEqual") + + return false; + } + + +public: + + /*! + the third division algorithm + */ + uint Div3(const UInt & ss2, UInt * remainder = 0) + { + if( this == &ss2 ) + { + UInt copy_ss2(ss2); + return Div3Ref(copy_ss2, remainder); + } + else + { + return Div3Ref(ss2, remainder); + } + } + + + /*! + the third division algorithm + */ + uint Div3(const UInt & ss2, UInt & remainder) + { + return Div3(ss2, &remainder); + } + + +private: + + /*! + the third division algorithm + + this algorithm is described in the following book: + "The art of computer programming 2" (4.3.1 page 272) + Donald E. Knuth + !! give the description here (from the book) + */ + uint Div3Ref(const UInt & v, UInt * remainder = 0) + { + uint m,n, test; + + test = Div_StandardTest(v, m, n, remainder); + if( test < 2 ) + return test; + + if( n == 0 ) + { + uint r; + DivInt( v.table[0], &r ); + + if( remainder ) + { + remainder->SetZero(); + remainder->table[0] = r; + } + + TTMATH_LOG("UInt::Div3") + + return 0; + } + + + // we can only use the third division algorithm when + // the divisor is greater or equal 2^32 (has more than one 32-bit word) + ++m; + ++n; + m = m - n; + Div3_Division(v, remainder, m, n); + + TTMATH_LOG("UInt::Div3") + + return 0; + } + + + +private: + + + void Div3_Division(UInt v, UInt * remainder, uint m, uint n) + { + TTMATH_ASSERT( n>=2 ) + + UInt uu, vv; + UInt q; + uint d, u_value_size, u0, u1, u2, v1, v0, j=m; + + u_value_size = Div3_Normalize(v, n, d); + + if( j+n == value_size ) + u2 = u_value_size; + else + u2 = table[j+n]; + + Div3_MakeBiggerV(v, vv); + + for(uint i = j+1 ; i & uu, uint j, uint n, uint u_max) + { + uint i; + + for(i=0 ; i so and 'i' is from <0..value_size> + // then table[i] is always correct (look at the declaration of 'uu') + uu.table[i] = u_max; + + for( ++i ; i & uu, uint j, uint n) + { + uint i; + + for(i=0 ; i & v, UInt & vv) + { + for(uint i=0 ; i & v, uint n, uint & d) + { + // v.table[n-1] is != 0 + + uint bit = (uint)FindLeadingBitInWord(v.table[n-1]); + uint move = (TTMATH_BITS_PER_UINT - bit - 1); + uint res = table[value_size-1]; + d = move; + + if( move > 0 ) + { + v.Rcl(move, 0); + Rcl(move, 0); + res = res >> (bit + 1); + } + else + { + res = 0; + } + + TTMATH_LOG("UInt::Div3_Normalize") + + return res; + } + + + void Div3_Unnormalize(UInt * remainder, uint n, uint d) + { + for(uint i=n ; i u_temp; + uint rp; + bool next_test; + + TTMATH_ASSERT( v1 != 0 ) + + u_temp.table[1] = u2; + u_temp.table[0] = u1; + u_temp.DivInt(v1, &rp); + + TTMATH_ASSERT( u_temp.table[1]==0 || u_temp.table[1]==1 ) + + do + { + bool decrease = false; + + if( u_temp.table[1] == 1 ) + decrease = true; + else + { + UInt<2> temp1, temp2; + + UInt<2>::MulTwoWords(u_temp.table[0], v0, temp1.table+1, temp1.table); + temp2.table[1] = rp; + temp2.table[0] = u0; + + if( temp1 > temp2 ) + decrease = true; + } + + next_test = false; + + if( decrease ) + { + u_temp.SubOne(); + + rp += v1; + + if( rp >= v1 ) // it means that there wasn't a carry (r & uu, + const UInt & vv, uint & qp) + { + // D4 (in the book) + + UInt vv_temp(vv); + vv_temp.MulInt(qp); + + if( uu.Sub(vv_temp) ) + { + // there was a carry + + // + // !!! this part of code was not tested + // + + --qp; + uu.Add(vv); + + // can be a carry from this additions but it should be ignored + // because it cancels with the borrow from uu.Sub(vv_temp) + } + + TTMATH_LOG("UInt::Div3_MultiplySubtract") + } + + + + + + +public: + + + /*! + power this = this ^ pow + binary algorithm (r-to-l) + + return values: + 0 - ok + 1 - carry + 2 - incorrect argument (0^0) + */ + uint Pow(UInt pow) + { + if(pow.IsZero() && IsZero()) + // we don't define zero^zero + return 2; + + UInt start(*this); + UInt result; + result.SetOne(); + uint c = 0; + + while( !c ) + { + if( pow.table[0] & 1 ) + c += result.Mul(start); + + pow.Rcr2_one(0); + if( pow.IsZero() ) + break; + + c += start.Mul(start); + } + + *this = result; + + TTMATH_LOGC("UInt::Pow(UInt<>)", c) + + return (c==0)? 0 : 1; + } + + + /*! + square root + e.g. Sqrt(9) = 3 + ('digit-by-digit' algorithm) + */ + void Sqrt() + { + UInt bit, temp; + + if( IsZero() ) + return; + + UInt value(*this); + + SetZero(); + bit.SetZero(); + bit.table[value_size-1] = (TTMATH_UINT_HIGHEST_BIT >> 1); + + while( bit > value ) + bit.Rcr(2); + + while( !bit.IsZero() ) + { + temp = *this; + temp.Add(bit); + + if( value >= temp ) + { + value.Sub(temp); + Rcr(1); + Add(bit); + } + else + { + Rcr(1); + } + + bit.Rcr(2); + } + + TTMATH_LOG("UInt::Sqrt") + } + + + + /*! + this method sets n first bits to value zero + + For example: + let n=2 then if there's a value 111 (bin) there'll be '100' (bin) + */ + void ClearFirstBits(uint n) + { + if( n >= value_size*TTMATH_BITS_PER_UINT ) + { + SetZero(); + TTMATH_LOG("UInt::ClearFirstBits") + return; + } + + uint * p = table; + + // first we're clearing the whole words + while( n >= TTMATH_BITS_PER_UINT ) + { + *p++ = 0; + n -= TTMATH_BITS_PER_UINT; + } + + if( n == 0 ) + { + TTMATH_LOG("UInt::ClearFirstBits") + return; + } + + // and then we're clearing one word which has left + // mask -- all bits are set to one + uint mask = TTMATH_UINT_MAX_VALUE; + + mask = mask << n; + + (*p) &= mask; + + TTMATH_LOG("UInt::ClearFirstBits") + } + + + /*! + this method returns true if the highest bit of the value is set + */ + bool IsTheHighestBitSet() const + { + return (table[value_size-1] & TTMATH_UINT_HIGHEST_BIT) != 0; + } + + + /*! + this method returns true if the lowest bit of the value is set + */ + bool IsTheLowestBitSet() const + { + return (*table & 1) != 0; + } + + + /*! + returning true if only the highest bit is set + */ + bool IsOnlyTheHighestBitSet() const + { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#endif + + for(uint i=0 ; i> (TTMATH_BITS_PER_UINT - rest); + + return (table[i] & mask) == 0; + } + + + + /*! + * + * conversion methods + * + */ + + + + /*! + this method converts an UInt type to this class + + this operation has mainly sense if the value from p is + equal or smaller than that one which is returned from UInt::SetMax() + + it returns a carry if the value 'p' is too big + */ + template + uint FromUInt(const UInt & p) + { + uint min_size = (value_size < argument_size)? value_size : argument_size; + uint i; + + for(i=0 ; i argument_size ) + { + // 'this' is longer than 'p' + + for( ; i)", 1) + return 1; + } + } + + TTMATH_LOGC("UInt::FromUInt(UInt<>)", 0) + + return 0; + } + + + /*! + this method converts an UInt type to this class + + this operation has mainly sense if the value from p is + equal or smaller than that one which is returned from UInt::SetMax() + + it returns a carry if the value 'p' is too big + */ + template + uint FromInt(const UInt & p) + { + return FromUInt(p); + } + + + /*! + this method converts the uint type to this class + */ + uint FromUInt(uint value) + { + for(uint i=1 ; i type to this class + + it doesn't return a carry + */ + template + UInt & operator=(const UInt & p) + { + FromUInt(p); + + return *this; + } + + + /*! + the assignment operator + */ + UInt & operator=(const UInt & p) + { + for(uint i=0 ; i)") + + return *this; + } + + + /*! + this method converts the uint type to this class + */ + UInt & operator=(uint i) + { + FromUInt(i); + + return *this; + } + + + /*! + a constructor for converting the uint to this class + */ + UInt(uint i) + { + FromUInt(i); + } + + + /*! + this method converts the sint type to this class + */ + UInt & operator=(sint i) + { + FromInt(i); + + return *this; + } + + + /*! + a constructor for converting the sint to this class + + look at the description of UInt::operator=(sint) + */ + UInt(sint i) + { + FromInt(i); + } + + +#ifdef TTMATH_PLATFORM32 + + + /*! + this method converts unsigned 64 bit int type to this class + ***this method is created only on a 32bit platform*** + */ + uint FromUInt(ulint n) + { + table[0] = (uint)n; + + if( value_size == 1 ) + { + uint c = ((n >> TTMATH_BITS_PER_UINT) == 0) ? 0 : 1; + + TTMATH_LOGC("UInt::FromUInt(ulint)", c) + return c; + } + + table[1] = (uint)(n >> TTMATH_BITS_PER_UINT); + + for(uint i=2 ; i & operator=(ulint n) + { + FromUInt(n); + + return *this; + } + + + /*! + a constructor for converting unsigned 64 bit int to this class + ***this constructor is created only on a 32bit platform*** + */ + UInt(ulint n) + { + FromUInt(n); + } + + + /*! + this operator converts signed 64 bit int type to this class + ***this operator is created only on a 32bit platform*** + */ + UInt & operator=(slint n) + { + FromInt(n); + + return *this; + } + + + /*! + a constructor for converting signed 64 bit int to this class + ***this constructor is created only on a 32bit platform*** + */ + UInt(slint n) + { + FromInt(n); + } + +#endif + + + +#ifdef TTMATH_PLATFORM64 + + + /*! + this method converts 32 bit unsigned int type to this class + ***this operator is created only on a 64bit platform*** + */ + uint FromUInt(unsigned int i) + { + return FromUInt(uint(i)); + } + + /*! + this method converts 32 bit unsigned int type to this class + ***this operator is created only on a 64bit platform*** + */ + uint FromInt(unsigned int i) + { + return FromUInt(uint(i)); + } + + + /*! + this method converts 32 bit signed int type to this class + ***this operator is created only on a 64bit platform*** + */ + uint FromInt(signed int i) + { + return FromInt(sint(i)); + } + + + /*! + this operator converts 32 bit unsigned int type to this class + ***this operator is created only on a 64bit platform*** + */ + UInt & operator=(unsigned int i) + { + FromUInt(i); + + return *this; + } + + + /*! + a constructor for converting 32 bit unsigned int to this class + ***this constructor is created only on a 64bit platform*** + */ + UInt(unsigned int i) + { + FromUInt(i); + } + + + /*! + an operator for converting 32 bit signed int to this class + ***this constructor is created only on a 64bit platform*** + */ + UInt & operator=(signed int i) + { + FromInt(i); + + return *this; + } + + + /*! + a constructor for converting 32 bit signed int to this class + ***this constructor is created only on a 64bit platform*** + */ + UInt(signed int i) + { + FromInt(i); + } + + +#endif + + + + + + /*! + a constructor for converting a string to this class (with the base=10) + */ + UInt(const char * s) + { + FromString(s); + } + + + /*! + a constructor for converting a string to this class (with the base=10) + */ + UInt(const std::string & s) + { + FromString( s.c_str() ); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + a constructor for converting a string to this class (with the base=10) + */ + UInt(const wchar_t * s) + { + FromString(s); + } + + + /*! + a constructor for converting a string to this class (with the base=10) + */ + UInt(const std::wstring & s) + { + FromString( s.c_str() ); + } + +#endif + + + + + /*! + a default constructor + + we don't clear the table + */ + UInt() + { + // when macro TTMATH_DEBUG_LOG is defined + // we set special values to the table + // in order to be everywhere the same value of the UInt object + // without this it would be difficult to analyse the log file + #ifdef TTMATH_DEBUG_LOG + #ifdef TTMATH_PLATFORM32 + for(uint i=0 ; i & u) + { + for(uint i=0 ; i)") + } + + + + /*! + a template for producting constructors for copying from another types + */ + template + UInt(const UInt & u) + { + // look that 'size' we still set as 'value_size' and not as u.value_size + FromUInt(u); + } + + + + + /*! + a destructor + */ + ~UInt() + { + } + + + /*! + this method returns the lowest value from table + + we must be sure when we using this method whether the value + will hold in an uint type or not (the rest value from the table must be zero) + */ + uint ToUInt() const + { + return table[0]; + } + + + /*! + this method converts the value to uint type + can return a carry if the value is too long to store it in uint type + */ + uint ToUInt(uint & result) const + { + result = table[0]; + + for(uint i=1 ; i> 32) != 0 ) + return 1; + + for(uint i=1 ; i + */ + double ToStringLog2(uint x) const + { + static double log_tab[] = { + 1.000000000000000000, + 0.630929753571457437, + 0.500000000000000000, + 0.430676558073393050, + 0.386852807234541586, + 0.356207187108022176, + 0.333333333333333333, + 0.315464876785728718, + 0.301029995663981195, + 0.289064826317887859, + 0.278942945651129843, + 0.270238154427319741, + 0.262649535037193547, + 0.255958024809815489, + 0.250000000000000000 + }; + + if( x<2 || x>16 ) + return 0; + + return log_tab[x-2]; + } + + +public: + + + /*! + an auxiliary method for converting to a string + it's used from Int::ToString() too (negative is set true then) + */ + template + void ToStringBase(string_type & result, uint b = 10, bool negative = false) const + { + UInt temp(*this); + uint rest, table_id, index, digits; + double digits_d; + char character; + + result.clear(); + + if( b<2 || b>16 ) + return; + + if( !FindLeadingBit(table_id, index) ) + { + result = '0'; + return; + } + + if( negative ) + result = '-'; + + digits_d = static_cast(table_id); // for not making an overflow in uint type + digits_d *= TTMATH_BITS_PER_UINT; + digits_d += index + 1; + digits_d *= ToStringLog2(b); + digits = static_cast(digits_d) + 3; // plus some epsilon + + if( result.capacity() < digits ) + result.reserve(digits); + + do + { + temp.DivInt(b, &rest); + character = static_cast(Misc::DigitToChar(rest)); + result.insert(result.end(), character); + } + while( !temp.IsZero() ); + + size_t i1 = negative ? 1 : 0; // the first is a hyphen (when negative is true) + size_t i2 = result.size() - 1; + + for( ; i1 < i2 ; ++i1, --i2 ) + { + char tempc = static_cast(result[i1]); + result[i1] = result[i2]; + result[i2] = tempc; + } + } + + + + /*! + this method converts the value to a string with a base equal 'b' + */ + void ToString(std::string & result, uint b = 10) const + { + return ToStringBase(result, b); + } + + + std::string ToString(uint b = 10) const + { + std::string result; + ToStringBase(result, b); + + return result; + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + void ToString(std::wstring & result, uint b = 10) const + { + return ToStringBase(result, b); + } + + std::wstring ToWString(uint b = 10) const + { + std::wstring result; + ToStringBase(result, b); + + return result; + } + +#endif + + + +private: + + /*! + an auxiliary method for converting from a string + */ + template + uint FromStringBase(const char_type * s, uint b = 10, const char_type ** after_source = 0, bool * value_read = 0) + { + UInt base( b ); + UInt temp; + sint z; + uint c = 0; + + SetZero(); + temp.SetZero(); + Misc::SkipWhiteCharacters(s); + + if( after_source ) + *after_source = s; + + if( value_read ) + *value_read = false; + + if( b<2 || b>16 ) + return 1; + + + for( ; (z=Misc::CharToDigit(*s, b)) != -1 ; ++s) + { + if( value_read ) + *value_read = true; + + if( c == 0 ) + { + temp.table[0] = z; + + c += Mul(base); // !! IMPROVE ME: there can be used MulInt here + c += Add(temp); + } + } + + if( after_source ) + *after_source = s; + + TTMATH_LOGC("UInt::FromString", c) + + return (c==0)? 0 : 1; + } + + +public: + + + /*! + this method converts a string into its value + it returns carry=1 if the value will be too big or an incorrect base 'b' is given + + string is ended with a non-digit value, for example: + "12" will be translated to 12 + as well as: + "12foo" will be translated to 12 too + + existing first white characters will be ommited + + if the value from s is too large the rest digits will be skipped + + after_source (if exists) is pointing at the end of the parsed string + + value_read (if exists) tells whether something has actually been read (at least one digit) + */ + uint FromString(const char * s, uint b = 10, const char ** after_source = 0, bool * value_read = 0) + { + return FromStringBase(s, b, after_source, value_read); + } + + + /*! + this method converts a string into its value + + (it returns carry=1 if the value will be too big or an incorrect base 'b' is given) + */ + uint FromString(const std::string & s, uint b = 10) + { + return FromString( s.c_str(), b ); + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + UInt & operator=(const char * s) + { + FromString(s); + + return *this; + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + UInt & operator=(const std::string & s) + { + FromString( s.c_str() ); + + return *this; + } + + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + this method converts a string into its value + */ + uint FromString(const wchar_t * s, uint b = 10, const wchar_t ** after_source = 0, bool * value_read = 0) + { + return FromStringBase(s, b, after_source, value_read); + } + + + /*! + this method converts a string into its value + + (it returns carry=1 if the value will be too big or an incorrect base 'b' is given) + */ + uint FromString(const std::wstring & s, uint b = 10) + { + return FromString( s.c_str(), b ); + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + UInt & operator=(const wchar_t * s) + { + FromString(s); + + return *this; + } + + + /*! + this operator converts a string into its value (with base = 10) + */ + UInt & operator=(const std::wstring & s) + { + FromString( s.c_str() ); + + return *this; + } + +#endif + + + /*! + * + * methods for comparing + * + */ + + + /*! + this method returns true if 'this' is smaller than 'l' + + 'index' is an index of the first word from will be the comparison performed + (note: we start the comparison from back - from the last word, when index is -1 /default/ + it is automatically set into the last word) + I introduced it for some kind of optimization made in the second division algorithm (Div2) + */ + bool CmpSmaller(const UInt & l, sint index = -1) const + { + sint i; + + if( index==-1 || index>=sint(value_size) ) + i = value_size - 1; + else + i = index; + + + for( ; i>=0 ; --i) + { + if( table[i] != l.table[i] ) + return table[i] < l.table[i]; + } + + // they're equal + return false; + } + + + + /*! + this method returns true if 'this' is bigger than 'l' + + 'index' is an index of the first word from will be the comparison performed + (note: we start the comparison from back - from the last word, when index is -1 /default/ + it is automatically set into the last word) + + I introduced it for some kind of optimization made in the second division algorithm (Div2) + */ + bool CmpBigger(const UInt & l, sint index = -1) const + { + sint i; + + if( index==-1 || index>=sint(value_size) ) + i = value_size - 1; + else + i = index; + + + for( ; i>=0 ; --i) + { + if( table[i] != l.table[i] ) + return table[i] > l.table[i]; + } + + // they're equal + return false; + } + + + /*! + this method returns true if 'this' is equal 'l' + + 'index' is an index of the first word from will be the comparison performed + (note: we start the comparison from back - from the last word, when index is -1 /default/ + it is automatically set into the last word) + */ + bool CmpEqual(const UInt & l, sint index = -1) const + { + sint i; + + if( index==-1 || index>=sint(value_size) ) + i = value_size - 1; + else + i = index; + + + for( ; i>=0 ; --i) + if( table[i] != l.table[i] ) + return false; + + return true; + } + + + + /*! + this method returns true if 'this' is smaller than or equal 'l' + + 'index' is an index of the first word from will be the comparison performed + (note: we start the comparison from back - from the last word, when index is -1 /default/ + it is automatically set into the last word) + */ + bool CmpSmallerEqual(const UInt & l, sint index=-1) const + { + sint i; + + if( index==-1 || index>=sint(value_size) ) + i = value_size - 1; + else + i = index; + + + for( ; i>=0 ; --i) + { + if( table[i] != l.table[i] ) + return table[i] < l.table[i]; + } + + // they're equal + return true; + } + + + + /*! + this method returns true if 'this' is bigger than or equal 'l' + + 'index' is an index of the first word from will be the comparison performed + (note: we start the comparison from back - from the last word, when index is -1 /default/ + it is automatically set into the last word) + */ + bool CmpBiggerEqual(const UInt & l, sint index=-1) const + { + sint i; + + if( index==-1 || index>=sint(value_size) ) + i = value_size - 1; + else + i = index; + + + for( ; i>=0 ; --i) + { + if( table[i] != l.table[i] ) + return table[i] > l.table[i]; + } + + // they're equal + return true; + } + + + /* + operators for comparising + */ + + bool operator<(const UInt & l) const + { + return CmpSmaller(l); + } + + + bool operator>(const UInt & l) const + { + return CmpBigger(l); + } + + + bool operator==(const UInt & l) const + { + return CmpEqual(l); + } + + + bool operator!=(const UInt & l) const + { + return !operator==(l); + } + + + bool operator<=(const UInt & l) const + { + return CmpSmallerEqual(l); + } + + bool operator>=(const UInt & l) const + { + return CmpBiggerEqual(l); + } + + + /*! + * + * standard mathematical operators + * + */ + + UInt operator-(const UInt & p2) const + { + UInt temp(*this); + + temp.Sub(p2); + + return temp; + } + + UInt & operator-=(const UInt & p2) + { + Sub(p2); + + return *this; + } + + UInt operator+(const UInt & p2) const + { + UInt temp(*this); + + temp.Add(p2); + + return temp; + } + + UInt & operator+=(const UInt & p2) + { + Add(p2); + + return *this; + } + + + UInt operator*(const UInt & p2) const + { + UInt temp(*this); + + temp.Mul(p2); + + return temp; + } + + + UInt & operator*=(const UInt & p2) + { + Mul(p2); + + return *this; + } + + + UInt operator/(const UInt & p2) const + { + UInt temp(*this); + + temp.Div(p2); + + return temp; + } + + + UInt & operator/=(const UInt & p2) + { + Div(p2); + + return *this; + } + + + UInt operator%(const UInt & p2) const + { + UInt temp(*this); + UInt remainder; + + temp.Div( p2, remainder ); + + return remainder; + } + + + UInt & operator%=(const UInt & p2) + { + UInt remainder; + + Div( p2, remainder ); + operator=(remainder); + + return *this; + } + + + /*! + Prefix operator e.g ++variable + */ + UInt & operator++() + { + AddOne(); + + return *this; + } + + + /*! + Postfix operator e.g variable++ + */ + UInt operator++(int) + { + UInt temp( *this ); + + AddOne(); + + return temp; + } + + + UInt & operator--() + { + SubOne(); + + return *this; + } + + + UInt operator--(int) + { + UInt temp( *this ); + + SubOne(); + + return temp; + } + + + + /*! + * + * bitwise operators + * + */ + + UInt operator~() const + { + UInt temp( *this ); + + temp.BitNot(); + + return temp; + } + + + UInt operator&(const UInt & p2) const + { + UInt temp( *this ); + + temp.BitAnd(p2); + + return temp; + } + + + UInt & operator&=(const UInt & p2) + { + BitAnd(p2); + + return *this; + } + + + UInt operator|(const UInt & p2) const + { + UInt temp( *this ); + + temp.BitOr(p2); + + return temp; + } + + + UInt & operator|=(const UInt & p2) + { + BitOr(p2); + + return *this; + } + + + UInt operator^(const UInt & p2) const + { + UInt temp( *this ); + + temp.BitXor(p2); + + return temp; + } + + + UInt & operator^=(const UInt & p2) + { + BitXor(p2); + + return *this; + } + + + UInt operator>>(int move) const + { + UInt temp( *this ); + + temp.Rcr(move); + + return temp; + } + + + UInt & operator>>=(int move) + { + Rcr(move); + + return *this; + } + + + UInt operator<<(int move) const + { + UInt temp( *this ); + + temp.Rcl(move); + + return temp; + } + + + UInt & operator<<=(int move) + { + Rcl(move); + + return *this; + } + + + /*! + * + * input/output operators for standard streams + * + * (they are very simple, in the future they should be changed) + * + */ + + +private: + + + /*! + an auxiliary method for outputing to standard streams + */ + template + static ostream_type & OutputToStream(ostream_type & s, const UInt & l) + { + string_type ss; + + l.ToString(ss); + s << ss; + + return s; + } + + +public: + + + /*! + output to standard streams + */ + friend std::ostream & operator<<(std::ostream & s, const UInt & l) + { + return OutputToStream(s, l); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + output to standard streams + */ + friend std::wostream & operator<<(std::wostream & s, const UInt & l) + { + return OutputToStream(s, l); + } + +#endif + + + +private: + + /*! + an auxiliary method for reading from standard streams + */ + template + static istream_type & InputFromStream(istream_type & s, UInt & l) + { + string_type ss; + + // char or wchar_t for operator>> + char_type z; + + // operator>> omits white characters if they're set for ommiting + s >> z; + + // we're reading only digits (base=10) + while( s.good() && Misc::CharToDigit(z, 10)>=0 ) + { + ss += z; + z = static_cast(s.get()); + } + + // we're leaving the last read character + // (it's not belonging to the value) + s.unget(); + + l.FromString(ss); + + return s; + } + +public: + + + /*! + input from standard streams + */ + friend std::istream & operator>>(std::istream & s, UInt & l) + { + return InputFromStream(s, l); + } + + +#ifndef TTMATH_DONT_USE_WCHAR + + /*! + input from standard streams + */ + friend std::wistream & operator>>(std::wistream & s, UInt & l) + { + return InputFromStream(s, l); + } + +#endif + + + /* + Following methods are defined in: + ttmathuint_x86.h + ttmathuint_x86_64.h + ttmathuint_noasm.h + */ + +#ifdef TTMATH_NOASM + static uint AddTwoWords(uint a, uint b, uint carry, uint * result); + static uint SubTwoWords(uint a, uint b, uint carry, uint * result); + +#ifdef TTMATH_PLATFORM64 + + union uint_ + { + struct + { + unsigned int low; // 32 bit + unsigned int high; // 32 bit + } u_; + + uint u; // 64 bit + }; + + + static void DivTwoWords2(uint a,uint b, uint c, uint * r, uint * rest); + static uint DivTwoWordsNormalize(uint_ & a_, uint_ & b_, uint_ & c_); + static uint DivTwoWordsUnnormalize(uint u, uint d); + static unsigned int DivTwoWordsCalculate(uint_ u_, unsigned int u3, uint_ v_); + static void MultiplySubtract(uint_ & u_, unsigned int & u3, unsigned int & q, uint_ v_); + +#endif // TTMATH_PLATFORM64 +#endif // TTMATH_NOASM + + +private: + uint Rcl2_one(uint c); + uint Rcr2_one(uint c); + uint Rcl2(uint bits, uint c); + uint Rcr2(uint bits, uint c); + +public: + static const char * LibTypeStr(); + static LibTypeCode LibType(); + uint Add(const UInt & ss2, uint c=0); + uint AddInt(uint value, uint index = 0); + uint AddTwoInts(uint x2, uint x1, uint index); + static uint AddVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result); + uint Sub(const UInt & ss2, uint c=0); + uint SubInt(uint value, uint index = 0); + static uint SubVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result); + static sint FindLeadingBitInWord(uint x); + static sint FindLowestBitInWord(uint x); + static uint SetBitInWord(uint & value, uint bit); + static void MulTwoWords(uint a, uint b, uint * result_high, uint * result_low); + static void DivTwoWords(uint a,uint b, uint c, uint * r, uint * rest); + +}; + + + +/*! + this specialization is needed in order to not confused the compiler "error: ISO C++ forbids zero-size array" + when compiling Mul3Big2() method +*/ +template<> +class UInt<0> +{ +public: + uint table[1]; + + void Mul2Big(const UInt<0> &, UInt<0> &) { TTMATH_ASSERT(false) }; + void SetZero() { TTMATH_ASSERT(false) }; + uint AddTwoInts(uint, uint, uint) { TTMATH_ASSERT(false) return 0; }; +}; + + +} //namespace + + +#include "ttmathuint_x86.h" +#include "ttmathuint_x86_64.h" +#include "ttmathuint_noasm.h" + +#endif diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmathuint_noasm.h b/3rd_party/ttmath-0.9.3/ttmath/ttmathuint_noasm.h new file mode 100644 index 00000000..07c73fc4 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmathuint_noasm.h @@ -0,0 +1,1017 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2010, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef headerfilettmathuint_noasm +#define headerfilettmathuint_noasm + + +#ifdef TTMATH_NOASM + +/*! + \file ttmathuint_noasm.h + \brief template class UInt with methods without any assembler code + + this file is included at the end of ttmathuint.h +*/ + + +namespace ttmath +{ + + /*! + returning the string represents the currect type of the library + we have following types: + asm_vc_32 - with asm code designed for Microsoft Visual C++ (32 bits) + asm_gcc_32 - with asm code designed for GCC (32 bits) + asm_vc_64 - with asm for VC (64 bit) + asm_gcc_64 - with asm for GCC (64 bit) + no_asm_32 - pure C++ version (32 bit) - without any asm code + no_asm_64 - pure C++ version (64 bit) - without any asm code + */ + template + const char * UInt::LibTypeStr() + { + #ifdef TTMATH_PLATFORM32 + static const char info[] = "no_asm_32"; + #endif + + #ifdef TTMATH_PLATFORM64 + static const char info[] = "no_asm_64"; + #endif + + return info; + } + + + /*! + returning the currect type of the library + */ + template + LibTypeCode UInt::LibType() + { + #ifdef TTMATH_PLATFORM32 + LibTypeCode info = no_asm_32; + #endif + + #ifdef TTMATH_PLATFORM64 + LibTypeCode info = no_asm_64; + #endif + + return info; + } + + + /*! + this method adds two words together + returns carry + + this method is created only when TTMATH_NOASM macro is defined + */ + template + uint UInt::AddTwoWords(uint a, uint b, uint carry, uint * result) + { + uint temp; + + if( carry == 0 ) + { + temp = a + b; + + if( temp < a ) + carry = 1; + } + else + { + carry = 1; + temp = a + b + carry; + + if( temp > a ) // !(temp<=a) + carry = 0; + } + + *result = temp; + + return carry; + } + + + + /*! + this method adding ss2 to the this and adding carry if it's defined + (this = this + ss2 + c) + + c must be zero or one (might be a bigger value than 1) + function returns carry (1) (if it was) + */ + + template + uint UInt::Add(const UInt & ss2, uint c) + { + uint i; + + for(i=0 ; i + uint UInt::AddInt(uint value, uint index) + { + uint i, c; + + TTMATH_ASSERT( index < value_size ) + + + c = AddTwoWords(table[index], value, 0, &table[index]); + + for(i=index+1 ; i + uint UInt::AddTwoInts(uint x2, uint x1, uint index) + { + uint i, c; + + TTMATH_ASSERT( index < value_size - 1 ) + + + c = AddTwoWords(table[index], x1, 0, &table[index]); + c = AddTwoWords(table[index+1], x2, c, &table[index+1]); + + for(i=index+2 ; i + uint UInt::AddVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + { + uint i, c = 0; + + TTMATH_ASSERT( ss1_size >= ss2_size ) + + for(i=0 ; i + uint UInt::SubTwoWords(uint a, uint b, uint carry, uint * result) + { + if( carry == 0 ) + { + *result = a - b; + + if( a < b ) + carry = 1; + } + else + { + carry = 1; + *result = a - b - carry; + + if( a > b ) // !(a <= b ) + carry = 0; + } + + return carry; + } + + + + + /*! + this method's subtracting ss2 from the 'this' and subtracting + carry if it has been defined + (this = this - ss2 - c) + + c must be zero or one (might be a bigger value than 1) + function returns carry (1) (if it was) + */ + template + uint UInt::Sub(const UInt & ss2, uint c) + { + uint i; + + for(i=0 ; i + uint UInt::SubInt(uint value, uint index) + { + uint i, c; + + TTMATH_ASSERT( index < value_size ) + + + c = SubTwoWords(table[index], value, 0, &table[index]); + + for(i=index+1 ; i + uint UInt::SubVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + { + uint i, c = 0; + + TTMATH_ASSERT( ss1_size >= ss2_size ) + + for(i=0 ; i + uint UInt::Rcl2_one(uint c) + { + uint i, new_c; + + if( c != 0 ) + c = 1; + + for(i=0 ; i this -> return value + + the highest *bit* will be held the 'c' and + the state of one additional bit (on the right hand side) + will be returned + + for example: + let this is 000000010 + after Rcr2_one(1) there'll be 100000001 and Rcr2_one returns 0 + */ + template + uint UInt::Rcr2_one(uint c) + { + sint i; // signed i + uint new_c; + + if( c != 0 ) + c = TTMATH_UINT_HIGHEST_BIT; + + for(i=sint(value_size)-1 ; i>=0 ; --i) + { + new_c = (table[i] & 1) ? TTMATH_UINT_HIGHEST_BIT : 0; + table[i] = (table[i] >> 1) | c; + c = new_c; + } + + c = (c != 0)? 1 : 0; + + TTMATH_LOGC("UInt::Rcr2_one", c) + + return c; + } + + + + + /*! + this method moves all bits into the left hand side + return value <- this <- c + + the lowest *bits* will be held the 'c' and + the state of one additional bit (on the left hand side) + will be returned + + for example: + let this is 001010000 + after Rcl2(3, 1) there'll be 010000111 and Rcl2 returns 1 + */ + template + uint UInt::Rcl2(uint bits, uint c) + { + TTMATH_ASSERT( bits>0 && bits> move; + + for(i=0 ; i> move; + table[i] = (table[i] << bits) | c; + c = new_c; + } + + TTMATH_LOGC("UInt::Rcl2", (c & 1)) + + return (c & 1); + } + + + + + /*! + this method moves all bits into the right hand side + C -> this -> return value + + the highest *bits* will be held the 'c' and + the state of one additional bit (on the right hand side) + will be returned + + for example: + let this is 000000010 + after Rcr2(2, 1) there'll be 110000000 and Rcr2 returns 1 + */ + template + uint UInt::Rcr2(uint bits, uint c) + { + TTMATH_ASSERT( bits>0 && bits=0 ; --i) + { + new_c = table[i] << move; + table[i] = (table[i] >> bits) | c; + c = new_c; + } + + c = (c & TTMATH_UINT_HIGHEST_BIT) ? 1 : 0; + + TTMATH_LOGC("UInt::Rcr2", c) + + return c; + } + + + + + /*! + this method returns the number of the highest set bit in x + if the 'x' is zero this method returns '-1' + */ + template + sint UInt::FindLeadingBitInWord(uint x) + { + if( x == 0 ) + return -1; + + uint bit = TTMATH_BITS_PER_UINT - 1; + + while( (x & TTMATH_UINT_HIGHEST_BIT) == 0 ) + { + x = x << 1; + --bit; + } + + return bit; + } + + + + /*! + this method returns the number of the highest set bit in x + if the 'x' is zero this method returns '-1' + */ + template + sint UInt::FindLowestBitInWord(uint x) + { + if( x == 0 ) + return -1; + + uint bit = 0; + + while( (x & 1) == 0 ) + { + x = x >> 1; + ++bit; + } + + return bit; + } + + + + /*! + this method sets a special bit in the 'value' + and returns the last state of the bit (zero or one) + + bit is from <0,TTMATH_BITS_PER_UINT-1> + + e.g. + uint x = 100; + uint bit = SetBitInWord(x, 3); + now: x = 108 and bit = 0 + */ + template + uint UInt::SetBitInWord(uint & value, uint bit) + { + TTMATH_ASSERT( bit < TTMATH_BITS_PER_UINT ) + + uint mask = 1; + + if( bit > 0 ) + mask = mask << bit; + + uint last = value & mask; + value = value | mask; + + return (last != 0) ? 1 : 0; + } + + + + + + + /*! + * + * Multiplication + * + * + */ + + + /*! + multiplication: result_high:result_low = a * b + result_high - higher word of the result + result_low - lower word of the result + + this methos never returns a carry + this method is used in the second version of the multiplication algorithms + */ + template + void UInt::MulTwoWords(uint a, uint b, uint * result_high, uint * result_low) + { + #ifdef TTMATH_PLATFORM32 + + /* + on 32bit platforms we have defined 'unsigned long long int' type known as 'ulint' in ttmath namespace + this type has 64 bits, then we're using only one multiplication: 32bit * 32bit = 64bit + */ + + union uint_ + { + struct + { + uint low; // 32 bits + uint high; // 32 bits + } u_; + + ulint u; // 64 bits + } res; + + res.u = ulint(a) * ulint(b); // multiply two 32bit words, the result has 64 bits + + *result_high = res.u_.high; + *result_low = res.u_.low; + + #else + + /* + 64 bits platforms + + we don't have a native type which has 128 bits + then we're splitting 'a' and 'b' to 4 parts (high and low halves) + and using 4 multiplications (with additions and carry correctness) + */ + + uint_ a_; + uint_ b_; + uint_ res_high1, res_high2; + uint_ res_low1, res_low2; + + a_.u = a; + b_.u = b; + + /* + the multiplication is as follows (schoolbook algorithm with O(n^2) ): + + 32 bits 32 bits + + +--------------------------------+ + | a_.u_.high | a_.u_.low | + +--------------------------------+ + | b_.u_.high | b_.u_.low | + +--------------------------------+--------------------------------+ + | res_high1.u | res_low1.u | + +--------------------------------+--------------------------------+ + | res_high2.u | res_low2.u | + +--------------------------------+--------------------------------+ + + 64 bits 64 bits + */ + + + uint_ temp; + + res_low1.u = uint(b_.u_.low) * uint(a_.u_.low); + + temp.u = uint(res_low1.u_.high) + uint(b_.u_.low) * uint(a_.u_.high); + res_low1.u_.high = temp.u_.low; + res_high1.u_.low = temp.u_.high; + res_high1.u_.high = 0; + + res_low2.u_.low = 0; + temp.u = uint(b_.u_.high) * uint(a_.u_.low); + res_low2.u_.high = temp.u_.low; + + res_high2.u = uint(b_.u_.high) * uint(a_.u_.high) + uint(temp.u_.high); + + uint c = AddTwoWords(res_low1.u, res_low2.u, 0, &res_low2.u); + AddTwoWords(res_high1.u, res_high2.u, c, &res_high2.u); // there is no carry from here + + *result_high = res_high2.u; + *result_low = res_low2.u; + + #endif + } + + + + + /*! + * + * Division + * + * + */ + + + /*! + this method calculates 64bits word a:b / 32bits c (a higher, b lower word) + r = a:b / c and rest - remainder + + * + * WARNING: + * the c has to be suitably large for the result being keeped in one word, + * if c is equal zero there'll be a hardware interruption (0) + * and probably the end of your program + * + */ + template + void UInt::DivTwoWords(uint a, uint b, uint c, uint * r, uint * rest) + { + // (a < c ) for the result to be one word + TTMATH_ASSERT( c != 0 && a < c ) + + #ifdef TTMATH_PLATFORM32 + + union + { + struct + { + uint low; // 32 bits + uint high; // 32 bits + } u_; + + ulint u; // 64 bits + } ab; + + ab.u_.high = a; + ab.u_.low = b; + + *r = uint(ab.u / c); + *rest = uint(ab.u % c); + + #else + + uint_ c_; + c_.u = c; + + + if( a == 0 ) + { + *r = b / c; + *rest = b % c; + } + else + if( c_.u_.high == 0 ) + { + // higher half of 'c' is zero + // then higher half of 'a' is zero too (look at the asserts at the beginning - 'a' is smaller than 'c') + uint_ a_, b_, res_, temp1, temp2; + + a_.u = a; + b_.u = b; + + temp1.u_.high = a_.u_.low; + temp1.u_.low = b_.u_.high; + + res_.u_.high = (unsigned int)(temp1.u / c); + temp2.u_.high = (unsigned int)(temp1.u % c); + temp2.u_.low = b_.u_.low; + + res_.u_.low = (unsigned int)(temp2.u / c); + *rest = temp2.u % c; + + *r = res_.u; + } + else + { + return DivTwoWords2(a, b, c, r, rest); + } + + #endif + } + + +#ifdef TTMATH_PLATFORM64 + + + /*! + this method is available only on 64bit platforms + + the same algorithm like the third division algorithm in ttmathuint.h + but now with the radix=2^32 + */ + template + void UInt::DivTwoWords2(uint a, uint b, uint c, uint * r, uint * rest) + { + // a is not zero + // c_.u_.high is not zero + + uint_ a_, b_, c_, u_, q_; + unsigned int u3; // 32 bit + + a_.u = a; + b_.u = b; + c_.u = c; + + // normalizing + uint d = DivTwoWordsNormalize(a_, b_, c_); + + // loop from j=1 to j=0 + // the first step (for j=2) is skipped because our result is only in one word, + // (first 'q' were 0 and nothing would be changed) + u_.u_.high = a_.u_.high; + u_.u_.low = a_.u_.low; + u3 = b_.u_.high; + q_.u_.high = DivTwoWordsCalculate(u_, u3, c_); + MultiplySubtract(u_, u3, q_.u_.high, c_); + + u_.u_.high = u_.u_.low; + u_.u_.low = u3; + u3 = b_.u_.low; + q_.u_.low = DivTwoWordsCalculate(u_, u3, c_); + MultiplySubtract(u_, u3, q_.u_.low, c_); + + *r = q_.u; + + // unnormalizing for the remainder + u_.u_.high = u_.u_.low; + u_.u_.low = u3; + *rest = DivTwoWordsUnnormalize(u_.u, d); + } + + + + + template + uint UInt::DivTwoWordsNormalize(uint_ & a_, uint_ & b_, uint_ & c_) + { + uint d = 0; + + for( ; (c_.u & TTMATH_UINT_HIGHEST_BIT) == 0 ; ++d ) + { + c_.u = c_.u << 1; + + uint bc = b_.u & TTMATH_UINT_HIGHEST_BIT; // carry from 'b' + + b_.u = b_.u << 1; + a_.u = a_.u << 1; // carry bits from 'a' are simply skipped + + if( bc ) + a_.u = a_.u | 1; + } + + return d; + } + + + template + uint UInt::DivTwoWordsUnnormalize(uint u, uint d) + { + if( d == 0 ) + return u; + + u = u >> d; + + return u; + } + + + template + unsigned int UInt::DivTwoWordsCalculate(uint_ u_, unsigned int u3, uint_ v_) + { + bool next_test; + uint_ qp_, rp_, temp_; + + qp_.u = u_.u / uint(v_.u_.high); + rp_.u = u_.u % uint(v_.u_.high); + + TTMATH_ASSERT( qp_.u_.high==0 || qp_.u_.high==1 ) + + do + { + bool decrease = false; + + if( qp_.u_.high == 1 ) + decrease = true; + else + { + temp_.u_.high = rp_.u_.low; + temp_.u_.low = u3; + + if( qp_.u * uint(v_.u_.low) > temp_.u ) + decrease = true; + } + + next_test = false; + + if( decrease ) + { + --qp_.u; + rp_.u += v_.u_.high; + + if( rp_.u_.high == 0 ) + next_test = true; + } + } + while( next_test ); + + return qp_.u_.low; + } + + + template + void UInt::MultiplySubtract(uint_ & u_, unsigned int & u3, unsigned int & q, uint_ v_) + { + uint_ temp_; + + uint res_high; + uint res_low; + + MulTwoWords(v_.u, q, &res_high, &res_low); + + uint_ sub_res_high_; + uint_ sub_res_low_; + + temp_.u_.high = u_.u_.low; + temp_.u_.low = u3; + + uint c = SubTwoWords(temp_.u, res_low, 0, &sub_res_low_.u); + + temp_.u_.high = 0; + temp_.u_.low = u_.u_.high; + c = SubTwoWords(temp_.u, res_high, c, &sub_res_high_.u); + + if( c ) + { + --q; + + c = AddTwoWords(sub_res_low_.u, v_.u, 0, &sub_res_low_.u); + AddTwoWords(sub_res_high_.u, 0, c, &sub_res_high_.u); + } + + u_.u_.high = sub_res_high_.u_.low; + u_.u_.low = sub_res_low_.u_.high; + u3 = sub_res_low_.u_.low; + } + +#endif // #ifdef TTMATH_PLATFORM64 + + + +} //namespace + + +#endif //ifdef TTMATH_NOASM +#endif + + + + diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmathuint_x86.h b/3rd_party/ttmath-0.9.3/ttmath/ttmathuint_x86.h new file mode 100644 index 00000000..1dd087f5 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmathuint_x86.h @@ -0,0 +1,1602 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2009, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + + +#ifndef headerfilettmathuint_x86 +#define headerfilettmathuint_x86 + + +#ifndef TTMATH_NOASM +#ifdef TTMATH_PLATFORM32 + + +/*! + \file ttmathuint_x86.h + \brief template class UInt with assembler code for 32bit x86 processors + + this file is included at the end of ttmathuint.h +*/ + + + +/*! + \brief a namespace for the TTMath library +*/ +namespace ttmath +{ + + /*! + returning the string represents the currect type of the library + we have following types: + asm_vc_32 - with asm code designed for Microsoft Visual C++ (32 bits) + asm_gcc_32 - with asm code designed for GCC (32 bits) + asm_vc_64 - with asm for VC (64 bit) + asm_gcc_64 - with asm for GCC (64 bit) + no_asm_32 - pure C++ version (32 bit) - without any asm code + no_asm_64 - pure C++ version (64 bit) - without any asm code + */ + template + const char * UInt::LibTypeStr() + { + #ifndef __GNUC__ + static const char info[] = "asm_vc_32"; + #endif + + #ifdef __GNUC__ + static const char info[] = "asm_gcc_32"; + #endif + + return info; + } + + + /*! + returning the currect type of the library + */ + template + LibTypeCode UInt::LibType() + { + #ifndef __GNUC__ + LibTypeCode info = asm_vc_32; + #endif + + #ifdef __GNUC__ + LibTypeCode info = asm_gcc_32; + #endif + + return info; + } + + + + /*! + * + * basic mathematic functions + * + */ + + + /*! + adding ss2 to the this and adding carry if it's defined + (this = this + ss2 + c) + + c must be zero or one (might be a bigger value than 1) + function returns carry (1) (if it has been) + */ + template + uint UInt::Add(const UInt & ss2, uint c) + { + uint b = value_size; + uint * p1 = table; + uint * p2 = const_cast(ss2.table); + + // we don't have to use TTMATH_REFERENCE_ASSERT here + // this algorithm doesn't require it + + #ifndef __GNUC__ + + // this part might be compiled with for example visual c + + __asm + { + push eax + push ebx + push ecx + push edx + push esi + + mov ecx,[b] + + mov ebx,[p1] + mov esi,[p2] + + xor edx,edx // edx=0 + mov eax,[c] + neg eax // CF=1 if rax!=0 , CF=0 if rax==0 + + ttmath_loop: + mov eax,[esi+edx*4] + adc [ebx+edx*4],eax + + inc edx + dec ecx + jnz ttmath_loop + + adc ecx, ecx + mov [c], ecx + + pop esi + pop edx + pop ecx + pop ebx + pop eax + } + + + + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + // this part should be compiled with gcc + + __asm__ __volatile__( + + "xorl %%edx, %%edx \n" + "negl %%eax \n" // CF=1 if rax!=0 , CF=0 if rax==0 + + "1: \n" + "movl (%%esi,%%edx,4), %%eax \n" + "adcl %%eax, (%%ebx,%%edx,4) \n" + + "incl %%edx \n" + "decl %%ecx \n" + "jnz 1b \n" + + "adc %%ecx, %%ecx \n" + + : "=c" (c), "=a" (dummy), "=d" (dummy2) + : "0" (b), "1" (c), "b" (p1), "S" (p2) + : "cc", "memory" ); + #endif + + TTMATH_LOGC("UInt::Add", c) + + return c; + } + + + + /*! + adding one word (at a specific position) + and returning a carry (if it has been) + + e.g. + + if we've got (value_size=3): + table[0] = 10; + table[1] = 30; + table[2] = 5; + and we call: + AddInt(2,1) + then it'll be: + table[0] = 10; + table[1] = 30 + 2; + table[2] = 5; + + of course if there was a carry from table[2] it would be returned + */ + template + uint UInt::AddInt(uint value, uint index) + { + uint b = value_size; + uint * p1 = table; + uint c; + + TTMATH_ASSERT( index < value_size ) + + #ifndef __GNUC__ + + __asm + { + push eax + push ebx + push ecx + push edx + + mov ecx, [b] + sub ecx, [index] + + mov edx, [index] + mov ebx, [p1] + + mov eax, [value] + + ttmath_loop: + add [ebx+edx*4], eax + jnc ttmath_end + + mov eax, 1 + inc edx + dec ecx + jnz ttmath_loop + + ttmath_end: + setc al + movzx edx, al + mov [c], edx + + pop edx + pop ecx + pop ebx + pop eax + } + + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "subl %%edx, %%ecx \n" + + "1: \n" + "addl %%eax, (%%ebx,%%edx,4) \n" + "jnc 2f \n" + + "movl $1, %%eax \n" + "incl %%edx \n" + "decl %%ecx \n" + "jnz 1b \n" + + "2: \n" + "setc %%al \n" + "movzx %%al, %%edx \n" + + : "=d" (c), "=a" (dummy), "=c" (dummy2) + : "0" (index), "1" (value), "2" (b), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::AddInt", c) + + return c; + } + + + + + /*! + adding only two unsigned words to the existing value + and these words begin on the 'index' position + (it's used in the multiplication algorithm 2) + + index should be equal or smaller than value_size-2 (index <= value_size-2) + x1 - lower word, x2 - higher word + + for example if we've got value_size equal 4 and: + table[0] = 3 + table[1] = 4 + table[2] = 5 + table[3] = 6 + then let + x1 = 10 + x2 = 20 + and + index = 1 + + the result of this method will be: + table[0] = 3 + table[1] = 4 + x1 = 14 + table[2] = 5 + x2 = 25 + table[3] = 6 + + and no carry at the end of table[3] + + (of course if there was a carry in table[2](5+20) then + this carry would be passed to the table[3] etc.) + */ + template + uint UInt::AddTwoInts(uint x2, uint x1, uint index) + { + uint b = value_size; + uint * p1 = table; + uint c; + + TTMATH_ASSERT( index < value_size - 1 ) + + #ifndef __GNUC__ + __asm + { + push eax + push ebx + push ecx + push edx + + mov ecx, [b] + sub ecx, [index] + + mov ebx, [p1] + mov edx, [index] + + mov eax, [x1] + add [ebx+edx*4], eax + inc edx + dec ecx + + mov eax, [x2] + + ttmath_loop: + adc [ebx+edx*4], eax + jnc ttmath_end + + mov eax, 0 + inc edx + dec ecx + jnz ttmath_loop + + ttmath_end: + setc al + movzx edx, al + mov [c], edx + + pop edx + pop ecx + pop ebx + pop eax + + } + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "subl %%edx, %%ecx \n" + + "addl %%esi, (%%ebx,%%edx,4) \n" + "incl %%edx \n" + "decl %%ecx \n" + + "1: \n" + "adcl %%eax, (%%ebx,%%edx,4) \n" + "jnc 2f \n" + + "mov $0, %%eax \n" + "incl %%edx \n" + "decl %%ecx \n" + "jnz 1b \n" + + "2: \n" + "setc %%al \n" + "movzx %%al, %%eax \n" + + : "=a" (c), "=c" (dummy), "=d" (dummy2) + : "0" (x2), "1" (b), "2" (index), "b" (p1), "S" (x1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::AddTwoInts", c) + + return c; + } + + + + /*! + this static method addes one vector to the other + 'ss1' is larger in size or equal to 'ss2' + + ss1 points to the first (larger) vector + ss2 points to the second vector + ss1_size - size of the ss1 (and size of the result too) + ss2_size - size of the ss2 + result - is the result vector (which has size the same as ss1: ss1_size) + + Example: ss1_size is 5, ss2_size is 3 + ss1: ss2: result (output): + 5 1 5+1 + 4 3 4+3 + 2 7 2+7 + 6 6 + 9 9 + of course the carry is propagated and will be returned from the last item + (this method is used by the Karatsuba multiplication algorithm) + */ + template + uint UInt::AddVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + { + TTMATH_ASSERT( ss1_size >= ss2_size ) + + uint rest = ss1_size - ss2_size; + uint c; + + #ifndef __GNUC__ + + // this part might be compiled with for example visual c + __asm + { + pushad + + mov ecx, [ss2_size] + xor edx, edx // edx = 0, cf = 0 + + mov esi, [ss1] + mov ebx, [ss2] + mov edi, [result] + + ttmath_loop: + mov eax, [esi+edx*4] + adc eax, [ebx+edx*4] + mov [edi+edx*4], eax + + inc edx + dec ecx + jnz ttmath_loop + + adc ecx, ecx // ecx has the cf state + + mov ebx, [rest] + or ebx, ebx + jz ttmath_end + + xor ebx, ebx // ebx = 0 + neg ecx // setting cf from ecx + mov ecx, [rest] // ecx is != 0 + + ttmath_loop2: + mov eax, [esi+edx*4] + adc eax, ebx + mov [edi+edx*4], eax + + inc edx + dec ecx + jnz ttmath_loop2 + + adc ecx, ecx + + ttmath_end: + mov [c], ecx + + popad + } + + #endif + + + #ifdef __GNUC__ + + // this part should be compiled with gcc + uint dummy1, dummy2, dummy3; + + __asm__ __volatile__( + "push %%edx \n" + "xor %%edx, %%edx \n" // edx = 0, cf = 0 + "1: \n" + "mov (%%esi,%%edx,4), %%eax \n" + "adc (%%ebx,%%edx,4), %%eax \n" + "mov %%eax, (%%edi,%%edx,4) \n" + + "inc %%edx \n" + "dec %%ecx \n" + "jnz 1b \n" + + "adc %%ecx, %%ecx \n" // ecx has the cf state + "pop %%eax \n" // eax = rest + + "or %%eax, %%eax \n" + "jz 3f \n" + + "xor %%ebx, %%ebx \n" // ebx = 0 + "neg %%ecx \n" // setting cf from ecx + "mov %%eax, %%ecx \n" // ecx=rest and is != 0 + "2: \n" + "mov (%%esi, %%edx, 4), %%eax \n" + "adc %%ebx, %%eax \n" + "mov %%eax, (%%edi, %%edx, 4) \n" + + "inc %%edx \n" + "dec %%ecx \n" + "jnz 2b \n" + + "adc %%ecx, %%ecx \n" + "3: \n" + + : "=a" (dummy1), "=b" (dummy2), "=c" (c), "=d" (dummy3) + : "1" (ss2), "2" (ss2_size), "3" (rest), "S" (ss1), "D" (result) + : "cc", "memory" ); + + #endif + + TTMATH_VECTOR_LOGC("UInt::AddVector", c, result, ss1_size) + + return c; + } + + + /*! + subtracting ss2 from the 'this' and subtracting + carry if it has been defined + (this = this - ss2 - c) + + c must be zero or one (might be a bigger value than 1) + function returns carry (1) (if it has been) + */ + template + uint UInt::Sub(const UInt & ss2, uint c) + { + uint b = value_size; + uint * p1 = table; + uint * p2 = const_cast(ss2.table); + + // we don't have to use TTMATH_REFERENCE_ASSERT here + // this algorithm doesn't require it + + #ifndef __GNUC__ + + __asm + { + push eax + push ebx + push ecx + push edx + push esi + + mov ecx,[b] + + mov ebx,[p1] + mov esi,[p2] + + xor edx,edx // edx=0 + mov eax,[c] + neg eax // CF=1 if rax!=0 , CF=0 if rax==0 + + ttmath_loop: + mov eax,[esi+edx*4] + sbb [ebx+edx*4],eax + + inc edx + dec ecx + jnz ttmath_loop + + adc ecx, ecx + mov [c], ecx + + pop esi + pop edx + pop ecx + pop ebx + pop eax + } + + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "xorl %%edx, %%edx \n" + "negl %%eax \n" // CF=1 if rax!=0 , CF=0 if rax==0 + + "1: \n" + "movl (%%esi,%%edx,4), %%eax \n" + "sbbl %%eax, (%%ebx,%%edx,4) \n" + + "incl %%edx \n" + "decl %%ecx \n" + "jnz 1b \n" + + "adc %%ecx, %%ecx \n" + + : "=c" (c), "=a" (dummy), "=d" (dummy2) + : "0" (b), "1" (c), "b" (p1), "S" (p2) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Sub", c) + + return c; + } + + + + + /*! + this method subtracts one word (at a specific position) + and returns a carry (if it was) + + e.g. + + if we've got (value_size=3): + table[0] = 10; + table[1] = 30; + table[2] = 5; + and we call: + SubInt(2,1) + then it'll be: + table[0] = 10; + table[1] = 30 - 2; + table[2] = 5; + + of course if there was a carry from table[2] it would be returned + */ + template + uint UInt::SubInt(uint value, uint index) + { + uint b = value_size; + uint * p1 = table; + uint c; + + TTMATH_ASSERT( index < value_size ) + + #ifndef __GNUC__ + + __asm + { + push eax + push ebx + push ecx + push edx + + mov ecx, [b] + sub ecx, [index] + + mov edx, [index] + mov ebx, [p1] + + mov eax, [value] + + ttmath_loop: + sub [ebx+edx*4], eax + jnc ttmath_end + + mov eax, 1 + inc edx + dec ecx + jnz ttmath_loop + + ttmath_end: + setc al + movzx edx, al + mov [c], edx + + pop edx + pop ecx + pop ebx + pop eax + } + + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "subl %%edx, %%ecx \n" + + "1: \n" + "subl %%eax, (%%ebx,%%edx,4) \n" + "jnc 2f \n" + + "movl $1, %%eax \n" + "incl %%edx \n" + "decl %%ecx \n" + "jnz 1b \n" + + "2: \n" + "setc %%al \n" + "movzx %%al, %%edx \n" + + : "=d" (c), "=a" (dummy), "=c" (dummy2) + : "0" (index), "1" (value), "2" (b), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::SubInt", c) + + return c; + } + + + + /*! + this static method subtractes one vector from the other + 'ss1' is larger in size or equal to 'ss2' + + ss1 points to the first (larger) vector + ss2 points to the second vector + ss1_size - size of the ss1 (and size of the result too) + ss2_size - size of the ss2 + result - is the result vector (which has size the same as ss1: ss1_size) + + Example: ss1_size is 5, ss2_size is 3 + ss1: ss2: result (output): + 5 1 5-1 + 4 3 4-3 + 2 7 2-7 + 6 6-1 (the borrow from previous item) + 9 9 + return (carry): 0 + of course the carry (borrow) is propagated and will be returned from the last item + (this method is used by the Karatsuba multiplication algorithm) + */ + template + uint UInt::SubVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + { + TTMATH_ASSERT( ss1_size >= ss2_size ) + + uint rest = ss1_size - ss2_size; + uint c; + + #ifndef __GNUC__ + + // this part might be compiled with for example visual c + + /* + the asm code is nearly the same as in AddVector + only two instructions 'adc' are changed to 'sbb' + */ + __asm + { + pushad + + mov ecx, [ss2_size] + xor edx, edx // edx = 0, cf = 0 + + mov esi, [ss1] + mov ebx, [ss2] + mov edi, [result] + + ttmath_loop: + mov eax, [esi+edx*4] + sbb eax, [ebx+edx*4] + mov [edi+edx*4], eax + + inc edx + dec ecx + jnz ttmath_loop + + adc ecx, ecx // ecx has the cf state + + mov ebx, [rest] + or ebx, ebx + jz ttmath_end + + xor ebx, ebx // ebx = 0 + neg ecx // setting cf from ecx + mov ecx, [rest] // ecx is != 0 + + ttmath_loop2: + mov eax, [esi+edx*4] + sbb eax, ebx + mov [edi+edx*4], eax + + inc edx + dec ecx + jnz ttmath_loop2 + + adc ecx, ecx + + ttmath_end: + mov [c], ecx + + popad + } + + #endif + + + #ifdef __GNUC__ + + // this part should be compiled with gcc + uint dummy1, dummy2, dummy3; + + __asm__ __volatile__( + "push %%edx \n" + "xor %%edx, %%edx \n" // edx = 0, cf = 0 + "1: \n" + "mov (%%esi,%%edx,4), %%eax \n" + "sbb (%%ebx,%%edx,4), %%eax \n" + "mov %%eax, (%%edi,%%edx,4) \n" + + "inc %%edx \n" + "dec %%ecx \n" + "jnz 1b \n" + + "adc %%ecx, %%ecx \n" // ecx has the cf state + "pop %%eax \n" // eax = rest + + "or %%eax, %%eax \n" + "jz 3f \n" + + "xor %%ebx, %%ebx \n" // ebx = 0 + "neg %%ecx \n" // setting cf from ecx + "mov %%eax, %%ecx \n" // ecx=rest and is != 0 + "2: \n" + "mov (%%esi, %%edx, 4), %%eax \n" + "sbb %%ebx, %%eax \n" + "mov %%eax, (%%edi, %%edx, 4) \n" + + "inc %%edx \n" + "dec %%ecx \n" + "jnz 2b \n" + + "adc %%ecx, %%ecx \n" + "3: \n" + + : "=a" (dummy1), "=b" (dummy2), "=c" (c), "=d" (dummy3) + : "1" (ss2), "2" (ss2_size), "3" (rest), "S" (ss1), "D" (result) + : "cc", "memory" ); + + #endif + + TTMATH_VECTOR_LOGC("UInt::SubVector", c, result, ss1_size) + + return c; + } + + + + /*! + this method moves all bits into the left hand side + return value <- this <- c + + the lowest *bit* will be held the 'c' and + the state of one additional bit (on the left hand side) + will be returned + + for example: + let this is 001010000 + after Rcl2_one(1) there'll be 010100001 and Rcl2_one returns 0 + */ + template + uint UInt::Rcl2_one(uint c) + { + uint b = value_size; + uint * p1 = table; + + #ifndef __GNUC__ + __asm + { + push ebx + push ecx + push edx + + mov ebx, [p1] + xor edx, edx + mov ecx, [c] + neg ecx + mov ecx, [b] + + ttmath_loop: + rcl dword ptr [ebx+edx*4], 1 + + inc edx + dec ecx + jnz ttmath_loop + + adc ecx, ecx + mov [c], ecx + + pop edx + pop ecx + pop ebx + } + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "xorl %%edx, %%edx \n" // edx=0 + "negl %%eax \n" // CF=1 if eax!=0 , CF=0 if eax==0 + + "1: \n" + "rcll $1, (%%ebx, %%edx, 4) \n" + + "incl %%edx \n" + "decl %%ecx \n" + "jnz 1b \n" + + "adcl %%ecx, %%ecx \n" + + : "=c" (c), "=a" (dummy), "=d" (dummy2) + : "0" (b), "1" (c), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Rcl2_one", c) + + return c; + } + + + + /*! + this method moves all bits into the right hand side + c -> this -> return value + + the highest *bit* will be held the 'c' and + the state of one additional bit (on the right hand side) + will be returned + + for example: + let this is 000000010 + after Rcr2_one(1) there'll be 100000001 and Rcr2_one returns 0 + */ + template + uint UInt::Rcr2_one(uint c) + { + uint b = value_size; + uint * p1 = table; + + #ifndef __GNUC__ + __asm + { + push ebx + push ecx + + mov ebx, [p1] + mov ecx, [c] + neg ecx + mov ecx, [b] + + ttmath_loop: + rcr dword ptr [ebx+ecx*4-4], 1 + + dec ecx + jnz ttmath_loop + + adc ecx, ecx + mov [c], ecx + + pop ecx + pop ebx + } + #endif + + + #ifdef __GNUC__ + uint dummy; + + __asm__ __volatile__( + + "negl %%eax \n" // CF=1 if eax!=0 , CF=0 if eax==0 + + "1: \n" + "rcrl $1, -4(%%ebx, %%ecx, 4) \n" + + "decl %%ecx \n" + "jnz 1b \n" + + "adcl %%ecx, %%ecx \n" + + : "=c" (c), "=a" (dummy) + : "0" (b), "1" (c), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Rcr2_one", c) + + return c; + } + + + +#ifdef _MSC_VER +#pragma warning (disable : 4731) +//warning C4731: frame pointer register 'ebp' modified by inline assembly code +#endif + + + + /*! + this method moves all bits into the left hand side + return value <- this <- c + + the lowest *bits* will be held the 'c' and + the state of one additional bit (on the left hand side) + will be returned + + for example: + let this is 001010000 + after Rcl2(3, 1) there'll be 010000111 and Rcl2 returns 1 + */ + template + uint UInt::Rcl2(uint bits, uint c) + { + TTMATH_ASSERT( bits>0 && bits edx -> cf) (cl times) + "movl %%edx, %%ebp \n" // ebp = edx = mask + "movl %%esi, %%ecx \n" + + "xorl %%edx, %%edx \n" + "movl %%edx, %%esi \n" + "orl %%eax, %%eax \n" + "cmovnz %%ebp, %%esi \n" // if(c) esi=mask else esi=0 + + "1: \n" + "roll %%cl, (%%ebx,%%edx,4) \n" + + "movl (%%ebx,%%edx,4), %%eax \n" + "andl %%ebp, %%eax \n" + "xorl %%eax, (%%ebx,%%edx,4) \n" + "orl %%esi, (%%ebx,%%edx,4) \n" + "movl %%eax, %%esi \n" + + "incl %%edx \n" + "decl %%edi \n" + "jnz 1b \n" + + "and $1, %%eax \n" + + "pop %%ebp \n" + + : "=a" (c), "=D" (dummy), "=S" (dummy2), "=d" (dummy3) + : "0" (c), "1" (b), "b" (p1), "c" (bits) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Rcl2", c) + + return c; + } + + + + + /*! + this method moves all bits into the right hand side + C -> this -> return value + + the highest *bits* will be held the 'c' and + the state of one additional bit (on the right hand side) + will be returned + + for example: + let this is 000000010 + after Rcr2(2, 1) there'll be 110000000 and Rcr2 returns 1 + */ + template + uint UInt::Rcr2(uint bits, uint c) + { + TTMATH_ASSERT( bits>0 && bits + sint UInt::FindLeadingBitInWord(uint x) + { + sint result; + + #ifndef __GNUC__ + __asm + { + push eax + push edx + + mov edx,-1 + bsr eax,[x] + cmovz eax,edx + mov [result], eax + + pop edx + pop eax + } + #endif + + + #ifdef __GNUC__ + uint dummy; + + __asm__ ( + + "movl $-1, %1 \n" + "bsrl %2, %0 \n" + "cmovz %1, %0 \n" + + : "=r" (result), "=&r" (dummy) + : "r" (x) + : "cc" ); + + #endif + + return result; + } + + + + /* + this method returns the number of the smallest set bit in one 32-bit word + if the 'x' is zero this method returns '-1' + */ + template + sint UInt::FindLowestBitInWord(uint x) + { + sint result; + + #ifndef __GNUC__ + __asm + { + push eax + push edx + + mov edx,-1 + bsf eax,[x] + cmovz eax,edx + mov [result], eax + + pop edx + pop eax + } + #endif + + + #ifdef __GNUC__ + uint dummy; + + __asm__ ( + + "movl $-1, %1 \n" + "bsfl %2, %0 \n" + "cmovz %1, %0 \n" + + : "=r" (result), "=&r" (dummy) + : "r" (x) + : "cc" ); + + #endif + + return result; + } + + + + /*! + this method sets a special bit in the 'value' + and returns the last state of the bit (zero or one) + + bit is from <0,31> + e.g. + uint x = 100; + uint bit = SetBitInWord(x, 3); + now: x = 108 and bit = 0 + */ + template + uint UInt::SetBitInWord(uint & value, uint bit) + { + TTMATH_ASSERT( bit < TTMATH_BITS_PER_UINT ) + + uint old_bit; + uint v = value; + + #ifndef __GNUC__ + __asm + { + push ebx + push eax + + mov eax, [v] + mov ebx, [bit] + bts eax, ebx + mov [v], eax + + setc bl + movzx ebx, bl + mov [old_bit], ebx + + pop eax + pop ebx + } + #endif + + + #ifdef __GNUC__ + __asm__ ( + + "btsl %%ebx, %%eax \n" + "setc %%bl \n" + "movzx %%bl, %%ebx \n" + + : "=a" (v), "=b" (old_bit) + : "0" (v), "1" (bit) + : "cc" ); + + #endif + + value = v; + + return old_bit; + } + + + + + /*! + multiplication: result_high:result_low = a * b + result_high - higher word of the result + result_low - lower word of the result + + this methos never returns a carry + this method is used in the second version of the multiplication algorithms + */ + template + void UInt::MulTwoWords(uint a, uint b, uint * result_high, uint * result_low) + { + /* + we must use these temporary variables in order to inform the compilator + that value pointed with result1 and result2 has changed + + this has no effect in visual studio but it's useful when + using gcc and options like -Ox + */ + uint result1_; + uint result2_; + + #ifndef __GNUC__ + + __asm + { + push eax + push edx + + mov eax, [a] + mul dword ptr [b] + + mov [result2_], edx + mov [result1_], eax + + pop edx + pop eax + } + + #endif + + + #ifdef __GNUC__ + + __asm__ ( + + "mull %%edx \n" + + : "=a" (result1_), "=d" (result2_) + : "0" (a), "1" (b) + : "cc" ); + + #endif + + + *result_low = result1_; + *result_high = result2_; + } + + + + + + /*! + * + * Division + * + * + */ + + + + + /*! + this method calculates 64bits word a:b / 32bits c (a higher, b lower word) + r = a:b / c and rest - remainder + + * + * WARNING: + * if r (one word) is too small for the result or c is equal zero + * there'll be a hardware interruption (0) + * and probably the end of your program + * + */ + template + void UInt::DivTwoWords(uint a, uint b, uint c, uint * r, uint * rest) + { + uint r_; + uint rest_; + /* + these variables have similar meaning like those in + the multiplication algorithm MulTwoWords + */ + + TTMATH_ASSERT( c != 0 ) + + #ifndef __GNUC__ + __asm + { + push eax + push edx + + mov edx, [a] + mov eax, [b] + div dword ptr [c] + + mov [r_], eax + mov [rest_], edx + + pop edx + pop eax + } + #endif + + + #ifdef __GNUC__ + + __asm__ ( + + "divl %%ecx \n" + + : "=a" (r_), "=d" (rest_) + : "0" (b), "1" (a), "c" (c) + : "cc" ); + + #endif + + + *r = r_; + *rest = rest_; + + } + + + +} //namespace + + + +#endif //ifdef TTMATH_PLATFORM32 +#endif //ifndef TTMATH_NOASM +#endif diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmathuint_x86_64.h b/3rd_party/ttmath-0.9.3/ttmath/ttmathuint_x86_64.h new file mode 100644 index 00000000..188fc5e7 --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmathuint_x86_64.h @@ -0,0 +1,1146 @@ +/* + * This file is a part of TTMath Bignum Library + * and is distributed under the (new) BSD licence. + * Author: Tomasz Sowa + */ + +/* + * Copyright (c) 2006-2010, Tomasz Sowa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name Tomasz Sowa nor the names of contributors to this + * project may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef headerfilettmathuint_x86_64 +#define headerfilettmathuint_x86_64 + + +#ifndef TTMATH_NOASM +#ifdef TTMATH_PLATFORM64 + + +/*! + \file ttmathuint_x86_64.h + \brief template class UInt with assembler code for 64bit x86_64 processors + + this file is included at the end of ttmathuint.h +*/ + +#ifndef __GNUC__ +#include +#endif + + +namespace ttmath +{ + + #ifndef __GNUC__ + + extern "C" + { + uint __fastcall ttmath_adc_x64(uint* p1, const uint* p2, uint nSize, uint c); + uint __fastcall ttmath_addindexed_x64(uint* p1, uint nSize, uint nPos, uint nValue); + uint __fastcall ttmath_addindexed2_x64(uint* p1, uint nSize, uint nPos, uint nValue1, uint nValue2); + uint __fastcall ttmath_addvector_x64(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result); + uint __fastcall ttmath_sbb_x64(uint* p1, const uint* p2, uint nSize, uint c); + uint __fastcall ttmath_subindexed_x64(uint* p1, uint nSize, uint nPos, uint nValue); + uint __fastcall ttmath_subvector_x64(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result); + uint __fastcall ttmath_rcl_x64(uint* p1, uint nSize, uint nLowestBit); + uint __fastcall ttmath_rcr_x64(uint* p1, uint nSize, uint nLowestBit); + uint __fastcall ttmath_div_x64(uint* pnValHi, uint* pnValLo, uint nDiv); + uint __fastcall ttmath_rcl2_x64(uint* p1, uint nSize, uint nBits, uint c); + uint __fastcall ttmath_rcr2_x64(uint* p1, uint nSize, uint nBits, uint c); + }; + #endif + + + /*! + returning the string represents the currect type of the library + we have following types: + asm_vc_32 - with asm code designed for Microsoft Visual C++ (32 bits) + asm_gcc_32 - with asm code designed for GCC (32 bits) + asm_vc_64 - with asm for VC (64 bit) + asm_gcc_64 - with asm for GCC (64 bit) + no_asm_32 - pure C++ version (32 bit) - without any asm code + no_asm_64 - pure C++ version (64 bit) - without any asm code + */ + template + const char * UInt::LibTypeStr() + { + #ifndef __GNUC__ + static const char info[] = "asm_vc_64"; + #endif + + #ifdef __GNUC__ + static const char info[] = "asm_gcc_64"; + #endif + + return info; + } + + + /*! + returning the currect type of the library + */ + template + LibTypeCode UInt::LibType() + { + #ifndef __GNUC__ + LibTypeCode info = asm_vc_64; + #endif + + #ifdef __GNUC__ + LibTypeCode info = asm_gcc_64; + #endif + + return info; + } + + + /*! + * + * basic mathematic functions + * + */ + + + + /*! + this method adding ss2 to the this and adding carry if it's defined + (this = this + ss2 + c) + + ***this method is created only on a 64bit platform*** + + c must be zero or one (might be a bigger value than 1) + function returns carry (1) (if it was) + */ + template + uint UInt::Add(const UInt & ss2, uint c) + { + uint b = value_size; + uint * p1 = table; + const uint * p2 = ss2.table; + + // we don't have to use TTMATH_REFERENCE_ASSERT here + // this algorithm doesn't require it + + #ifndef __GNUC__ + c = ttmath_adc_x64(p1,p2,b,c); + #endif + + #ifdef __GNUC__ + uint dummy, dummy2; + + /* + this part should be compiled with gcc + */ + __asm__ __volatile__( + + "xorq %%rdx, %%rdx \n" + "negq %%rax \n" // CF=1 if rax!=0 , CF=0 if rax==0 + + "1: \n" + "movq (%%rsi,%%rdx,8), %%rax \n" + "adcq %%rax, (%%rbx,%%rdx,8) \n" + + "incq %%rdx \n" + "decq %%rcx \n" + "jnz 1b \n" + + "adcq %%rcx, %%rcx \n" + + : "=c" (c), "=a" (dummy), "=d" (dummy2) + : "0" (b), "1" (c), "b" (p1), "S" (p2) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Add", c) + + return c; + } + + + + /*! + this method adds one word (at a specific position) + and returns a carry (if it was) + + ***this method is created only on a 64bit platform*** + + + if we've got (value_size=3): + table[0] = 10; + table[1] = 30; + table[2] = 5; + and we call: + AddInt(2,1) + then it'll be: + table[0] = 10; + table[1] = 30 + 2; + table[2] = 5; + + of course if there was a carry from table[2] it would be returned + */ + template + uint UInt::AddInt(uint value, uint index) + { + uint b = value_size; + uint * p1 = table; + uint c; + + TTMATH_ASSERT( index < value_size ) + + #ifndef __GNUC__ + c = ttmath_addindexed_x64(p1,b,index,value); + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "subq %%rdx, %%rcx \n" + + "1: \n" + "addq %%rax, (%%rbx,%%rdx,8) \n" + "jnc 2f \n" + + "movq $1, %%rax \n" + "incq %%rdx \n" + "decq %%rcx \n" + "jnz 1b \n" + + "2: \n" + "setc %%al \n" + "movzx %%al, %%rdx \n" + + : "=d" (c), "=a" (dummy), "=c" (dummy2) + : "0" (index), "1" (value), "2" (b), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::AddInt", c) + + return c; + } + + + + /*! + this method adds only two unsigned words to the existing value + and these words begin on the 'index' position + (it's used in the multiplication algorithm 2) + + ***this method is created only on a 64bit platform*** + + index should be equal or smaller than value_size-2 (index <= value_size-2) + x1 - lower word, x2 - higher word + + for example if we've got value_size equal 4 and: + table[0] = 3 + table[1] = 4 + table[2] = 5 + table[3] = 6 + then let + x1 = 10 + x2 = 20 + and + index = 1 + + the result of this method will be: + table[0] = 3 + table[1] = 4 + x1 = 14 + table[2] = 5 + x2 = 25 + table[3] = 6 + + and no carry at the end of table[3] + + (of course if there was a carry in table[2](5+20) then + this carry would be passed to the table[3] etc.) + */ + template + uint UInt::AddTwoInts(uint x2, uint x1, uint index) + { + uint b = value_size; + uint * p1 = table; + uint c; + + TTMATH_ASSERT( index < value_size - 1 ) + + #ifndef __GNUC__ + c = ttmath_addindexed2_x64(p1,b,index,x1,x2); + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "subq %%rdx, %%rcx \n" + + "addq %%rsi, (%%rbx,%%rdx,8) \n" + "incq %%rdx \n" + "decq %%rcx \n" + + "1: \n" + "adcq %%rax, (%%rbx,%%rdx,8) \n" + "jnc 2f \n" + + "mov $0, %%rax \n" + "incq %%rdx \n" + "decq %%rcx \n" + "jnz 1b \n" + + "2: \n" + "setc %%al \n" + "movzx %%al, %%rax \n" + + : "=a" (c), "=c" (dummy), "=d" (dummy2) + : "0" (x2), "1" (b), "2" (index), "b" (p1), "S" (x1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::AddTwoInts", c) + + return c; + } + + + + /*! + this static method addes one vector to the other + 'ss1' is larger in size or equal to 'ss2' + + ss1 points to the first (larger) vector + ss2 points to the second vector + ss1_size - size of the ss1 (and size of the result too) + ss2_size - size of the ss2 + result - is the result vector (which has size the same as ss1: ss1_size) + + Example: ss1_size is 5, ss2_size is 3 + ss1: ss2: result (output): + 5 1 5+1 + 4 3 4+3 + 2 7 2+7 + 6 6 + 9 9 + of course the carry is propagated and will be returned from the last item + (this method is used by the Karatsuba multiplication algorithm) + */ + template + uint UInt::AddVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + { + TTMATH_ASSERT( ss1_size >= ss2_size ) + + uint c; + + #ifndef __GNUC__ + c = ttmath_addvector_x64(ss1, ss2, ss1_size, ss2_size, result); + #endif + + + #ifdef __GNUC__ + uint dummy1, dummy2, dummy3; + uint rest = ss1_size - ss2_size; + + // this part should be compiled with gcc + + __asm__ __volatile__( + "mov %%rdx, %%r8 \n" + "xor %%rdx, %%rdx \n" // rdx = 0, cf = 0 + "1: \n" + "mov (%%rsi,%%rdx,8), %%rax \n" + "adc (%%rbx,%%rdx,8), %%rax \n" + "mov %%rax, (%%rdi,%%rdx,8) \n" + + "inc %%rdx \n" + "dec %%rcx \n" + "jnz 1b \n" + + "adc %%rcx, %%rcx \n" // rcx has the cf state + + "or %%r8, %%r8 \n" + "jz 3f \n" + + "xor %%rbx, %%rbx \n" // ebx = 0 + "neg %%rcx \n" // setting cf from rcx + "mov %%r8, %%rcx \n" // rcx=rest and is != 0 + "2: \n" + "mov (%%rsi, %%rdx, 8), %%rax \n" + "adc %%rbx, %%rax \n" + "mov %%rax, (%%rdi, %%rdx, 8) \n" + + "inc %%rdx \n" + "dec %%rcx \n" + "jnz 2b \n" + + "adc %%rcx, %%rcx \n" + "3: \n" + + : "=a" (dummy1), "=b" (dummy2), "=c" (c), "=d" (dummy3) + : "1" (ss2), "2" (ss2_size), "3" (rest), "S" (ss1), "D" (result) + : "%r8", "cc", "memory" ); + + #endif + + TTMATH_VECTOR_LOGC("UInt::AddVector", c, result, ss1_size) + + return c; + } + + + + /*! + this method's subtracting ss2 from the 'this' and subtracting + carry if it has been defined + (this = this - ss2 - c) + + ***this method is created only on a 64bit platform*** + + c must be zero or one (might be a bigger value than 1) + function returns carry (1) (if it was) + */ + template + uint UInt::Sub(const UInt & ss2, uint c) + { + uint b = value_size; + uint * p1 = table; + const uint * p2 = ss2.table; + + // we don't have to use TTMATH_REFERENCE_ASSERT here + // this algorithm doesn't require it + + #ifndef __GNUC__ + c = ttmath_sbb_x64(p1,p2,b,c); + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "xorq %%rdx, %%rdx \n" + "negq %%rax \n" // CF=1 if rax!=0 , CF=0 if rax==0 + + "1: \n" + "movq (%%rsi,%%rdx,8), %%rax \n" + "sbbq %%rax, (%%rbx,%%rdx,8) \n" + + "incq %%rdx \n" + "decq %%rcx \n" + "jnz 1b \n" + + "adcq %%rcx, %%rcx \n" + + : "=c" (c), "=a" (dummy), "=d" (dummy2) + : "0" (b), "1" (c), "b" (p1), "S" (p2) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Sub", c) + + return c; + } + + + + /*! + this method subtracts one word (at a specific position) + and returns a carry (if it was) + + ***this method is created only on a 64bit platform*** + + if we've got (value_size=3): + table[0] = 10; + table[1] = 30; + table[2] = 5; + and we call: + SubInt(2,1) + then it'll be: + table[0] = 10; + table[1] = 30 - 2; + table[2] = 5; + + of course if there was a carry from table[2] it would be returned + */ + template + uint UInt::SubInt(uint value, uint index) + { + uint b = value_size; + uint * p1 = table; + uint c; + + TTMATH_ASSERT( index < value_size ) + + #ifndef __GNUC__ + c = ttmath_subindexed_x64(p1,b,index,value); + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "subq %%rdx, %%rcx \n" + + "1: \n" + "subq %%rax, (%%rbx,%%rdx,8) \n" + "jnc 2f \n" + + "movq $1, %%rax \n" + "incq %%rdx \n" + "decq %%rcx \n" + "jnz 1b \n" + + "2: \n" + "setc %%al \n" + "movzx %%al, %%rdx \n" + + : "=d" (c), "=a" (dummy), "=c" (dummy2) + : "0" (index), "1" (value), "2" (b), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::SubInt", c) + + return c; + } + + + /*! + this static method subtractes one vector from the other + 'ss1' is larger in size or equal to 'ss2' + + ss1 points to the first (larger) vector + ss2 points to the second vector + ss1_size - size of the ss1 (and size of the result too) + ss2_size - size of the ss2 + result - is the result vector (which has size the same as ss1: ss1_size) + + Example: ss1_size is 5, ss2_size is 3 + ss1: ss2: result (output): + 5 1 5-1 + 4 3 4-3 + 2 7 2-7 + 6 6-1 (the borrow from previous item) + 9 9 + return (carry): 0 + of course the carry (borrow) is propagated and will be returned from the last item + (this method is used by the Karatsuba multiplication algorithm) + */ + template + uint UInt::SubVector(const uint * ss1, const uint * ss2, uint ss1_size, uint ss2_size, uint * result) + { + TTMATH_ASSERT( ss1_size >= ss2_size ) + + uint c; + + #ifndef __GNUC__ + c = ttmath_subvector_x64(ss1, ss2, ss1_size, ss2_size, result); + #endif + + + #ifdef __GNUC__ + + // the asm code is nearly the same as in AddVector + // only two instructions 'adc' are changed to 'sbb' + + uint dummy1, dummy2, dummy3; + uint rest = ss1_size - ss2_size; + + __asm__ __volatile__( + "mov %%rdx, %%r8 \n" + "xor %%rdx, %%rdx \n" // rdx = 0, cf = 0 + "1: \n" + "mov (%%rsi,%%rdx,8), %%rax \n" + "sbb (%%rbx,%%rdx,8), %%rax \n" + "mov %%rax, (%%rdi,%%rdx,8) \n" + + "inc %%rdx \n" + "dec %%rcx \n" + "jnz 1b \n" + + "adc %%rcx, %%rcx \n" // rcx has the cf state + + "or %%r8, %%r8 \n" + "jz 3f \n" + + "xor %%rbx, %%rbx \n" // ebx = 0 + "neg %%rcx \n" // setting cf from rcx + "mov %%r8, %%rcx \n" // rcx=rest and is != 0 + "2: \n" + "mov (%%rsi, %%rdx, 8), %%rax \n" + "sbb %%rbx, %%rax \n" + "mov %%rax, (%%rdi, %%rdx, 8) \n" + + "inc %%rdx \n" + "dec %%rcx \n" + "jnz 2b \n" + + "adc %%rcx, %%rcx \n" + "3: \n" + + : "=a" (dummy1), "=b" (dummy2), "=c" (c), "=d" (dummy3) + : "1" (ss2), "2" (ss2_size), "3" (rest), "S" (ss1), "D" (result) + : "%r8", "cc", "memory" ); + + #endif + + TTMATH_VECTOR_LOGC("UInt::SubVector", c, result, ss1_size) + + return c; + } + + + /*! + this method moves all bits into the left hand side + return value <- this <- c + + the lowest *bit* will be held the 'c' and + the state of one additional bit (on the left hand side) + will be returned + + for example: + let this is 001010000 + after Rcl2_one(1) there'll be 010100001 and Rcl2_one returns 0 + + ***this method is created only on a 64bit platform*** + */ + template + uint UInt::Rcl2_one(uint c) + { + sint b = value_size; + uint * p1 = table; + + + #ifndef __GNUC__ + c = ttmath_rcl_x64(p1,b,c); + #endif + + + #ifdef __GNUC__ + uint dummy, dummy2; + + __asm__ __volatile__( + + "xorq %%rdx, %%rdx \n" // rdx=0 + "negq %%rax \n" // CF=1 if rax!=0 , CF=0 if rax==0 + + "1: \n" + "rclq $1, (%%rbx, %%rdx, 8) \n" + + "incq %%rdx \n" + "decq %%rcx \n" + "jnz 1b \n" + + "adcq %%rcx, %%rcx \n" + + : "=c" (c), "=a" (dummy), "=d" (dummy2) + : "0" (b), "1" (c), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Rcl2_one", c) + + return c; + } + + + /*! + this method moves all bits into the right hand side + c -> this -> return value + + the highest *bit* will be held the 'c' and + the state of one additional bit (on the right hand side) + will be returned + + for example: + let this is 000000010 + after Rcr2_one(1) there'll be 100000001 and Rcr2_one returns 0 + + ***this method is created only on a 64bit platform*** + */ + template + uint UInt::Rcr2_one(uint c) + { + sint b = value_size; + uint * p1 = table; + + + #ifndef __GNUC__ + c = ttmath_rcr_x64(p1,b,c); + #endif + + + #ifdef __GNUC__ + uint dummy; + + __asm__ __volatile__( + + "negq %%rax \n" // CF=1 if rax!=0 , CF=0 if rax==0 + + "1: \n" + "rcrq $1, -8(%%rbx, %%rcx, 8) \n" + + "decq %%rcx \n" + "jnz 1b \n" + + "adcq %%rcx, %%rcx \n" + + : "=c" (c), "=a" (dummy) + : "0" (b), "1" (c), "b" (p1) + : "cc", "memory" ); + + #endif + + TTMATH_LOGC("UInt::Rcr2_one", c) + + return c; + } + + + + /*! + this method moves all bits into the left hand side + return value <- this <- c + + the lowest *bits* will be held the 'c' and + the state of one additional bit (on the left hand side) + will be returned + + for example: + let this is 001010000 + after Rcl2(3, 1) there'll be 010000111 and Rcl2 returns 1 + + ***this method is created only on a 64bit platform*** + */ + template + uint UInt::Rcl2(uint bits, uint c) + { + TTMATH_ASSERT( bits>0 && bits this -> return value + + the highest *bits* will be held the 'c' and + the state of one additional bit (on the right hand side) + will be returned + + for example: + let this is 000000010 + after Rcr2(2, 1) there'll be 110000000 and Rcr2 returns 1 + + ***this method is created only on a 64bit platform*** + */ + template + uint UInt::Rcr2(uint bits, uint c) + { + TTMATH_ASSERT( bits>0 && bits + sint UInt::FindLeadingBitInWord(uint x) + { + sint result; + + + #ifndef __GNUC__ + + unsigned long nIndex = 0; + + if( _BitScanReverse64(&nIndex,x) == 0 ) + result = -1; + else + result = nIndex; + + #endif + + + #ifdef __GNUC__ + uint dummy; + + __asm__ ( + + "movq $-1, %1 \n" + "bsrq %2, %0 \n" + "cmovz %1, %0 \n" + + : "=r" (result), "=&r" (dummy) + : "r" (x) + : "cc" ); + + #endif + + + return result; + } + + + /* + this method returns the number of the highest set bit in one 64-bit word + if the 'x' is zero this method returns '-1' + + ***this method is created only on a 64bit platform*** + */ + template + sint UInt::FindLowestBitInWord(uint x) + { + sint result; + + + #ifndef __GNUC__ + + unsigned long nIndex = 0; + + if( _BitScanForward64(&nIndex,x) == 0 ) + result = -1; + else + result = nIndex; + + #endif + + + #ifdef __GNUC__ + uint dummy; + + __asm__ ( + + "movq $-1, %1 \n" + "bsfq %2, %0 \n" + "cmovz %1, %0 \n" + + : "=r" (result), "=&r" (dummy) + : "r" (x) + : "cc" ); + + #endif + + + return result; + } + + + /*! + this method sets a special bit in the 'value' + and returns the last state of the bit (zero or one) + + ***this method is created only on a 64bit platform*** + + bit is from <0,63> + + e.g. + uint x = 100; + uint bit = SetBitInWord(x, 3); + now: x = 108 and bit = 0 + */ + template + uint UInt::SetBitInWord(uint & value, uint bit) + { + TTMATH_ASSERT( bit < TTMATH_BITS_PER_UINT ) + + uint old_bit; + uint v = value; + + + #ifndef __GNUC__ + old_bit = _bittestandset64((__int64*)&value,bit) != 0; + #endif + + + #ifdef __GNUC__ + + __asm__ ( + + "btsq %%rbx, %%rax \n" + "setc %%bl \n" + "movzx %%bl, %%rbx \n" + + : "=a" (v), "=b" (old_bit) + : "0" (v), "1" (bit) + : "cc" ); + + #endif + + value = v; + + return old_bit; + } + + + /*! + * + * Multiplication + * + * + */ + + + /*! + multiplication: result_high:result_low = a * b + result_high - higher word of the result + result_low - lower word of the result + + this methos never returns a carry + this method is used in the second version of the multiplication algorithms + + ***this method is created only on a 64bit platform*** + */ + template + void UInt::MulTwoWords(uint a, uint b, uint * result_high, uint * result_low) + { + /* + we must use these temporary variables in order to inform the compilator + that value pointed with result1 and result2 has changed + + this has no effect in visual studio but it's usefull when + using gcc and options like -O + */ + uint result1_; + uint result2_; + + + #ifndef __GNUC__ + result1_ = _umul128(a,b,&result2_); + #endif + + + #ifdef __GNUC__ + + __asm__ ( + + "mulq %%rdx \n" + + : "=a" (result1_), "=d" (result2_) + : "0" (a), "1" (b) + : "cc" ); + + #endif + + + *result_low = result1_; + *result_high = result2_; + } + + + + + /*! + * + * Division + * + * + */ + + + /*! + this method calculates 64bits word a:b / 32bits c (a higher, b lower word) + r = a:b / c and rest - remainder + + ***this method is created only on a 64bit platform*** + + * + * WARNING: + * if r (one word) is too small for the result or c is equal zero + * there'll be a hardware interruption (0) + * and probably the end of your program + * + */ + template + void UInt::DivTwoWords(uint a,uint b, uint c, uint * r, uint * rest) + { + uint r_; + uint rest_; + /* + these variables have similar meaning like those in + the multiplication algorithm MulTwoWords + */ + + TTMATH_ASSERT( c != 0 ) + + + #ifndef __GNUC__ + + ttmath_div_x64(&a,&b,c); + r_ = a; + rest_ = b; + + #endif + + + #ifdef __GNUC__ + + __asm__ ( + + "divq %%rcx \n" + + : "=a" (r_), "=d" (rest_) + : "d" (a), "a" (b), "c" (c) + : "cc" ); + + #endif + + + *r = r_; + *rest = rest_; + } + +} //namespace + + +#endif //ifdef TTMATH_PLATFORM64 +#endif //ifndef TTMATH_NOASM +#endif + + diff --git a/3rd_party/ttmath-0.9.3/ttmath/ttmathuint_x86_64_msvc.asm b/3rd_party/ttmath-0.9.3/ttmath/ttmathuint_x86_64_msvc.asm new file mode 100644 index 00000000..b7c85c2b --- /dev/null +++ b/3rd_party/ttmath-0.9.3/ttmath/ttmathuint_x86_64_msvc.asm @@ -0,0 +1,548 @@ +; +; This file is a part of TTMath Bignum Library +; and is distributed under the (new) BSD licence. +; Author: Christian Kaiser +; + +; +; Copyright (c) 2009, Christian Kaiser +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; * Redistributions of source code must retain the above copyright notice, +; this list of conditions and the following disclaimer. +; +; * 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. +; +; * Neither the name Christian Kaiser nor the names of contributors to this +; project may be used to endorse or promote products derived +; from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. +; + +; +; compile with debug info: ml64.exe /c /Zd /Zi ttmathuint_x86_64_msvc.asm +; compile without debug info: ml64.exe /c ttmathuint_x86_64_msvc.asm +; this creates ttmathuint_x86_64_msvc.obj file which can be linked with your program +; + +PUBLIC ttmath_adc_x64 +PUBLIC ttmath_addindexed_x64 +PUBLIC ttmath_addindexed2_x64 +PUBLIC ttmath_addvector_x64 + +PUBLIC ttmath_sbb_x64 +PUBLIC ttmath_subindexed_x64 +PUBLIC ttmath_subvector_x64 + +PUBLIC ttmath_rcl_x64 +PUBLIC ttmath_rcr_x64 + +PUBLIC ttmath_rcl2_x64 +PUBLIC ttmath_rcr2_x64 + +PUBLIC ttmath_div_x64 + +; +; Microsoft x86_64 convention: http://msdn.microsoft.com/en-us/library/9b372w95.aspx +; +; "rax, rcx, rdx, r8-r11 are volatile." +; "rbx, rbp, rdi, rsi, r12-r15 are nonvolatile." +; + + +.CODE + + + ALIGN 8 + +;---------------------------------------- + +ttmath_adc_x64 PROC + ; rcx = p1 + ; rdx = p2 + ; r8 = nSize + ; r9 = nCarry + + xor rax, rax + xor r11, r11 + sub rax, r9 ; sets CARRY if r9 != 0 + + ALIGN 16 + loop1: + mov rax,qword ptr [rdx + r11 * 8] + adc qword ptr [rcx + r11 * 8], rax + lea r11, [r11+1] + dec r8 + jnz loop1 + + setc al + movzx rax, al + + ret + +ttmath_adc_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_addindexed_x64 PROC + + ; rcx = p1 + ; rdx = nSize + ; r8 = nPos + ; r9 = nValue + + xor rax, rax ; rax = result + sub rdx, r8 ; rdx = remaining count of uints + + add qword ptr [rcx + r8 * 8], r9 + jc next1 + + ret + +next1: + mov r9, 1 + + ALIGN 16 +loop1: + dec rdx + jz done_with_cy + lea r8, [r8+1] + add qword ptr [rcx + r8 * 8], r9 + jc loop1 + + ret + +done_with_cy: + lea rax, [rax+1] ; rax = 1 + + ret + +ttmath_addindexed_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_addindexed2_x64 PROC + + ; rcx = p1 (pointer) + ; rdx = b (value size) + ; r8 = nPos + ; r9 = nValue1 + ; [esp+0x28] = nValue2 + + xor rax, rax ; return value + mov r11, rcx ; table + sub rdx, r8 ; rdx = remaining count of uints + mov r10, [esp+028h] ; r10 = nValue2 + + add qword ptr [r11 + r8 * 8], r9 + lea r8, [r8+1] + lea rdx, [rdx-1] + adc qword ptr [r11 + r8 * 8], r10 + jc next + ret + + ALIGN 16 +loop1: + lea r8, [r8+1] + add qword ptr [r11 + r8 * 8], 1 + jc next + ret + +next: + dec rdx ; does not modify CY too... + jnz loop1 + lea rax, [rax+1] + ret + +ttmath_addindexed2_x64 ENDP + + + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + + +ttmath_addvector_x64 PROC + ; rcx = ss1 + ; rdx = ss2 + ; r8 = ss1_size + ; r9 = ss2_size + ; [esp+0x28] = result + + mov r10, [esp+028h] + sub r8, r9 + xor r11, r11 ; r11=0, cf=0 + + ALIGN 16 + loop1: + mov rax, qword ptr [rcx + r11 * 8] + adc rax, qword ptr [rdx + r11 * 8] + mov qword ptr [r10 + r11 * 8], rax + inc r11 + dec r9 + jnz loop1 + + adc r9, r9 ; r9 has the cf state + + or r8, r8 + jz done + + neg r9 ; setting cf from r9 + mov r9, 0 ; don't use xor here (cf is used) + loop2: + mov rax, qword ptr [rcx + r11 * 8] + adc rax, r9 + mov qword ptr [r10 + r11 * 8], rax + inc r11 + dec r8 + jnz loop2 + + adc r8, r8 + mov rax, r8 + + ret + +done: + mov rax, r9 + ret + +ttmath_addvector_x64 ENDP + + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_sbb_x64 PROC + + ; rcx = p1 + ; rdx = p2 + ; r8 = nCount + ; r9 = nCarry + + xor rax, rax + xor r11, r11 + sub rax, r9 ; sets CARRY if r9 != 0 + + ALIGN 16 + loop1: + mov rax,qword ptr [rdx + r11 * 8] + sbb qword ptr [rcx + r11 * 8], rax + lea r11, [r11+1] + dec r8 + jnz loop1 + + setc al + movzx rax, al + + ret + +ttmath_sbb_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_subindexed_x64 PROC + ; rcx = p1 + ; rdx = nSize + ; r8 = nPos + ; r9 = nValue + + sub rdx, r8 ; rdx = remaining count of uints + + ALIGN 16 +loop1: + sub qword ptr [rcx + r8 * 8], r9 + jnc done + + lea r8, [r8+1] + mov r9, 1 + dec rdx + jnz loop1 + + mov rax, 1 + ret + +done: + xor rax, rax + ret + +ttmath_subindexed_x64 ENDP + + + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +; the same asm code as in addvector_x64 only two instructions 'adc' changed to 'sbb' + +ttmath_subvector_x64 PROC + ; rcx = ss1 + ; rdx = ss2 + ; r8 = ss1_size + ; r9 = ss2_size + ; [esp+0x28] = result + + mov r10, [esp+028h] + sub r8, r9 + xor r11, r11 ; r11=0, cf=0 + + ALIGN 16 + loop1: + mov rax, qword ptr [rcx + r11 * 8] + sbb rax, qword ptr [rdx + r11 * 8] + mov qword ptr [r10 + r11 * 8], rax + inc r11 + dec r9 + jnz loop1 + + adc r9, r9 ; r9 has the cf state + + or r8, r8 + jz done + + neg r9 ; setting cf from r9 + mov r9, 0 ; don't use xor here (cf is used) + loop2: + mov rax, qword ptr [rcx + r11 * 8] + sbb rax, r9 + mov qword ptr [r10 + r11 * 8], rax + inc r11 + dec r8 + jnz loop2 + + adc r8, r8 + mov rax, r8 + + ret + +done: + mov rax, r9 + ret + +ttmath_subvector_x64 ENDP + + + + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_rcl_x64 PROC + ; rcx = p1 + ; rdx = b + ; r8 = nLowestBit + + mov r11, rcx ; table + xor r10, r10 + neg r8 ; CY set if r8 <> 0 + + ALIGN 16 +loop1: + rcl qword ptr [r11 + r10 * 8], 1 + lea r10, [r10+1] + dec rdx + jnz loop1 + + setc al + movzx rax, al + + ret + +ttmath_rcl_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_rcr_x64 PROC + ; rcx = p1 + ; rdx = nSize + ; r8 = nLowestBit + + xor r10, r10 + neg r8 ; CY set if r8 <> 0 + + ALIGN 16 +loop1: + rcr qword ptr -8[rcx + rdx * 8], 1 + dec rdx + jnz loop1 + + setc al + movzx rax, al + + ret + +ttmath_rcr_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_div_x64 PROC + + ; rcx = &Hi + ; rdx = &Lo + ; r8 = nDiv + + mov r11, rcx + mov r10, rdx + + mov rdx, qword ptr [r11] + mov rax, qword ptr [r10] + div r8 + mov qword ptr [r10], rdx ; remainder + mov qword ptr [r11], rax ; value + + ret + +ttmath_div_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_rcl2_x64 PROC + ; rcx = p1 + ; rdx = nSize + ; r8 = bits + ; r9 = c + + push rbx + + mov r10, rcx ; r10 = p1 + xor rax, rax + + mov rcx, 64 + sub rcx, r8 + + mov r11, -1 + shr r11, cl ; r11 = mask + + mov rcx, r8 ; rcx = count of bits + + mov rbx, rax ; rbx = old value = 0 + or r9, r9 + cmovnz rbx, r11 ; if (c) then old value = mask + + mov r9, rax ; r9 = index (0..nSize-1) + + ALIGN 16 +loop1: + rol qword ptr [r10+r9*8], cl + mov rax, qword ptr [r10+r9*8] + and rax, r11 + xor qword ptr [r10+r9*8], rax + or qword ptr [r10+r9*8], rbx + mov rbx, rax + + lea r9, [r9+1] + dec rdx + + jnz loop1 + + and rax, 1 + pop rbx + ret + +ttmath_rcl2_x64 ENDP + +;---------------------------------------- + + ALIGN 8 + +;---------------------------------------- + +ttmath_rcr2_x64 PROC + ; rcx = p1 + ; rdx = nSize + ; r8 = bits + ; r9 = c + + push rbx + mov r10, rcx ; r10 = p1 + xor rax, rax + + mov rcx, 64 + sub rcx, r8 + + mov r11, -1 + shl r11, cl ; r11 = mask + + mov rcx, r8 ; rcx = count of bits + + mov rbx, rax ; rbx = old value = 0 + or r9, r9 + cmovnz rbx, r11 ; if (c) then old value = mask + + mov r9, rdx ; r9 = index (0..nSize-1) + lea r9, [r9-1] + + ALIGN 16 +loop1: + ror qword ptr [r10+r9*8], cl + mov rax, qword ptr [r10+r9*8] + and rax, r11 + xor qword ptr [r10+r9*8], rax + or qword ptr [r10+r9*8], rbx + mov rbx, rax + + lea r9, [r9-1] + dec rdx + + jnz loop1 + + rol rax, 1 + and rax, 1 + pop rbx + + ret + +ttmath_rcr2_x64 ENDP + +END diff --git a/3rd_party/winfsp-1.10/License.txt b/3rd_party/winfsp-1.10/License.txt new file mode 100644 index 00000000..01afcd10 --- /dev/null +++ b/3rd_party/winfsp-1.10/License.txt @@ -0,0 +1,708 @@ +The WinFsp project is Copyright (C) Bill Zissimopoulos. It is licensed +under the terms of the GPLv3. + +As a special exception to GPLv3, Bill Zissimopoulos grants additional +permissions to Free/Libre and Open Source Software ("FLOSS") without requiring +that such software is covered by the GPLv3. + + 1. Permission to link with a platform specific version of the WinFsp DLL + (one of: winfsp-x64.dll, winfsp-x86.dll, winfsp-msil.dll). + + 2. Permission to distribute unmodified binary releases of the WinFsp + installer (as released by the WinFsp project). + + These permissions (and no other) are granted provided that the software: + + 1. Is distributed under a license that satisfies the Free Software + Definition Version 1.141 (https://www.gnu.org/philosophy/free-sw.en.html) + or the Open Source Definition Version 1.9 (https://opensource.org/osd). + + 2. Includes the copyright notice "WinFsp - Windows File System Proxy, + Copyright (C) Bill Zissimopoulos" and a link to the WinFsp repository in + its user-interface and any user-facing documentation. + + 3. Is not linked or distributed with proprietary (non-FLOSS) software. + [You cannot mix FLOSS and proprietary software while using WinFsp under + this special exception.] + +Commercial licensing options are also available: Please contact +Bill Zissimopoulos . + +The full text of the GPLv3 license follows below. + +----------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/3rd_party/winfsp-1.10/bin/diag.bat b/3rd_party/winfsp-1.10/bin/diag.bat new file mode 100644 index 00000000..18802b98 --- /dev/null +++ b/3rd_party/winfsp-1.10/bin/diag.bat @@ -0,0 +1,29 @@ +@echo off + +setlocal + +echo WINFSP INSTALLATION DIRECTORY AND LAUNCHER REGISTRATIONS +reg query HKLM\SOFTWARE\WinFsp /s /reg:32 +echo. + +echo WINFSP DLL REGISTRATIONS +reg query HKLM\SYSTEM\CurrentControlSet\Control\NetworkProvider\Order +reg query HKLM\SYSTEM\CurrentControlSet\Services\WinFsp.Np\NetworkProvider +reg query HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Application\WinFsp +echo. + +echo WINFSP FSD CONFIGURATION AND STATUS +sc query WinFsp +sc qc WinFsp +sc sdshow WinFsp +echo. + +echo WINFSP LAUNCHER SERVICE CONFIGURATION AND STATUS +sc query WinFsp.Launcher +sc qc WinFsp.Launcher +sc sdshow WinFsp.Launcher +echo. + +echo OS INFORMATION +systeminfo +echo. diff --git a/3rd_party/winfsp-1.10/bin/fsptool-x64.exe b/3rd_party/winfsp-1.10/bin/fsptool-x64.exe new file mode 100644 index 00000000..e21459c6 Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/fsptool-x64.exe differ diff --git a/3rd_party/winfsp-1.10/bin/fsptool-x86.exe b/3rd_party/winfsp-1.10/bin/fsptool-x86.exe new file mode 100644 index 00000000..71036bee Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/fsptool-x86.exe differ diff --git a/3rd_party/winfsp-1.10/bin/fsreg.bat b/3rd_party/winfsp-1.10/bin/fsreg.bat new file mode 100644 index 00000000..8cad12a8 --- /dev/null +++ b/3rd_party/winfsp-1.10/bin/fsreg.bat @@ -0,0 +1,42 @@ +@echo off + +setlocal +setlocal EnableDelayedExpansion + +set RegKey=HKLM\Software\WinFsp\Services + +if not X%1==X-u ( + set unreg=0 + + if not X%1==X set fsname=%1 + if not X%2==X set fsexec="%~f2" + if not X%3==X set fscmdl=%3 + if not X%4==X set fssecu=%4 + + if X!fscmdl!==X goto usage + if not exist !fsexec! goto notfound + + reg add !RegKey!\!fsname! /v Executable /t REG_SZ /d !fsexec! /f /reg:32 + reg add !RegKey!\!fsname! /v CommandLine /t REG_SZ /d !fscmdl! /f /reg:32 + reg add !RegKey!\!fsname! /v JobControl /t REG_DWORD /d 1 /f /reg:32 + if not X!fssecu!==X reg add !RegKey!\!fsname! /v Security /t REG_SZ /d !fssecu! /f /reg:32 +) else ( + set unreg=1 + + if not X%2==X set fsname=%2 + + if X!fsname!==X goto usage + + reg delete !RegKey!\!fsname! /f /reg:32 +) + +exit /b 0 + +:notfound +echo executable !fsexec! not found >&2 +exit /b 2 + +:usage +echo usage: fsreg NAME EXECUTABLE COMMANDLINE [SECURITY] >&2 +echo usage: fsreg -u NAME >&2 +exit /b 2 diff --git a/3rd_party/winfsp-1.10/bin/launchctl-x64.exe b/3rd_party/winfsp-1.10/bin/launchctl-x64.exe new file mode 100644 index 00000000..3868f565 Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/launchctl-x64.exe differ diff --git a/3rd_party/winfsp-1.10/bin/launchctl-x86.exe b/3rd_party/winfsp-1.10/bin/launchctl-x86.exe new file mode 100644 index 00000000..60ecf4a8 Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/launchctl-x86.exe differ diff --git a/3rd_party/winfsp-1.10/bin/launcher-x64.exe b/3rd_party/winfsp-1.10/bin/launcher-x64.exe new file mode 100644 index 00000000..0e36bef1 Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/launcher-x64.exe differ diff --git a/3rd_party/winfsp-1.10/bin/launcher-x86.exe b/3rd_party/winfsp-1.10/bin/launcher-x86.exe new file mode 100644 index 00000000..53e570af Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/launcher-x86.exe differ diff --git a/3rd_party/winfsp-1.10/bin/memfs-dotnet-msil.exe b/3rd_party/winfsp-1.10/bin/memfs-dotnet-msil.exe new file mode 100644 index 00000000..dcebf95b Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/memfs-dotnet-msil.exe differ diff --git a/3rd_party/winfsp-1.10/bin/memfs-x64.exe b/3rd_party/winfsp-1.10/bin/memfs-x64.exe new file mode 100644 index 00000000..7aff6b7b Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/memfs-x64.exe differ diff --git a/3rd_party/winfsp-1.10/bin/memfs-x86.exe b/3rd_party/winfsp-1.10/bin/memfs-x86.exe new file mode 100644 index 00000000..62c1c7a9 Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/memfs-x86.exe differ diff --git a/3rd_party/winfsp-1.10/bin/winfsp-msil.dll b/3rd_party/winfsp-1.10/bin/winfsp-msil.dll new file mode 100644 index 00000000..5331d740 Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/winfsp-msil.dll differ diff --git a/3rd_party/winfsp-1.10/bin/winfsp-msil.xml b/3rd_party/winfsp-1.10/bin/winfsp-msil.xml new file mode 100644 index 00000000..f53442db --- /dev/null +++ b/3rd_party/winfsp-1.10/bin/winfsp-msil.xml @@ -0,0 +1,1345 @@ + + + + winfsp-msil + + + + + Provides the base class that user mode file systems must inherit from. + + + + + Provides a means to customize the returned status code when an exception happens. + + + STATUS_SUCCESS or error code. + + + + Occurs just before the file system is mounted. + File systems may override this method to configure the file system host. + + + The file system host that is mounting this file system. + + STATUS_SUCCESS or error code. + + + + Occurs just after the file system is mounted, + but prior to receiving any file system operation. + + + The file system host that is mounting this file system. + + STATUS_SUCCESS or error code. + + + + Occurs just after the file system is unmounted. + No other file system operations will be received on this file system. + + + The file system host that is mounting this file system. + + + + + Gets the volume information. + + + Receives the volume information. + + STATUS_SUCCESS or error code. + + + + Sets the volume label. + + + The new label for the volume. + + + Receives the updated volume information. + + STATUS_SUCCESS or error code. + + + + Gets file or directory attributes and security descriptor given a file name. + + + The name of the file or directory to get the attributes and security descriptor for. + + + Receives the file attributes on successful return. + If this call returns STATUS_REPARSE, the file system may place here the index of the + first reparse point within FileName. + + + Receives the file security descriptor. If the SecurityDescriptor parameter is null + on input the file system should not fill this value. + + + STATUS_SUCCESS, STATUS_REPARSE or error code. + STATUS_REPARSE should be returned by file systems that support reparse points when + they encounter a FileName that contains reparse points anywhere but the final path + component. + + + + + Creates a new file or directory. + + + The name of the file or directory to be created. + + + Create options for this request. + + + Determines the specific access rights that have been granted for this request. + + + File attributes to apply to the newly created file or directory. + + + Security descriptor to apply to the newly created file or directory. + + + Allocation size for the newly created file. + + + Receives the file node for the newly created file. + + + Receives the file descriptor for the newly created file. + + + Receives the file information for the newly created file. + + + Receives the normalized name for the newly created file. + + STATUS_SUCCESS or error code. + + + + Opens a file or directory. + + + The name of the file or directory to be opened. + + + Create options for this request. + + + Determines the specific access rights that have been granted for this request. + + + Receives the file node for the newly opened file. + + + Receives the file descriptor for the newly opened file. + + + Receives the file information for the newly opened file. + + + Receives the normalized name for the newly opened file. + + STATUS_SUCCESS or error code. + + + + Overwrites a file. + + + The file node for the file to be overwritten. + + + The file descriptor for the file to be overwritten. + + + File attributes to apply to the overwritten file. + + + When true the existing file attributes should be replaced with the new ones. + When false the existing file attributes should be merged (or'ed) with the new ones. + + + Allocation size for the overwritten file. + + + Receives the updated file information. + + STATUS_SUCCESS or error code. + + + + Cleans up a file or directory. + + + + When CreateFile is used to open or create a file the kernel creates a kernel mode file + object (type FILE_OBJECT) and a handle for it, which it returns to user-mode. The handle may + be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same + file object. When all handles for a particular file object get closed (using CloseHandle) + the system sends a Cleanup request to the file system. + + There will be a Cleanup operation for every Create or Open operation posted to the user mode + file system. However the Cleanup operation is not the final close operation on a file. + The file system must be ready to receive additional operations until close time. This is true + even when the file is being deleted! + + The Flags parameter contains information about the cleanup operation: + + CleanupDelete - + An important function of the Cleanup operation is to complete a delete operation. Deleting + a file or directory in Windows is a three-stage process where the file is first opened, then + tested to see if the delete can proceed and if the answer is positive the file is then + deleted during Cleanup. + When this flag is set, this is the last outstanding cleanup for this particular file node. + + CleanupSetAllocationSize - + The NTFS and FAT file systems reset a file's allocation size when they receive the last + outstanding cleanup for a particular file node. User mode file systems that implement + allocation size and wish to duplicate the NTFS and FAT behavior can use this flag. + + CleanupSetArchiveBit - + File systems that support the archive bit should set the file node's archive bit when this + flag is set. + + CleanupSetLastAccessTime, CleanupSetLastWriteTime, CleanupSetChangeTime - + File systems should set the corresponding file time when each one of these flags is set. + Note that updating the last access time is expensive and a file system may choose to not + implement it. + + + + There is no way to report failure of this operation. This is a Windows limitation. + + + + The file node of the file or directory to cleanup. + + + The file descriptor of the file or directory to cleanup. + + + The name of the file or directory to cleanup. Sent only when a Delete is requested. + + + These flags determine whether the file was modified and whether to delete the file. + + + + + + + + Closes a file or directory. + + + The file node of the file or directory to close. + + + The file descriptor of the file or directory to close. + + + + + Reads a file. + + + The file node of the file to read. + + + The file descriptor of the file to read. + + + Pointer to a buffer that receives the results of the read operation. + + + Offset within the file to read from. + + + Length of data to read. + + + Receives the actual number of bytes read. + + STATUS_SUCCESS or error code. + + + + Writes a file. + + + The file node of the file to write. + + + The file descriptor of the file to write. + + + Pointer to a buffer that receives the results of the write operation. + + + Offset within the file to write to. + + + Length of data to write. + + + When true the file system must write to the current end of file. In this case the Offset + parameter will contain the value -1. + + + When true the file system must not extend the file (i.e. change the file size). + + + Receives the actual number of bytes written. + + + Receives the updated file information. + + STATUS_SUCCESS or error code. + + + + Flushes a file or volume. + + + Note that the FSD will also flush all file/volume caches prior to invoking this operation. + + + The file node of the file to flush. + When this and the FileDesc parameter are null the whole volume is being flushed. + + + The file descriptor of the file to flush. + When this and the FileNode parameter are null the whole volume is being flushed. + + + Receives the updated file information. + + STATUS_SUCCESS or error code. + + + + Gets file or directory information. + + + The file node of the file to get information for. + + + The file descriptor of the file to get information for. + + + Receives the file information. + + STATUS_SUCCESS or error code. + + + + Sets file or directory basic information. + + + The file node of the file to set information for. + + + The file descriptor of the file to set information for. + + + File attributes to apply to the file or directory. + If the value -1 is sent, the file attributes should not be changed. + + + Creation time to apply to the file or directory. + If the value 0 is sent, the creation time should not be changed. + + + Last access time to apply to the file or directory. + If the value 0 is sent, the last access time should not be changed. + + + Last write time to apply to the file or directory. + If the value 0 is sent, the last write time should not be changed. + + + Change time to apply to the file or directory. + If the value 0 is sent, the change time should not be changed. + + + Receives the updated file information. + + STATUS_SUCCESS or error code. + + + + Sets file/allocation size. + + + + This function is used to change a file's sizes. Windows file systems maintain two kinds + of sizes: the file size is where the End Of File (EOF) is, and the allocation size is the + actual size that a file takes up on the "disk". + + The rules regarding file/allocation size are: + + + Allocation size must always be aligned to the allocation unit boundary. The allocation + unit is the product SectorSize * SectorsPerAllocationUnit. The FSD will always send + properly aligned allocation sizes when setting the allocation size. + + + Allocation size is always greater or equal to the file size. + + + A file size of more than the current allocation size will also extend the allocation + size to the next allocation unit boundary. + + + An allocation size of less than the current file size should also truncate the current + file size. + + + + + + The file node of the file to set the file/allocation size for. + + + The file descriptor of the file to set the file/allocation size for. + + + New file/allocation size to apply to the file. + + + If true, then the allocation size is being set. if false, then the file size is being set. + + + Receives the updated file information. + + STATUS_SUCCESS or error code. + + + + Determines whether a file or directory can be deleted. + + + + This function tests whether a file or directory can be safely deleted. This function does + not need to perform access checks, but may performs tasks such as check for empty + directories, etc. + + This function should NEVER delete the file or directory in question. Deletion should + happen during Cleanup with the CleanupDelete flag set. + + This function gets called when Win32 API's such as DeleteFile or RemoveDirectory are used. + It does not get called when a file or directory is opened with FILE_DELETE_ON_CLOSE. + + NOTE: If both CanDelete and SetDelete are defined, SetDelete takes precedence. However + most file systems need only implement the CanDelete operation. + + + + The file node of the file or directory to test for deletion. + + + The file descriptor of the file or directory to test for deletion. + + + The name of the file or directory to test for deletion. + + STATUS_SUCCESS or error code. + + + + + + Renames a file or directory. + + + The kernel mode FSD provides certain guarantees prior to posting a rename operation: + + + A file cannot be renamed if a file with the same name exists and has open handles. + + + A directory cannot be renamed if it or any of its subdirectories contains a file that + has open handles. + + + + + The file node of the file or directory to be renamed. + + + The file descriptor of the file or directory to be renamed. + + + The current name of the file or directory to rename. + + + The new name for the file or directory. + + + Whether to replace a file that already exists at NewFileName. + + STATUS_SUCCESS or error code. + + + + Gets file or directory security descriptor. + + + The file node of the file or directory to get the security descriptor for. + + + The file descriptor of the file or directory to get the security descriptor for. + + + Receives the file security descriptor. + + STATUS_SUCCESS or error code. + + + + Sets file or directory security descriptor. + + + The file node of the file or directory to set the security descriptor for. + + + The file descriptor of the file or directory to set the security descriptor for. + + + Describes what parts of the file or directory security descriptor should be modified. + + + Describes the modifications to apply to the file or directory security descriptor. + + STATUS_SUCCESS or error code. + + + + + Reads a directory. + + + + + + Reads a directory entry. + + + The file node of the directory to be read. + + + The file descriptor of the directory to be read. + + + The pattern to match against files in this directory. Can be null. The file system + can choose to ignore this parameter as the FSD will always perform its own pattern + matching on the returned results. + + + A file name that marks where in the directory to start reading. Files with names + that are greater than (not equal to) this marker (in the directory order determined + by the file system) should be returned. Can be null. + + + Can be used by the file system to track the ReadDirectory operation. + + + Receives the file name for the directory entry. + + + Receives the file information for the directory entry. + + True if there are additional directory entries to return. False otherwise. + + + + + Resolves reparse points. + + + + + Gets a reparse point given a file name. + + + The name of the file or directory to get the reparse point for. + + + Determines whether the passed file name is assumed to be a directory. + + + Receives the reparse data for the file or directory. + + STATUS_SUCCESS or error code. + + + + Gets a reparse point. + + + The file node of the reparse point. + + + The file descriptor of the reparse point. + + + The file name of the reparse point. + + + Receives the reparse data for the reparse point. + + STATUS_SUCCESS or error code. + + + + Sets a reparse point. + + + The file node of the reparse point. + + + The file descriptor of the reparse point. + + + The file name of the reparse point. + + + The new reparse data for the reparse point. + + STATUS_SUCCESS or error code. + + + + Deletes a reparse point. + + + The file node of the reparse point. + + + The file descriptor of the reparse point. + + + The file name of the reparse point. + + + The reparse data for the reparse point. + + STATUS_SUCCESS or error code. + + + + Gets named streams information. + + + + + Gets named streams information entry. + + + The file node of the file or directory to get stream information for. + + + The file descriptor of the file or directory to get stream information for. + + + Can be used by the file system to track the GetStreamInfo operation. + + + Receives the stream name for the stream entry. + + + Receives the stream size for the stream entry. + + + Receives the stream allocation size for the stream entry. + + True if there are additional stream entries to return. False otherwise. + + + + Gets directory information for a single file or directory within a parent directory. + + + The file node of the parent directory. + + + The file descriptor of the parent directory. + + + The name of the file or directory to get information for. This name is relative + to the parent directory and is a single path component. + + + Receives the normalized name from the directory entry. + + + Receives the file information. + + STATUS_SUCCESS or error code. + + + + Processes a control code. + + + This function is called when a program uses the DeviceIoControl API. + + + The file node of the file or directory to be controled. + + + The file descriptor of the file or directory to be controled. + + + The control code for the operation. This code must have a DeviceType with bit + 0x8000 set and must have a TransferType of METHOD_BUFFERED. + + + Pointer to a buffer that contains the input data. + + + Input data length. + + + Pointer to a buffer that will receive the output data. + + + Output data length. + + + Receives the actual number of bytes transferred. + + STATUS_SUCCESS or error code. + + + + Sets the file delete flag. + + + + This function sets a flag to indicates whether the FSD file should delete a file + when it is closed. This function does not need to perform access checks, but may + performs tasks such as check for empty directories, etc. + + This function should NEVER delete the file or directory in question. Deletion should + happen during Cleanup with the CleanupDelete flag set. + + This function gets called when Win32 API's such as DeleteFile or RemoveDirectory are used. + It does not get called when a file or directory is opened with FILE_DELETE_ON_CLOSE. + + NOTE: If both CanDelete and SetDelete are defined, SetDelete takes precedence. However + most file systems need only implement the CanDelete operation. + + + + The file node of the file or directory to set the delete flag for. + + + The file descriptor of the file or directory to set the delete flag for. + + + The name of the file or directory to set the delete flag for. + + + If set to TRUE the FSD indicates that the file will be deleted on Cleanup; otherwise + it will not be deleted. It is legal to receive multiple SetDelete calls for the same + file with different DeleteFile parameters. + + STATUS_SUCCESS or error code. + + + + + + Converts a Win32 error code to a Windows kernel status code. + + + + + Converts a Windows kernel status code to a Win32 error code. + + + + + Gets the originating process ID. + + + Valid only during Create, Open and Rename requests when the target exists. + + + + + Modifies a security descriptor. [OBSOLETE] + + + This is a helper for implementing the SetSecurity operation. + + + The original security descriptor. + + + Describes what parts of the file or directory security descriptor should be modified. + + + Describes the modifications to apply to the file or directory security descriptor. + + The modified security descriptor. + + + + + Modifies a security descriptor. + + + This is a helper for implementing the SetSecurity operation. + + + The original security descriptor. + + + Describes what parts of the file or directory security descriptor should be modified. + + + Describes the modifications to apply to the file or directory security descriptor. + + + The modified security descriptor. This parameter is modified only on success. + + STATUS_SUCCESS or error code. + + + + + Finds a reparse point in file name. + + + This is a helper for implementing the GetSecurityByName operation in file systems + that support reparse points. + + + The name of the file or directory. + + + Receives the index of the first reparse point within FileName. + + True if a reparse point was found, false otherwise. + + + + + Makes a byte array that contains a reparse point. + + The reparse point byte array. + + + + Gets the reparse tag from reparse data. + + + The reparse data to extract the reparse tag from. + + The reparse tag. + + + + Tests whether reparse data can be replaced. + + + This is a helper for implementing the SetReparsePoint/DeleteReparsePoint operation + in file systems that support reparse points. + + + The current reparse data. + + + The replacement reparse data. + + STATUS_SUCCESS or error code. + + + + + + Provides a means to host (mount) a file system. + + + + + Creates an instance of the FileSystemHost class. + + The file system to host. + + + + Unmounts the file system and releases all associated resources. + + + + + Gets or sets the sector size used by the file system. + + + + + Gets or sets the sectors per allocation unit used by the file system. + + + + + Gets or sets the maximum path component length used by the file system. + + + + + Gets or sets the volume creation time. + + + + + Gets or sets the volume serial number. + + + + + Gets or sets the file information timeout. + + + + + Gets or sets the volume information timeout. + + + + + Gets or sets the directory information timeout. + + + + + Gets or sets the security information timeout. + + + + + Gets or sets the stream information timeout. + + + + + Gets or sets the EA information timeout. + + + + + Gets or sets a value that determines whether the file system is case sensitive. + + + + + Gets or sets a value that determines whether a case insensitive file system + preserves case in file names. + + + + + Gets or sets a value that determines whether file names support unicode characters. + + + + + Gets or sets a value that determines whether the file system supports ACL security. + + + + + Gets or sets a value that determines whether the file system supports reparse points. + + + + + Gets or sets a value that determines whether the file system allows creation of + symbolic links without additional privileges. + + + + + Gets or sets a value that determines whether the file system supports named streams. + + + + + Gets or sets a value that determines whether the file system supports extended attributes. + + + + + Gets or sets the prefix for a network file system. + + + + + Gets or sets the file system name. + + + + + Checks whether mounting a file system is possible. + + + The mount point for the new file system. A value of null means that + the file system should use the next available drive letter counting + downwards from Z: as its mount point. + + STATUS_SUCCESS or error code. + + + + Mounts a file system. + + + The mount point for the new file system. A value of null means that + the file system should use the next available drive letter counting + downwards from Z: as its mount point. + + + Security descriptor to use if mounting on (newly created) directory. + A value of null means the directory should be created with default + security. + + + If true file system operations are synchronized using an exclusive lock. + + + A value of 0 disables all debug logging. + A value of -1 enables all debug logging. + + STATUS_SUCCESS or error code. + + + + Mounts a file system. + + + The mount point for the new file system. A value of null means that + the file system should use the next available drive letter counting + downwards from Z: as its mount point. + + + Number of threads to use to service file system requests. A value + of 0 means that the default number of threads should be used. + + + Security descriptor to use if mounting on (newly created) directory. + A value of null means the directory should be created with default + security. + + + If true file system operations are synchronized using an exclusive lock. + + + A value of 0 disables all debug logging. + A value of -1 enables all debug logging. + + STATUS_SUCCESS or error code. + + + + Unmounts the file system and releases all associated resources. + + + + + Gets the file system mount point. + + The file system mount point. + + + + Gets the hosted file system. + + The hosted file system. + + + + Sets the debug log file to use when debug logging is enabled. + + + The debug log file name. A value of "-" means standard error output. + + STATUS_SUCCESS or error code. + + + + Return the installed version of WinFsp. + + + + + Returns a RequestHint to reference the current operation asynchronously. + + + + + Asynchronously complete a Read operation. + + + A reference to the operation to complete. + + + STATUS_SUCCESS or error code. + + + Number of bytes read. + + + + + Asynchronously complete a Write operation. + + + A reference to the operation to complete. + + + STATUS_SUCCESS or error code. + + + The number of bytes written. + + + Updated file information. + + + + + Asynchronously complete a ReadDirectory operation. + + + A reference to the operation to complete. + + + STATUS_SUCCESS or error code. + + + Number of bytes read. + + + + + Begin notifying Windows that the file system has file changes. + + + + A file system that wishes to notify Windows about file changes must + first issue an FspFileSystemBegin call, followed by 0 or more + FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call. + + This operation blocks concurrent file rename operations. File rename + operations may interfere with file notification, because a file being + notified may also be concurrently renamed. After all file change + notifications have been issued, you must make sure to call + FspFileSystemNotifyEnd to allow file rename operations to proceed. + + + + STATUS_SUCCESS or error code. The error code STATUS_CANT_WAIT means that + a file rename operation is currently in progress and the operation must be + retried at a later time. + + + + + End notifying Windows that the file system has file changes. + + + + A file system that wishes to notify Windows about file changes must + first issue an FspFileSystemBegin call, followed by 0 or more + FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call. + + This operation allows any blocked file rename operations to proceed. + + + STATUS_SUCCESS or error code. + + + + Notify Windows that the file system has file changes. + + + + A file system that wishes to notify Windows about file changes must + first issue an FspFileSystemBegin call, followed by 0 or more + FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call. + + Note that FspFileSystemNotify requires file names to be normalized. A + normalized file name is one that contains the correct case of all characters + in the file name. + + For case-sensitive file systems all file names are normalized by definition. + For case-insensitive file systems that implement file name normalization, + a normalized file name is the one that the file system specifies in the + response to Create or Open (see also FspFileSystemGetOpenFileInfo). For + case-insensitive file systems that do not implement file name normalization + a normalized file name is the upper case version of the file name used + to open the file. + + + STATUS_SUCCESS or error code. + + + + Contains volume information about a file system. + + + + + Total size of volume in bytes. + + + + + Free size of volume in bytes. + + + + + Sets the volume label. + + + + + Contains metadata information about a file or directory. + + + + + The file or directory attributes. + + + + + The reparse tag of the file or directory. + This value is 0 if the file or directory is not a reparse point. + + + + + The allocation size of the file. + + + + + The file size of the file (end of file). + + + + + The time that the file or directory was created. + + + + + The time that the file or directory was last accessed. + + + + + The time that the file or direcotry was last modified. + + + + + The time that the file or directory metadata was last modified. + + + + + A unique identifier that is associated with the file or directory. + Not all file systems support this value. + + + + + The number of hard links. + Not currently implemented. Set to 0. + + + + + The extended attribute size of the file. + + + + + Enumeration of all the possible values for NotifyInfo.Action + + + + + Enumeration of all the possible values for NotifyInfo.Filter + + + + + Contains file change notification information. + + + + + Provides the base class for a process that can be run as a service, + command line application or under the control of the WinFsp launcher. + + + + + Creates an instance of the Service class. + + The name of the service. + + + + Runs a service. + + Service process exit code. + + + + Stops a running service. + + + + + Gets or sets the service process exit code. + + + + + Provides a means to customize the returned status code when an exception happens. + + + STATUS_SUCCESS or error code. + + + + Occurs when the service starts. + + Command line arguments passed to the service. + + + + Occurs when the service stops. + + + + diff --git a/3rd_party/winfsp-1.10/bin/winfsp-x64.dll b/3rd_party/winfsp-1.10/bin/winfsp-x64.dll new file mode 100644 index 00000000..05638d0c Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/winfsp-x64.dll differ diff --git a/3rd_party/winfsp-1.10/bin/winfsp-x64.sys b/3rd_party/winfsp-1.10/bin/winfsp-x64.sys new file mode 100644 index 00000000..1e859751 Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/winfsp-x64.sys differ diff --git a/3rd_party/winfsp-1.10/bin/winfsp-x86.dll b/3rd_party/winfsp-1.10/bin/winfsp-x86.dll new file mode 100644 index 00000000..b96b0460 Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/winfsp-x86.dll differ diff --git a/3rd_party/winfsp-1.10/bin/winfsp-x86.sys b/3rd_party/winfsp-1.10/bin/winfsp-x86.sys new file mode 100644 index 00000000..da423a87 Binary files /dev/null and b/3rd_party/winfsp-1.10/bin/winfsp-x86.sys differ diff --git a/3rd_party/winfsp-1.10/inc/fuse/fuse.h b/3rd_party/winfsp-1.10/inc/fuse/fuse.h new file mode 100644 index 00000000..4d5af860 --- /dev/null +++ b/3rd_party/winfsp-1.10/inc/fuse/fuse.h @@ -0,0 +1,269 @@ +/** + * @file fuse/fuse.h + * WinFsp FUSE compatible API. + * + * This file is derived from libfuse/include/fuse.h: + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef FUSE_H_ +#define FUSE_H_ + +#include "fuse_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct fuse; + +typedef int (*fuse_fill_dir_t)(void *buf, const char *name, + const struct fuse_stat *stbuf, fuse_off_t off); +typedef struct fuse_dirhandle *fuse_dirh_t; +typedef int (*fuse_dirfil_t)(fuse_dirh_t h, const char *name, + int type, fuse_ino_t ino); + +struct fuse_operations +{ + /* S - supported by WinFsp */ + /* S */ int (*getattr)(const char *path, struct fuse_stat *stbuf); + /* S */ int (*getdir)(const char *path, fuse_dirh_t h, fuse_dirfil_t filler); + /* S */ int (*readlink)(const char *path, char *buf, size_t size); + /* S */ int (*mknod)(const char *path, fuse_mode_t mode, fuse_dev_t dev); + /* S */ int (*mkdir)(const char *path, fuse_mode_t mode); + /* S */ int (*unlink)(const char *path); + /* S */ int (*rmdir)(const char *path); + /* S */ int (*symlink)(const char *dstpath, const char *srcpath); + /* S */ int (*rename)(const char *oldpath, const char *newpath); + /* _ */ int (*link)(const char *srcpath, const char *dstpath); + /* S */ int (*chmod)(const char *path, fuse_mode_t mode); + /* S */ int (*chown)(const char *path, fuse_uid_t uid, fuse_gid_t gid); + /* S */ int (*truncate)(const char *path, fuse_off_t size); + /* S */ int (*utime)(const char *path, struct fuse_utimbuf *timbuf); + /* S */ int (*open)(const char *path, struct fuse_file_info *fi); + /* S */ int (*read)(const char *path, char *buf, size_t size, fuse_off_t off, + struct fuse_file_info *fi); + /* S */ int (*write)(const char *path, const char *buf, size_t size, fuse_off_t off, + struct fuse_file_info *fi); + /* S */ int (*statfs)(const char *path, struct fuse_statvfs *stbuf); + /* S */ int (*flush)(const char *path, struct fuse_file_info *fi); + /* S */ int (*release)(const char *path, struct fuse_file_info *fi); + /* S */ int (*fsync)(const char *path, int datasync, struct fuse_file_info *fi); + /* S */ int (*setxattr)(const char *path, const char *name, const char *value, size_t size, + int flags); + /* S */ int (*getxattr)(const char *path, const char *name, char *value, size_t size); + /* S */ int (*listxattr)(const char *path, char *namebuf, size_t size); + /* S */ int (*removexattr)(const char *path, const char *name); + /* S */ int (*opendir)(const char *path, struct fuse_file_info *fi); + /* S */ int (*readdir)(const char *path, void *buf, fuse_fill_dir_t filler, fuse_off_t off, + struct fuse_file_info *fi); + /* S */ int (*releasedir)(const char *path, struct fuse_file_info *fi); + /* S */ int (*fsyncdir)(const char *path, int datasync, struct fuse_file_info *fi); + /* S */ void *(*init)(struct fuse_conn_info *conn); + /* S */ void (*destroy)(void *data); + /* S */ int (*access)(const char *path, int mask); + /* S */ int (*create)(const char *path, fuse_mode_t mode, struct fuse_file_info *fi); + /* S */ int (*ftruncate)(const char *path, fuse_off_t off, struct fuse_file_info *fi); + /* S */ int (*fgetattr)(const char *path, struct fuse_stat *stbuf, struct fuse_file_info *fi); + /* _ */ int (*lock)(const char *path, + struct fuse_file_info *fi, int cmd, struct fuse_flock *lock); + /* S */ int (*utimens)(const char *path, const struct fuse_timespec tv[2]); + /* _ */ int (*bmap)(const char *path, size_t blocksize, uint64_t *idx); + /* _ */ unsigned int flag_nullpath_ok:1; + /* _ */ unsigned int flag_nopath:1; + /* _ */ unsigned int flag_utime_omit_ok:1; + /* _ */ unsigned int flag_reserved:29; + /* S */ int (*ioctl)(const char *path, int cmd, void *arg, struct fuse_file_info *fi, + unsigned int flags, void *data); + /* _ */ int (*poll)(const char *path, struct fuse_file_info *fi, + struct fuse_pollhandle *ph, unsigned *reventsp); + /* FUSE 2.9 */ + /* _ */ int (*write_buf)(const char *path, + struct fuse_bufvec *buf, fuse_off_t off, struct fuse_file_info *fi); + /* _ */ int (*read_buf)(const char *path, + struct fuse_bufvec **bufp, size_t size, fuse_off_t off, struct fuse_file_info *fi); + /* _ */ int (*flock)(const char *path, struct fuse_file_info *, int op); + /* _ */ int (*fallocate)(const char *path, int mode, fuse_off_t off, fuse_off_t len, + struct fuse_file_info *fi); + /* OSXFUSE */ + /* _ */ int (*reserved00)(); + /* _ */ int (*reserved01)(); + /* _ */ int (*reserved02)(); + /* _ */ int (*statfs_x)(const char *path, struct fuse_statfs *stbuf); + /* _ */ int (*setvolname)(const char *volname); + /* _ */ int (*exchange)(const char *oldpath, const char *newpath, unsigned long flags); + /* _ */ int (*getxtimes)(const char *path, + struct fuse_timespec *bkuptime, struct fuse_timespec *crtime); + /* _ */ int (*setbkuptime)(const char *path, const struct fuse_timespec *tv); + /* S */ int (*setchgtime)(const char *path, const struct fuse_timespec *tv); + /* S */ int (*setcrtime)(const char *path, const struct fuse_timespec *tv); + /* S */ int (*chflags)(const char *path, uint32_t flags); + /* _ */ int (*setattr_x)(const char *path, struct fuse_setattr_x *attr); + /* _ */ int (*fsetattr_x)(const char *path, struct fuse_setattr_x *attr, + struct fuse_file_info *fi); +}; + +struct fuse_context +{ + struct fuse *fuse; + fuse_uid_t uid; + fuse_gid_t gid; + fuse_pid_t pid; + void *private_data; + fuse_mode_t umask; +}; + +#define fuse_main(argc, argv, ops, data)\ + fuse_main_real(argc, argv, ops, sizeof *(ops), data) + +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_main_real)(struct fsp_fuse_env *env, + int argc, char *argv[], + const struct fuse_operations *ops, size_t opsize, void *data); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_is_lib_option)(struct fsp_fuse_env *env, + const char *opt); +FSP_FUSE_API struct fuse *FSP_FUSE_API_NAME(fsp_fuse_new)(struct fsp_fuse_env *env, + struct fuse_chan *ch, struct fuse_args *args, + const struct fuse_operations *ops, size_t opsize, void *data); +FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_destroy)(struct fsp_fuse_env *env, + struct fuse *f); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_loop)(struct fsp_fuse_env *env, + struct fuse *f); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_loop_mt)(struct fsp_fuse_env *env, + struct fuse *f); +FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_exit)(struct fsp_fuse_env *env, + struct fuse *f); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_exited)(struct fsp_fuse_env *env, + struct fuse *f); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_notify)(struct fsp_fuse_env *env, + struct fuse *f, const char *path, uint32_t action); +FSP_FUSE_API struct fuse_context *FSP_FUSE_API_NAME(fsp_fuse_get_context)(struct fsp_fuse_env *env); + +FSP_FUSE_SYM( +int fuse_main_real(int argc, char *argv[], + const struct fuse_operations *ops, size_t opsize, void *data), +{ + return FSP_FUSE_API_CALL(fsp_fuse_main_real) + (fsp_fuse_env(), argc, argv, ops, opsize, data); +}) + +FSP_FUSE_SYM( +int fuse_is_lib_option(const char *opt), +{ + return FSP_FUSE_API_CALL(fsp_fuse_is_lib_option) + (fsp_fuse_env(), opt); +}) + +FSP_FUSE_SYM( +struct fuse *fuse_new(struct fuse_chan *ch, struct fuse_args *args, + const struct fuse_operations *ops, size_t opsize, void *data), +{ + return FSP_FUSE_API_CALL(fsp_fuse_new) + (fsp_fuse_env(), ch, args, ops, opsize, data); +}) + +FSP_FUSE_SYM( +void fuse_destroy(struct fuse *f), +{ + FSP_FUSE_API_CALL(fsp_fuse_destroy) + (fsp_fuse_env(), f); +}) + +FSP_FUSE_SYM( +int fuse_loop(struct fuse *f), +{ + return FSP_FUSE_API_CALL(fsp_fuse_loop) + (fsp_fuse_env(), f); +}) + +FSP_FUSE_SYM( +int fuse_loop_mt(struct fuse *f), +{ + return FSP_FUSE_API_CALL(fsp_fuse_loop_mt) + (fsp_fuse_env(), f); +}) + +FSP_FUSE_SYM( +void fuse_exit(struct fuse *f), +{ + FSP_FUSE_API_CALL(fsp_fuse_exit) + (fsp_fuse_env(), f); +}) + +FSP_FUSE_SYM( +int fuse_exited(struct fuse *f), +{ + return FSP_FUSE_API_CALL(fsp_fuse_exited) + (fsp_fuse_env(), f); +}) + +FSP_FUSE_SYM( +int fuse_notify(struct fuse *f, const char *path, uint32_t action), +{ + return FSP_FUSE_API_CALL(fsp_fuse_notify) + (fsp_fuse_env(), f, path, action); +}) + +FSP_FUSE_SYM( +struct fuse_context *fuse_get_context(void), +{ + return FSP_FUSE_API_CALL(fsp_fuse_get_context) + (fsp_fuse_env()); +}) + +FSP_FUSE_SYM( +int fuse_getgroups(int size, fuse_gid_t list[]), +{ + (void)size; + (void)list; + return -ENOSYS; +}) + +FSP_FUSE_SYM( +int fuse_interrupted(void), +{ + return 0; +}) + +FSP_FUSE_SYM( +int fuse_invalidate(struct fuse *f, const char *path), +{ + return FSP_FUSE_API_CALL(fsp_fuse_notify) + (fsp_fuse_env(), f, path, 0); +}) + +FSP_FUSE_SYM( +int fuse_notify_poll(struct fuse_pollhandle *ph), +{ + (void)ph; + return 0; +}) + +FSP_FUSE_SYM( +struct fuse_session *fuse_get_session(struct fuse *f), +{ + return (struct fuse_session *)f; +}) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/inc/fuse/fuse_common.h b/3rd_party/winfsp-1.10/inc/fuse/fuse_common.h new file mode 100644 index 00000000..5e7cd432 --- /dev/null +++ b/3rd_party/winfsp-1.10/inc/fuse/fuse_common.h @@ -0,0 +1,199 @@ +/** + * @file fuse/fuse_common.h + * WinFsp FUSE compatible API. + * + * This file is derived from libfuse/include/fuse_common.h: + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef FUSE_COMMON_H_ +#define FUSE_COMMON_H_ + +#include "winfsp_fuse.h" +#include "fuse_opt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FUSE_MAJOR_VERSION 2 +#define FUSE_MINOR_VERSION 8 +#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) +#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) + +#define FUSE_CAP_ASYNC_READ (1 << 0) +#define FUSE_CAP_POSIX_LOCKS (1 << 1) +#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3) +#define FUSE_CAP_EXPORT_SUPPORT (1 << 4) +#define FUSE_CAP_BIG_WRITES (1 << 5) +#define FUSE_CAP_DONT_MASK (1 << 6) +#define FUSE_CAP_ALLOCATE (1 << 27) /* reserved (OSXFUSE) */ +#define FUSE_CAP_EXCHANGE_DATA (1 << 28) /* reserved (OSXFUSE) */ +#define FUSE_CAP_CASE_INSENSITIVE (1 << 29) /* file system is case insensitive */ +#define FUSE_CAP_VOL_RENAME (1 << 30) /* reserved (OSXFUSE) */ +#define FUSE_CAP_XTIMES (1 << 31) /* reserved (OSXFUSE) */ + +#define FSP_FUSE_CAP_READDIR_PLUS (1 << 21) /* file system supports enhanced readdir */ +#define FSP_FUSE_CAP_READ_ONLY (1 << 22) /* file system is marked read-only */ +#define FSP_FUSE_CAP_STAT_EX (1 << 23) /* file system supports fuse_stat_ex */ +#define FSP_FUSE_CAP_DELETE_ACCESS (1 << 24) /* file system supports access with DELETE_OK */ +#define FSP_FUSE_CAP_CASE_INSENSITIVE FUSE_CAP_CASE_INSENSITIVE + +#define FUSE_IOCTL_COMPAT (1 << 0) +#define FUSE_IOCTL_UNRESTRICTED (1 << 1) +#define FUSE_IOCTL_RETRY (1 << 2) +#define FUSE_IOCTL_MAX_IOV 256 + +/* from FreeBSD */ +#define FSP_FUSE_UF_HIDDEN 0x00008000 +#define FSP_FUSE_UF_READONLY 0x00001000 +#define FSP_FUSE_UF_SYSTEM 0x00000080 +#define FSP_FUSE_UF_ARCHIVE 0x00000800 +#if !defined(UF_HIDDEN) +#define UF_HIDDEN FSP_FUSE_UF_HIDDEN +#endif +#if !defined(UF_READONLY) +#define UF_READONLY FSP_FUSE_UF_READONLY +#endif +#if !defined(UF_SYSTEM) +#define UF_SYSTEM FSP_FUSE_UF_SYSTEM +#endif +#if !defined(UF_ARCHIVE) +#define UF_ARCHIVE FSP_FUSE_UF_ARCHIVE +#endif + +/* delete access */ +#define FSP_FUSE_DELETE_OK 0x40000000 + +/* notify extension */ +#define FSP_FUSE_NOTIFY_MKDIR 0x0001 +#define FSP_FUSE_NOTIFY_RMDIR 0x0002 +#define FSP_FUSE_NOTIFY_CREATE 0x0004 +#define FSP_FUSE_NOTIFY_UNLINK 0x0008 +#define FSP_FUSE_NOTIFY_CHMOD 0x0010 +#define FSP_FUSE_NOTIFY_CHOWN 0x0020 +#define FSP_FUSE_NOTIFY_UTIME 0x0040 +#define FSP_FUSE_NOTIFY_CHFLAGS 0x0080 +#define FSP_FUSE_NOTIFY_TRUNCATE 0x0100 + +struct fuse_file_info +{ + int flags; + unsigned int fh_old; + int writepage; + unsigned int direct_io:1; + unsigned int keep_cache:1; + unsigned int flush:1; + unsigned int nonseekable:1; + unsigned int padding:28; + uint64_t fh; + uint64_t lock_owner; +}; + +struct fuse_conn_info +{ + unsigned proto_major; + unsigned proto_minor; + unsigned async_read; + unsigned max_write; + unsigned max_readahead; + unsigned capable; + unsigned want; + unsigned reserved[25]; +}; + +struct fuse_session; +struct fuse_chan; +struct fuse_pollhandle; +struct fuse_bufvec; +struct fuse_statfs; +struct fuse_setattr_x; + +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_version)(struct fsp_fuse_env *env); +FSP_FUSE_API struct fuse_chan *FSP_FUSE_API_NAME(fsp_fuse_mount)(struct fsp_fuse_env *env, + const char *mountpoint, struct fuse_args *args); +FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_unmount)(struct fsp_fuse_env *env, + const char *mountpoint, struct fuse_chan *ch); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_parse_cmdline)(struct fsp_fuse_env *env, + struct fuse_args *args, + char **mountpoint, int *multithreaded, int *foreground); +FSP_FUSE_API int32_t FSP_FUSE_API_NAME(fsp_fuse_ntstatus_from_errno)(struct fsp_fuse_env *env, + int err); + +FSP_FUSE_SYM( +int fuse_version(void), +{ + return FSP_FUSE_API_CALL(fsp_fuse_version) + (fsp_fuse_env()); +}) + +FSP_FUSE_SYM( +struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args), +{ + return FSP_FUSE_API_CALL(fsp_fuse_mount) + (fsp_fuse_env(), mountpoint, args); +}) + +FSP_FUSE_SYM( +void fuse_unmount(const char *mountpoint, struct fuse_chan *ch), +{ + FSP_FUSE_API_CALL(fsp_fuse_unmount) + (fsp_fuse_env(), mountpoint, ch); +}) + +FSP_FUSE_SYM( +int fuse_parse_cmdline(struct fuse_args *args, + char **mountpoint, int *multithreaded, int *foreground), +{ + return FSP_FUSE_API_CALL(fsp_fuse_parse_cmdline) + (fsp_fuse_env(), args, mountpoint, multithreaded, foreground); +}) + +FSP_FUSE_SYM( +void fuse_pollhandle_destroy(struct fuse_pollhandle *ph), +{ + (void)ph; +}) + +FSP_FUSE_SYM( +int fuse_daemonize(int foreground), +{ + return fsp_fuse_daemonize(foreground); +}) + +FSP_FUSE_SYM( +int fuse_set_signal_handlers(struct fuse_session *se), +{ + return fsp_fuse_set_signal_handlers(se); +}) + +FSP_FUSE_SYM( +void fuse_remove_signal_handlers(struct fuse_session *se), +{ + (void)se; + fsp_fuse_set_signal_handlers(0); +}) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/inc/fuse/fuse_opt.h b/3rd_party/winfsp-1.10/inc/fuse/fuse_opt.h new file mode 100644 index 00000000..30d659cf --- /dev/null +++ b/3rd_party/winfsp-1.10/inc/fuse/fuse_opt.h @@ -0,0 +1,133 @@ +/** + * @file fuse/fuse_opt.h + * WinFsp FUSE compatible API. + * + * This file is derived from libfuse/include/fuse_opt.h: + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef FUSE_OPT_H_ +#define FUSE_OPT_H_ + +#include "winfsp_fuse.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FUSE_OPT_KEY(templ, key) { templ, -1, key } +#define FUSE_OPT_END { NULL, 0, 0 } + +#define FUSE_OPT_KEY_OPT -1 +#define FUSE_OPT_KEY_NONOPT -2 +#define FUSE_OPT_KEY_KEEP -3 +#define FUSE_OPT_KEY_DISCARD -4 + +#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } + +struct fuse_opt +{ + const char *templ; + unsigned int offset; + int value; +}; + +struct fuse_args +{ + int argc; + char **argv; + int allocated; +}; + +typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, + struct fuse_args *outargs); + +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_parse)(struct fsp_fuse_env *env, + struct fuse_args *args, void *data, + const struct fuse_opt opts[], fuse_opt_proc_t proc); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_add_arg)(struct fsp_fuse_env *env, + struct fuse_args *args, const char *arg); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_insert_arg)(struct fsp_fuse_env *env, + struct fuse_args *args, int pos, const char *arg); +FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_opt_free_args)(struct fsp_fuse_env *env, + struct fuse_args *args); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_add_opt)(struct fsp_fuse_env *env, + char **opts, const char *opt); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_add_opt_escaped)(struct fsp_fuse_env *env, + char **opts, const char *opt); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse_opt_match)(struct fsp_fuse_env *env, + const struct fuse_opt opts[], const char *opt); + +FSP_FUSE_SYM( +int fuse_opt_parse(struct fuse_args *args, void *data, + const struct fuse_opt opts[], fuse_opt_proc_t proc), +{ + return FSP_FUSE_API_CALL(fsp_fuse_opt_parse) + (fsp_fuse_env(), args, data, opts, proc); +}) + +FSP_FUSE_SYM( +int fuse_opt_add_arg(struct fuse_args *args, const char *arg), +{ + return FSP_FUSE_API_CALL(fsp_fuse_opt_add_arg) + (fsp_fuse_env(), args, arg); +}) + +FSP_FUSE_SYM( +int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg), +{ + return FSP_FUSE_API_CALL(fsp_fuse_opt_insert_arg) + (fsp_fuse_env(), args, pos, arg); +}) + +FSP_FUSE_SYM( +void fuse_opt_free_args(struct fuse_args *args), +{ + FSP_FUSE_API_CALL(fsp_fuse_opt_free_args) + (fsp_fuse_env(), args); +}) + +FSP_FUSE_SYM( +int fuse_opt_add_opt(char **opts, const char *opt), +{ + return FSP_FUSE_API_CALL(fsp_fuse_opt_add_opt) + (fsp_fuse_env(), opts, opt); +}) + +FSP_FUSE_SYM( +int fuse_opt_add_opt_escaped(char **opts, const char *opt), +{ + return FSP_FUSE_API_CALL(fsp_fuse_opt_add_opt_escaped) + (fsp_fuse_env(), opts, opt); +}) + +FSP_FUSE_SYM( +int fuse_opt_match(const struct fuse_opt opts[], const char *opt), +{ + return FSP_FUSE_API_CALL(fsp_fuse_opt_match) + (fsp_fuse_env(), opts, opt); +}) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/inc/fuse/winfsp_fuse.h b/3rd_party/winfsp-1.10/inc/fuse/winfsp_fuse.h new file mode 100644 index 00000000..37d14063 --- /dev/null +++ b/3rd_party/winfsp-1.10/inc/fuse/winfsp_fuse.h @@ -0,0 +1,434 @@ +/** + * @file fuse/winfsp_fuse.h + * WinFsp FUSE compatible API. + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef FUSE_WINFSP_FUSE_H_INCLUDED +#define FUSE_WINFSP_FUSE_H_INCLUDED + +#include +#include +#if !defined(WINFSP_DLL_INTERNAL) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(FSP_FUSE_API) +#if defined(WINFSP_DLL_INTERNAL) +#define FSP_FUSE_API __declspec(dllexport) +#else +#define FSP_FUSE_API __declspec(dllimport) +#endif +#endif + +#if !defined(FSP_FUSE_API_NAME) +#define FSP_FUSE_API_NAME(n) (n) +#endif + +#if !defined(FSP_FUSE_API_CALL) +#define FSP_FUSE_API_CALL(n) (n) +#endif + +#if !defined(FSP_FUSE_SYM) +#if !defined(CYGFUSE) +#define FSP_FUSE_SYM(proto, ...) static inline proto { __VA_ARGS__ } +#else +#define FSP_FUSE_SYM(proto, ...) proto; +#endif +#endif + +#define FSP_FUSE_DEVICE_TYPE (0x8000 | 'W' | 'F' * 0x100) /* DeviceIoControl -> ioctl */ +#define FSP_FUSE_CTLCODE_FROM_IOCTL(cmd)\ + (FSP_FUSE_DEVICE_TYPE << 16) | (((cmd) & 0x0fff) << 2) +#define FSP_FUSE_IOCTL(cmd, isiz, osiz) \ + ( \ + (((osiz) != 0) << 31) | \ + (((isiz) != 0) << 30) | \ + (((isiz) | (osiz)) << 16) | \ + (cmd) \ + ) + +/* + * FUSE uses a number of types (notably: struct stat) that are OS specific. + * Furthermore there are sometimes multiple definitions of the same type even + * within the same OS. This is certainly true on Windows, where these types + * are not even native. + * + * For this reason we will define our own fuse_* types which represent the + * types as the WinFsp DLL expects to see them. We will define these types + * to be compatible with the equivalent Cygwin types as we want WinFsp-FUSE + * to be usable from Cygwin. + */ + +#define FSP_FUSE_STAT_FIELD_DEFN \ + fuse_dev_t st_dev; \ + fuse_ino_t st_ino; \ + fuse_mode_t st_mode; \ + fuse_nlink_t st_nlink; \ + fuse_uid_t st_uid; \ + fuse_gid_t st_gid; \ + fuse_dev_t st_rdev; \ + fuse_off_t st_size; \ + struct fuse_timespec st_atim; \ + struct fuse_timespec st_mtim; \ + struct fuse_timespec st_ctim; \ + fuse_blksize_t st_blksize; \ + fuse_blkcnt_t st_blocks; \ + struct fuse_timespec st_birthtim; +#define FSP_FUSE_STAT_EX_FIELD_DEFN \ + FSP_FUSE_STAT_FIELD_DEFN \ + uint32_t st_flags; \ + uint32_t st_reserved32[3]; \ + uint64_t st_reserved64[2]; + +#if defined(_WIN64) || defined(_WIN32) + +typedef uint32_t fuse_uid_t; +typedef uint32_t fuse_gid_t; +typedef int32_t fuse_pid_t; + +typedef uint32_t fuse_dev_t; +typedef uint64_t fuse_ino_t; +typedef uint32_t fuse_mode_t; +typedef uint16_t fuse_nlink_t; +typedef int64_t fuse_off_t; + +#if defined(_WIN64) +typedef uint64_t fuse_fsblkcnt_t; +typedef uint64_t fuse_fsfilcnt_t; +#else +typedef uint32_t fuse_fsblkcnt_t; +typedef uint32_t fuse_fsfilcnt_t; +#endif +typedef int32_t fuse_blksize_t; +typedef int64_t fuse_blkcnt_t; + +#if defined(_WIN64) +struct fuse_utimbuf +{ + int64_t actime; + int64_t modtime; +}; +struct fuse_timespec +{ + int64_t tv_sec; + int64_t tv_nsec; +}; +#else +struct fuse_utimbuf +{ + int32_t actime; + int32_t modtime; +}; +struct fuse_timespec +{ + int32_t tv_sec; + int32_t tv_nsec; +}; +#endif + +#if !defined(FSP_FUSE_USE_STAT_EX) +struct fuse_stat +{ + FSP_FUSE_STAT_FIELD_DEFN +}; +#else +struct fuse_stat +{ + FSP_FUSE_STAT_EX_FIELD_DEFN +}; +#endif + +#if defined(_WIN64) +struct fuse_statvfs +{ + uint64_t f_bsize; + uint64_t f_frsize; + fuse_fsblkcnt_t f_blocks; + fuse_fsblkcnt_t f_bfree; + fuse_fsblkcnt_t f_bavail; + fuse_fsfilcnt_t f_files; + fuse_fsfilcnt_t f_ffree; + fuse_fsfilcnt_t f_favail; + uint64_t f_fsid; + uint64_t f_flag; + uint64_t f_namemax; +}; +#else +struct fuse_statvfs +{ + uint32_t f_bsize; + uint32_t f_frsize; + fuse_fsblkcnt_t f_blocks; + fuse_fsblkcnt_t f_bfree; + fuse_fsblkcnt_t f_bavail; + fuse_fsfilcnt_t f_files; + fuse_fsfilcnt_t f_ffree; + fuse_fsfilcnt_t f_favail; + uint32_t f_fsid; + uint32_t f_flag; + uint32_t f_namemax; +}; +#endif + +struct fuse_flock +{ + int16_t l_type; + int16_t l_whence; + fuse_off_t l_start; + fuse_off_t l_len; + fuse_pid_t l_pid; +}; + +#if defined(WINFSP_DLL_INTERNAL) +#define FSP_FUSE_ENV_INIT \ + { \ + 'W', \ + MemAlloc, MemFree, \ + fsp_fuse_daemonize, \ + fsp_fuse_set_signal_handlers, \ + 0/*conv_to_win_path*/, \ + 0/*winpid_to_pid*/, \ + { 0 }, \ + } +#else +#define FSP_FUSE_ENV_INIT \ + { \ + 'W', \ + malloc, free, \ + fsp_fuse_daemonize, \ + fsp_fuse_set_signal_handlers, \ + 0/*conv_to_win_path*/, \ + 0/*winpid_to_pid*/, \ + { 0 }, \ + } +#endif + +#elif defined(__CYGWIN__) + +#include +#include +#include +#include +#include +#include +#include + +#define fuse_uid_t uid_t +#define fuse_gid_t gid_t +#define fuse_pid_t pid_t + +#define fuse_dev_t dev_t +#define fuse_ino_t ino_t +#define fuse_mode_t mode_t +#define fuse_nlink_t nlink_t +#define fuse_off_t off_t + +#define fuse_fsblkcnt_t fsblkcnt_t +#define fuse_fsfilcnt_t fsfilcnt_t +#define fuse_blksize_t blksize_t +#define fuse_blkcnt_t blkcnt_t + +#define fuse_utimbuf utimbuf +#define fuse_timespec timespec + +#if !defined(FSP_FUSE_USE_STAT_EX) +#define fuse_stat stat +#else +struct fuse_stat +{ + FSP_FUSE_STAT_EX_FIELD_DEFN +}; +#endif +#define fuse_statvfs statvfs +#define fuse_flock flock + +#define FSP_FUSE_ENV_INIT \ + { \ + 'C', \ + malloc, free, \ + fsp_fuse_daemonize, \ + fsp_fuse_set_signal_handlers, \ + fsp_fuse_conv_to_win_path, \ + fsp_fuse_winpid_to_pid, \ + { 0 }, \ + } + +/* + * Note that long is 8 bytes long in Cygwin64 and 4 bytes long in Win64. + * For this reason we avoid using long anywhere in these headers. + */ + +#else +#error unsupported environment +#endif + +struct fuse_stat_ex +{ + FSP_FUSE_STAT_EX_FIELD_DEFN +}; + +struct fsp_fuse_env +{ + unsigned environment; + void *(*memalloc)(size_t); + void (*memfree)(void *); + int (*daemonize)(int); + int (*set_signal_handlers)(void *); + char *(*conv_to_win_path)(const char *); + fuse_pid_t (*winpid_to_pid)(uint32_t); + void (*reserved[2])(); +}; + +FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse_signal_handler)(int sig); + +#if defined(_WIN64) || defined(_WIN32) + +static inline int fsp_fuse_daemonize(int foreground) +{ + (void)foreground; + return 0; +} + +static inline int fsp_fuse_set_signal_handlers(void *se) +{ + (void)se; + return 0; +} + +#elif defined(__CYGWIN__) + +static inline int fsp_fuse_daemonize(int foreground) +{ + int daemon(int nochdir, int noclose); + int chdir(const char *path); + + if (!foreground) + { + if (-1 == daemon(0, 0)) + return -1; + } + else + chdir("/"); + + return 0; +} + +static inline void *fsp_fuse_signal_thread(void *psigmask) +{ + int sig; + + if (0 == sigwait((sigset_t *)psigmask, &sig)) + FSP_FUSE_API_CALL(fsp_fuse_signal_handler)(sig); + + return 0; +} + +static inline int fsp_fuse_set_signal_handlers(void *se) +{ +#define FSP_FUSE_SET_SIGNAL_HANDLER(sig, newha)\ + if (-1 != sigaction((sig), 0, &oldsa) &&\ + oldsa.sa_handler == (se ? SIG_DFL : (newha)))\ + {\ + newsa.sa_handler = se ? (newha) : SIG_DFL;\ + sigaction((sig), &newsa, 0);\ + } +#define FSP_FUSE_SIGADDSET(sig)\ + if (-1 != sigaction((sig), 0, &oldsa) &&\ + oldsa.sa_handler == SIG_DFL)\ + sigaddset(&sigmask, (sig)); + + static sigset_t sigmask; + static pthread_t sigthr; + struct sigaction oldsa, newsa; + + // memset instead of initializer to avoid GCC -Wmissing-field-initializers warning + memset(&newsa, 0, sizeof newsa); + + if (0 != se) + { + if (0 == sigthr) + { + FSP_FUSE_SET_SIGNAL_HANDLER(SIGPIPE, SIG_IGN); + + sigemptyset(&sigmask); + FSP_FUSE_SIGADDSET(SIGHUP); + FSP_FUSE_SIGADDSET(SIGINT); + FSP_FUSE_SIGADDSET(SIGTERM); + if (0 != pthread_sigmask(SIG_BLOCK, &sigmask, 0)) + return -1; + + if (0 != pthread_create(&sigthr, 0, fsp_fuse_signal_thread, &sigmask)) + return -1; + } + } + else + { + if (0 != sigthr) + { + pthread_cancel(sigthr); + pthread_join(sigthr, 0); + sigthr = 0; + + if (0 != pthread_sigmask(SIG_UNBLOCK, &sigmask, 0)) + return -1; + sigemptyset(&sigmask); + + FSP_FUSE_SET_SIGNAL_HANDLER(SIGPIPE, SIG_IGN); + } + } + + return 0; + +#undef FSP_FUSE_SIGADDSET +#undef FSP_FUSE_SET_SIGNAL_HANDLER +} + +static inline char *fsp_fuse_conv_to_win_path(const char *path) +{ + void *cygwin_create_path(unsigned, const void *); + return (char *)cygwin_create_path( + 0/*CCP_POSIX_TO_WIN_A*/ | 0x100/*CCP_RELATIVE*/, + path); +} + +static inline fuse_pid_t fsp_fuse_winpid_to_pid(uint32_t winpid) +{ + pid_t cygwin_winpid_to_pid(int winpid); + pid_t pid = cygwin_winpid_to_pid(winpid); + return -1 != pid ? pid : (fuse_pid_t)winpid; +} +#endif + + +static inline struct fsp_fuse_env *fsp_fuse_env(void) +{ + static struct fsp_fuse_env env = FSP_FUSE_ENV_INIT; + return &env; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/inc/fuse3/fuse.h b/3rd_party/winfsp-1.10/inc/fuse3/fuse.h new file mode 100644 index 00000000..85314c2a --- /dev/null +++ b/3rd_party/winfsp-1.10/inc/fuse3/fuse.h @@ -0,0 +1,338 @@ +/** + * @file fuse3/fuse.h + * WinFsp FUSE3 compatible API. + * + * This file is derived from libfuse/include/fuse.h: + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef FUSE_H_ +#define FUSE_H_ + +#include "fuse_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct fuse3; + +enum fuse3_readdir_flags +{ + FUSE_READDIR_PLUS = (1 << 0), +}; + +enum fuse3_fill_dir_flags +{ + FUSE_FILL_DIR_PLUS = (1 << 1), +}; + +typedef int (*fuse3_fill_dir_t)(void *buf, const char *name, + const struct fuse_stat *stbuf, fuse_off_t off, + enum fuse3_fill_dir_flags flags); + +struct fuse3_config +{ + int set_gid; + unsigned int gid; + int set_uid; + unsigned int uid; + int set_mode; + unsigned int umask; + double entry_timeout; + double negative_timeout; + double attr_timeout; + int intr; + int intr_signal; + int remember; + int hard_remove; + int use_ino; + int readdir_ino; + int direct_io; + int kernel_cache; + int auto_cache; + int ac_attr_timeout_set; + double ac_attr_timeout; + int nullpath_ok; + /* private */ + int show_help; + char *modules; + int debug; +}; + +struct fuse3_operations +{ + /* S - supported by WinFsp */ + /* S */ int (*getattr)(const char *path, struct fuse_stat *stbuf, + struct fuse3_file_info *fi); + /* S */ int (*readlink)(const char *path, char *buf, size_t size); + /* S */ int (*mknod)(const char *path, fuse_mode_t mode, fuse_dev_t dev); + /* S */ int (*mkdir)(const char *path, fuse_mode_t mode); + /* S */ int (*unlink)(const char *path); + /* S */ int (*rmdir)(const char *path); + /* S */ int (*symlink)(const char *dstpath, const char *srcpath); + /* S */ int (*rename)(const char *oldpath, const char *newpath, unsigned int flags); + /* _ */ int (*link)(const char *srcpath, const char *dstpath); + /* S */ int (*chmod)(const char *path, fuse_mode_t mode, + struct fuse3_file_info *fi); + /* S */ int (*chown)(const char *path, fuse_uid_t uid, fuse_gid_t gid, + struct fuse3_file_info *fi); + /* S */ int (*truncate)(const char *path, fuse_off_t size, + struct fuse3_file_info *fi); + /* S */ int (*open)(const char *path, struct fuse3_file_info *fi); + /* S */ int (*read)(const char *path, char *buf, size_t size, fuse_off_t off, + struct fuse3_file_info *fi); + /* S */ int (*write)(const char *path, const char *buf, size_t size, fuse_off_t off, + struct fuse3_file_info *fi); + /* S */ int (*statfs)(const char *path, struct fuse_statvfs *stbuf); + /* S */ int (*flush)(const char *path, struct fuse3_file_info *fi); + /* S */ int (*release)(const char *path, struct fuse3_file_info *fi); + /* S */ int (*fsync)(const char *path, int datasync, struct fuse3_file_info *fi); + /* S */ int (*setxattr)(const char *path, const char *name, const char *value, size_t size, + int flags); + /* S */ int (*getxattr)(const char *path, const char *name, char *value, size_t size); + /* S */ int (*listxattr)(const char *path, char *namebuf, size_t size); + /* S */ int (*removexattr)(const char *path, const char *name); + /* S */ int (*opendir)(const char *path, struct fuse3_file_info *fi); + /* S */ int (*readdir)(const char *path, void *buf, fuse3_fill_dir_t filler, fuse_off_t off, + struct fuse3_file_info *fi, enum fuse3_readdir_flags); + /* S */ int (*releasedir)(const char *path, struct fuse3_file_info *fi); + /* S */ int (*fsyncdir)(const char *path, int datasync, struct fuse3_file_info *fi); + /* S */ void *(*init)(struct fuse3_conn_info *conn, + struct fuse3_config *conf); + /* S */ void (*destroy)(void *data); + /* _ */ int (*access)(const char *path, int mask); + /* S */ int (*create)(const char *path, fuse_mode_t mode, struct fuse3_file_info *fi); + /* _ */ int (*lock)(const char *path, + struct fuse3_file_info *fi, int cmd, struct fuse_flock *lock); + /* S */ int (*utimens)(const char *path, const struct fuse_timespec tv[2], + struct fuse3_file_info *fi); + /* _ */ int (*bmap)(const char *path, size_t blocksize, uint64_t *idx); + /* S */ int (*ioctl)(const char *path, int cmd, void *arg, struct fuse3_file_info *fi, + unsigned int flags, void *data); + /* _ */ int (*poll)(const char *path, struct fuse3_file_info *fi, + struct fuse3_pollhandle *ph, unsigned *reventsp); + /* _ */ int (*write_buf)(const char *path, + struct fuse3_bufvec *buf, fuse_off_t off, struct fuse3_file_info *fi); + /* _ */ int (*read_buf)(const char *path, + struct fuse3_bufvec **bufp, size_t size, fuse_off_t off, struct fuse3_file_info *fi); + /* _ */ int (*flock)(const char *path, struct fuse3_file_info *, int op); + /* _ */ int (*fallocate)(const char *path, int mode, fuse_off_t off, fuse_off_t len, + struct fuse3_file_info *fi); +}; + +struct fuse3_context +{ + struct fuse3 *fuse; + fuse_uid_t uid; + fuse_gid_t gid; + fuse_pid_t pid; + void *private_data; + fuse_mode_t umask; +}; + +#define fuse_main(argc, argv, ops, data)\ + fuse3_main_real(argc, argv, ops, sizeof *(ops), data) + +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse3_main_real)(struct fsp_fuse_env *env, + int argc, char *argv[], + const struct fuse3_operations *ops, size_t opsize, void *data); +FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse3_lib_help)(struct fsp_fuse_env *env, + struct fuse_args *args); +FSP_FUSE_API struct fuse3 *FSP_FUSE_API_NAME(fsp_fuse3_new_30)(struct fsp_fuse_env *env, + struct fuse_args *args, + const struct fuse3_operations *ops, size_t opsize, void *data); +FSP_FUSE_API struct fuse3 *FSP_FUSE_API_NAME(fsp_fuse3_new)(struct fsp_fuse_env *env, + struct fuse_args *args, + const struct fuse3_operations *ops, size_t opsize, void *data); +FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse3_destroy)(struct fsp_fuse_env *env, + struct fuse3 *f); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse3_mount)(struct fsp_fuse_env *env, + struct fuse3 *f, const char *mountpoint); +FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse3_unmount)(struct fsp_fuse_env *env, + struct fuse3 *f); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse3_loop)(struct fsp_fuse_env *env, + struct fuse3 *f); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse3_loop_mt_31)(struct fsp_fuse_env *env, + struct fuse3 *f, int clone_fd); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse3_loop_mt)(struct fsp_fuse_env *env, + struct fuse3 *f, struct fuse3_loop_config *config); +FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse3_exit)(struct fsp_fuse_env *env, + struct fuse3 *f); +FSP_FUSE_API struct fuse3_context *FSP_FUSE_API_NAME(fsp_fuse3_get_context)(struct fsp_fuse_env *env); + +FSP_FUSE_SYM( +int fuse3_main_real(int argc, char *argv[], + const struct fuse3_operations *ops, size_t opsize, void *data), +{ + return FSP_FUSE_API_CALL(fsp_fuse3_main_real) + (fsp_fuse_env(), argc, argv, ops, opsize, data); +}) + +FSP_FUSE_SYM( +void fuse3_lib_help(struct fuse_args *args), +{ + FSP_FUSE_API_CALL(fsp_fuse3_lib_help) + (fsp_fuse_env(), args); +}) + +#if FUSE_USE_VERSION == 30 +FSP_FUSE_SYM( +struct fuse3 *fuse3_new_30(struct fuse_args *args, + const struct fuse3_operations *ops, size_t opsize, void *data), +{ + return FSP_FUSE_API_CALL(fsp_fuse3_new_30) + (fsp_fuse_env(), args, ops, opsize, data); +}) +#define fuse_new(args, op, size, data)\ + fuse3_new_30(args, op, size, data) + +#else +FSP_FUSE_SYM( +struct fuse3 *fuse3_new(struct fuse_args *args, + const struct fuse3_operations *ops, size_t opsize, void *data), +{ + return FSP_FUSE_API_CALL(fsp_fuse3_new) + (fsp_fuse_env(), args, ops, opsize, data); +}) +#endif + +FSP_FUSE_SYM( +void fuse3_destroy(struct fuse3 *f), +{ + FSP_FUSE_API_CALL(fsp_fuse3_destroy) + (fsp_fuse_env(), f); +}) + +FSP_FUSE_SYM( +int fuse3_mount(struct fuse3 *f, const char *mountpoint), +{ + return FSP_FUSE_API_CALL(fsp_fuse3_mount) + (fsp_fuse_env(), f, mountpoint); +}) + +FSP_FUSE_SYM( +void fuse3_unmount(struct fuse3 *f), +{ + FSP_FUSE_API_CALL(fsp_fuse3_unmount) + (fsp_fuse_env(), f); +}) + +FSP_FUSE_SYM( +int fuse3_loop(struct fuse3 *f), +{ + return FSP_FUSE_API_CALL(fsp_fuse3_loop) + (fsp_fuse_env(), f); +}) + +#if FUSE_USE_VERSION < 32 +FSP_FUSE_SYM( +int fuse3_loop_mt_31(struct fuse3 *f, int clone_fd), +{ + return FSP_FUSE_API_CALL(fsp_fuse3_loop_mt_31) + (fsp_fuse_env(), f, clone_fd); +}) +#define fuse_loop_mt(f, clone_fd)\ + fuse3_loop_mt_31(f, clone_fd) + +#else +FSP_FUSE_SYM( +int fuse3_loop_mt(struct fuse3 *f, struct fuse3_loop_config *config), +{ + return FSP_FUSE_API_CALL(fsp_fuse3_loop_mt) + (fsp_fuse_env(), f, config); +}) +#endif + +FSP_FUSE_SYM( +void fuse3_exit(struct fuse3 *f), +{ + FSP_FUSE_API_CALL(fsp_fuse3_exit) + (fsp_fuse_env(), f); +}) + +FSP_FUSE_SYM( +struct fuse3_context *fuse3_get_context(void), +{ + return FSP_FUSE_API_CALL(fsp_fuse3_get_context) + (fsp_fuse_env()); +}) + +FSP_FUSE_SYM( +int fuse3_getgroups(int size, fuse_gid_t list[]), +{ + (void)size; + (void)list; + return -ENOSYS; +}) + +FSP_FUSE_SYM( +int fuse3_interrupted(void), +{ + return 0; +}) + +FSP_FUSE_SYM( +int fuse3_invalidate_path(struct fuse3 *f, const char *path), +{ + (void)f; + (void)path; + return -ENOENT; +}) + +FSP_FUSE_SYM( +int fuse3_notify_poll(struct fuse3_pollhandle *ph), +{ + (void)ph; + return 0; +}) + +FSP_FUSE_SYM( +int fuse3_start_cleanup_thread(struct fuse3 *f), +{ + (void)f; + return 0; +}) + +FSP_FUSE_SYM( +void fuse3_stop_cleanup_thread(struct fuse3 *f), +{ + (void)f; +}) + +FSP_FUSE_SYM( +int fuse3_clean_cache(struct fuse3 *f), +{ + (void)f; + return 600; +}) + +FSP_FUSE_SYM( +struct fuse3_session *fuse3_get_session(struct fuse3 *f), +{ + return (struct fuse3_session *)f; +}) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/inc/fuse3/fuse_common.h b/3rd_party/winfsp-1.10/inc/fuse3/fuse_common.h new file mode 100644 index 00000000..953be35f --- /dev/null +++ b/3rd_party/winfsp-1.10/inc/fuse3/fuse_common.h @@ -0,0 +1,238 @@ +/** + * @file fuse3/fuse_common.h + * WinFsp FUSE3 compatible API. + * + * This file is derived from libfuse/include/fuse_common.h: + * FUSE: Filesystem in Userspace + * Copyright (C) 2001-2007 Miklos Szeredi + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef FUSE_COMMON_H_ +#define FUSE_COMMON_H_ + +#include "winfsp_fuse.h" +#if !defined(WINFSP_DLL_INTERNAL) +#include "fuse_opt.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define FUSE_MAJOR_VERSION 3 +#define FUSE_MINOR_VERSION 2 +#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) +#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) + +#define FUSE_CAP_ASYNC_READ (1 << 0) +#define FUSE_CAP_POSIX_LOCKS (1 << 1) +#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3) +#define FUSE_CAP_EXPORT_SUPPORT (1 << 4) +#define FUSE_CAP_DONT_MASK (1 << 6) +#define FUSE_CAP_SPLICE_WRITE (1 << 7) +#define FUSE_CAP_SPLICE_MOVE (1 << 8) +#define FUSE_CAP_SPLICE_READ (1 << 9) +#define FUSE_CAP_FLOCK_LOCKS (1 << 10) +#define FUSE_CAP_IOCTL_DIR (1 << 11) +#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12) +#define FUSE_CAP_READDIRPLUS (1 << 13) +#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14) +#define FUSE_CAP_ASYNC_DIO (1 << 15) +#define FUSE_CAP_WRITEBACK_CACHE (1 << 16) +#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17) +#define FUSE_CAP_PARALLEL_DIROPS (1 << 18) +#define FUSE_CAP_POSIX_ACL (1 << 19) +#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20) +#define FUSE_CAP_ALLOCATE (1 << 27) /* reserved (OSXFUSE) */ +#define FUSE_CAP_EXCHANGE_DATA (1 << 28) /* reserved (OSXFUSE) */ +#define FUSE_CAP_CASE_INSENSITIVE (1 << 29) /* file system is case insensitive */ +#define FUSE_CAP_VOL_RENAME (1 << 30) /* reserved (OSXFUSE) */ +#define FUSE_CAP_XTIMES (1 << 31) /* reserved (OSXFUSE) */ + +#define FSP_FUSE_CAP_CASE_INSENSITIVE FUSE_CAP_CASE_INSENSITIVE + +#define FUSE_IOCTL_COMPAT (1 << 0) +#define FUSE_IOCTL_UNRESTRICTED (1 << 1) +#define FUSE_IOCTL_RETRY (1 << 2) +#define FUSE_IOCTL_DIR (1 << 4) +#define FUSE_IOCTL_MAX_IOV 256 + +#define FUSE_BUFVEC_INIT(s) \ + ((struct fuse3_bufvec){ 1, 0, 0, { {s, (enum fuse3_buf_flags)0, 0, -1, 0} } }) + +struct fuse3_file_info +{ + int flags; + unsigned int writepage:1; + unsigned int direct_io:1; + unsigned int keep_cache:1; + unsigned int flush:1; + unsigned int nonseekable:1; + unsigned int flock_release:1; + unsigned int padding:27; + uint64_t fh; + uint64_t lock_owner; + uint32_t poll_events; +}; + +struct fuse3_loop_config +{ + int clone_fd; + unsigned int max_idle_threads; +}; + +struct fuse3_conn_info +{ + unsigned proto_major; + unsigned proto_minor; + unsigned max_write; + unsigned max_read; + unsigned max_readahead; + unsigned capable; + unsigned want; + unsigned max_background; + unsigned congestion_threshold; + unsigned time_gran; + unsigned reserved[22]; +}; + +enum fuse3_buf_flags +{ + FUSE_BUF_IS_FD = (1 << 1), + FUSE_BUF_FD_SEEK = (1 << 2), + FUSE_BUF_FD_RETRY = (1 << 3), +}; + +enum fuse3_buf_copy_flags +{ + FUSE_BUF_NO_SPLICE = (1 << 1), + FUSE_BUF_FORCE_SPLICE = (1 << 2), + FUSE_BUF_SPLICE_MOVE = (1 << 3), + FUSE_BUF_SPLICE_NONBLOCK = (1 << 4), +}; + +struct fuse3_buf +{ + size_t size; + enum fuse3_buf_flags flags; + void *mem; + int fd; + fuse_off_t pos; +}; + +struct fuse3_bufvec +{ + size_t count; + size_t idx; + size_t off; + struct fuse3_buf buf[1]; +}; + +struct fuse3_session; +struct fuse3_pollhandle; +struct fuse3_conn_info_opts; + +FSP_FUSE_API struct fuse3_conn_info_opts *FSP_FUSE_API_NAME(fsp_fuse3_parse_conn_info_opts)( + struct fsp_fuse_env *env, + struct fuse_args *args); +FSP_FUSE_API void FSP_FUSE_API_NAME(fsp_fuse3_apply_conn_info_opts)(struct fsp_fuse_env *env, + struct fuse3_conn_info_opts *opts, struct fuse3_conn_info *conn); +FSP_FUSE_API int FSP_FUSE_API_NAME(fsp_fuse3_version)(struct fsp_fuse_env *env); +FSP_FUSE_API const char *FSP_FUSE_API_NAME(fsp_fuse3_pkgversion)(struct fsp_fuse_env *env); +FSP_FUSE_API int32_t FSP_FUSE_API_NAME(fsp_fuse_ntstatus_from_errno)(struct fsp_fuse_env *env, + int err); + +FSP_FUSE_SYM( +struct fuse3_conn_info_opts* fuse3_parse_conn_info_opts( + struct fuse_args *args), +{ + return FSP_FUSE_API_CALL(fsp_fuse3_parse_conn_info_opts) + (fsp_fuse_env(), args); +}) + +FSP_FUSE_SYM( +void fuse3_apply_conn_info_opts( + struct fuse3_conn_info_opts *opts, struct fuse3_conn_info *conn), +{ + FSP_FUSE_API_CALL(fsp_fuse3_apply_conn_info_opts) + (fsp_fuse_env(), opts, conn); +}) + +FSP_FUSE_SYM( +int fuse3_version(void), +{ + return FSP_FUSE_API_CALL(fsp_fuse3_version) + (fsp_fuse_env()); +}) + +FSP_FUSE_SYM( +const char *fuse3_pkgversion(void), +{ + return FSP_FUSE_API_CALL(fsp_fuse3_pkgversion) + (fsp_fuse_env()); +}) + +FSP_FUSE_SYM( +void fuse3_pollhandle_destroy(struct fuse3_pollhandle *ph), +{ + (void)ph; +}) + +FSP_FUSE_SYM( +size_t fuse3_buf_size(const struct fuse3_bufvec *bufv), +{ + (void)bufv; + return 0; +}) + +FSP_FUSE_SYM( +ssize_t fuse3_buf_copy(struct fuse3_bufvec *dst, struct fuse3_bufvec *src, + enum fuse3_buf_copy_flags flags), +{ + (void)dst; + (void)src; + (void)flags; + return 0; +}) + +FSP_FUSE_SYM( +int fuse3_daemonize(int foreground), +{ + return fsp_fuse_daemonize(foreground); +}) + +FSP_FUSE_SYM( +int fuse3_set_signal_handlers(struct fuse3_session *se), +{ + return fsp_fuse_set_signal_handlers(se); +}) + +FSP_FUSE_SYM( +void fuse3_remove_signal_handlers(struct fuse3_session *se), +{ + (void)se; + fsp_fuse_set_signal_handlers(0); +}) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/inc/fuse3/fuse_opt.h b/3rd_party/winfsp-1.10/inc/fuse3/fuse_opt.h new file mode 100644 index 00000000..d6221856 --- /dev/null +++ b/3rd_party/winfsp-1.10/inc/fuse3/fuse_opt.h @@ -0,0 +1,23 @@ +/** + * @file fuse3/fuse_opt.h + * WinFsp FUSE3 compatible API. + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#include "../fuse/fuse_opt.h" diff --git a/3rd_party/winfsp-1.10/inc/fuse3/winfsp_fuse.h b/3rd_party/winfsp-1.10/inc/fuse3/winfsp_fuse.h new file mode 100644 index 00000000..cf3a66be --- /dev/null +++ b/3rd_party/winfsp-1.10/inc/fuse3/winfsp_fuse.h @@ -0,0 +1,82 @@ +/** + * @file fuse3/winfsp_fuse.h + * WinFsp FUSE3 compatible API. + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef FUSE3_WINFSP_FUSE_H_INCLUDED +#define FUSE3_WINFSP_FUSE_H_INCLUDED + +#include "../fuse/winfsp_fuse.h" + +#if defined(_WIN64) || defined(_WIN32) +typedef intptr_t ssize_t; +#endif + +#if !defined(WINFSP_DLL_INTERNAL) +#define fuse3 fuse +#define fuse3_apply_conn_info_opts fuse_apply_conn_info_opts +#define fuse3_buf fuse_buf +#define fuse3_buf_copy fuse_buf_copy +#define fuse3_buf_copy_flags fuse_buf_copy_flags +#define fuse3_buf_flags fuse_buf_flags +#define fuse3_buf_size fuse_buf_size +#define fuse3_bufvec fuse_bufvec +#define fuse3_clean_cache fuse_clean_cache +#define fuse3_config fuse_config +#define fuse3_conn_info fuse_conn_info +#define fuse3_conn_info_opts fuse_conn_info_opts +#define fuse3_context fuse_context +#define fuse3_daemonize fuse_daemonize +#define fuse3_destroy fuse_destroy +#define fuse3_exit fuse_exit +#define fuse3_file_info fuse_file_info +#define fuse3_fill_dir_flags fuse_fill_dir_flags +#define fuse3_fill_dir_t fuse_fill_dir_t +#define fuse3_get_context fuse_get_context +#define fuse3_get_session fuse_get_session +#define fuse3_getgroups fuse_getgroups +#define fuse3_interrupted fuse_interrupted +#define fuse3_invalidate_path fuse_invalidate_path +#define fuse3_lib_help fuse_lib_help +#define fuse3_loop fuse_loop +#define fuse3_loop_config fuse_loop_config +#define fuse3_loop_mt fuse_loop_mt +#define fuse3_loop_mt_31 fuse_loop_mt_31 +#define fuse3_main_real fuse_main_real +#define fuse3_mount fuse_mount +#define fuse3_new fuse_new +#define fuse3_new_30 fuse_new_30 +#define fuse3_notify_poll fuse_notify_poll +#define fuse3_operations fuse_operations +#define fuse3_parse_conn_info_opts fuse_parse_conn_info_opts +#define fuse3_pkgversion fuse_pkgversion +#define fuse3_pollhandle fuse_pollhandle +#define fuse3_pollhandle_destroy fuse_pollhandle_destroy +#define fuse3_readdir_flags fuse_readdir_flags +#define fuse3_remove_signal_handlers fuse_remove_signal_handlers +#define fuse3_session fuse_session +#define fuse3_set_signal_handlers fuse_set_signal_handlers +#define fuse3_start_cleanup_thread fuse_start_cleanup_thread +#define fuse3_stop_cleanup_thread fuse_stop_cleanup_thread +#define fuse3_unmount fuse_unmount +#define fuse3_version fuse_version +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/inc/winfsp/fsctl.h b/3rd_party/winfsp-1.10/inc/winfsp/fsctl.h new file mode 100644 index 00000000..0eec0da7 --- /dev/null +++ b/3rd_party/winfsp-1.10/inc/winfsp/fsctl.h @@ -0,0 +1,690 @@ +/** + * @file winfsp/fsctl.h + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef WINFSP_FSCTL_H_INCLUDED +#define WINFSP_FSCTL_H_INCLUDED + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* static_assert is a C++11 feature, but seems to work with C on MSVC 2015 */ +#if defined(WINFSP_SYS_INTERNAL) || defined(WINFSP_DLL_INTERNAL) +#define FSP_FSCTL_STATIC_ASSERT(e,m) static_assert(e,m) +#else +#define FSP_FSCTL_STATIC_ASSERT(e,m) static_assert(1,"") +#endif + +#define FSP_FSCTL_STR(x) FSP_FSCTL_STR_(x) +#define FSP_FSCTL_STR_(x) #x +#if defined(MyProductName) +#define FSP_FSCTL_PRODUCT_NAME FSP_FSCTL_STR(MyProductName) +#else +#define FSP_FSCTL_PRODUCT_NAME "WinFsp" +#endif +#if defined(MyProductFileName) +#define FSP_FSCTL_PRODUCT_FILE_NAME FSP_FSCTL_STR(MyProductFileName) +#else +#define FSP_FSCTL_PRODUCT_FILE_NAME "winfsp" +#endif + +#define FSP_FSCTL_DRIVER_NAME FSP_FSCTL_PRODUCT_NAME +#define FSP_FSCTL_DISK_DEVICE_NAME FSP_FSCTL_DRIVER_NAME ".Disk" +#define FSP_FSCTL_NET_DEVICE_NAME FSP_FSCTL_DRIVER_NAME ".Net" +#define FSP_FSCTL_MUP_DEVICE_NAME FSP_FSCTL_DRIVER_NAME ".Mup" + +#if defined(MyFspFsctlDeviceClassGuid) +extern const __declspec(selectany) GUID FspFsctlDeviceClassGuid = MyFspFsctlDeviceClassGuid; +#else +extern const __declspec(selectany) GUID FspFsctlDeviceClassGuid = + { 0x6f9d25fa, 0x6dee, 0x4a9d, { 0x80, 0xf5, 0xe9, 0x8e, 0x14, 0xf3, 0x5e, 0x54 } }; +#endif +#if defined(MyFspFsvrtDeviceClassGuid) +extern const __declspec(selectany) GUID FspFsvrtDeviceClassGuid = MyFspFsvrtDeviceClassGuid; +#else +extern const __declspec(selectany) GUID FspFsvrtDeviceClassGuid = + { 0xb48171c3, 0xdd50, 0x4852, { 0x83, 0xa3, 0x34, 0x4c, 0x50, 0xd9, 0x3b, 0x17 } }; +#endif + +/* alignment macros */ +#define FSP_FSCTL_ALIGN_UP(x, s) (((x) + ((s) - 1L)) & ~((s) - 1L)) +#define FSP_FSCTL_DEFAULT_ALIGNMENT 8 +#define FSP_FSCTL_DEFAULT_ALIGN_UP(x) FSP_FSCTL_ALIGN_UP(x, FSP_FSCTL_DEFAULT_ALIGNMENT) +#define FSP_FSCTL_DECLSPEC_ALIGN __declspec(align(FSP_FSCTL_DEFAULT_ALIGNMENT)) + +/* fsctl device codes */ +#define FSP_FSCTL_MOUNTDEV \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'M', METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSP_FSCTL_VOLUME_NAME \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'N', METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSP_FSCTL_VOLUME_LIST \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'L', METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSP_FSCTL_TRANSACT \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'T', METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSP_FSCTL_TRANSACT_BATCH \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 't', METHOD_OUT_DIRECT, FILE_ANY_ACCESS) +#define FSP_FSCTL_STOP \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'S', METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSP_FSCTL_STOP0 \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 's', METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSP_FSCTL_NOTIFY \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'n', METHOD_NEITHER, FILE_ANY_ACCESS) + +/* fsctl internal device codes (usable only in-kernel) */ +#define FSP_FSCTL_TRANSACT_INTERNAL \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + 'I', METHOD_NEITHER, FILE_ANY_ACCESS) + +/* fsvol device codes */ +#define FSP_FSCTL_QUERY_WINFSP \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x800 + '?', METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define FSP_FSCTL_VOLUME_PARAMS_PREFIX "\\VolumeParams=" + +#define FSP_FSCTL_VOLUME_NAME_SIZE (64 * sizeof(WCHAR)) +#define FSP_FSCTL_VOLUME_PREFIX_SIZE (192 * sizeof(WCHAR)) +#define FSP_FSCTL_VOLUME_FSNAME_SIZE (16 * sizeof(WCHAR)) +#define FSP_FSCTL_VOLUME_NAME_SIZEMAX (FSP_FSCTL_VOLUME_NAME_SIZE + FSP_FSCTL_VOLUME_PREFIX_SIZE) +FSP_FSCTL_STATIC_ASSERT(FSP_FSCTL_VOLUME_NAME_SIZEMAX <= 260 * sizeof(WCHAR), + "Max volume name size is greater than MAX_PATH."); + +#define FSP_FSCTL_TRANSACT_PATH_SIZEMAX (1024 * sizeof(WCHAR)) + +#define FSP_FSCTL_TRANSACT_REQ_SIZEMAX (16 * 1024 - 64) /* 64: size for internal request header */ +#define FSP_FSCTL_TRANSACT_RSP_SIZEMAX (16 * 1024) +#define FSP_FSCTL_TRANSACT_REQ_BUFFER_SIZEMAX (FSP_FSCTL_TRANSACT_REQ_SIZEMAX - sizeof(FSP_FSCTL_TRANSACT_REQ)) +#define FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX (FSP_FSCTL_TRANSACT_RSP_SIZEMAX - sizeof(FSP_FSCTL_TRANSACT_RSP)) +#define FSP_FSCTL_TRANSACT_BATCH_BUFFER_SIZEMIN (64 * 1024) +#define FSP_FSCTL_TRANSACT_BUFFER_SIZEMIN FSP_FSCTL_TRANSACT_REQ_SIZEMAX + +#define FSP_FSCTL_TRANSACT_REQ_TOKEN_HANDLE(T) ((HANDLE)((UINT_PTR)((T) & 0xffffffff))) +#define FSP_FSCTL_TRANSACT_REQ_TOKEN_PID(T) ((UINT32)(((T) >> 32) & 0xffffffff)) + +#define FSP_FSCTL_DEVICECONTROL_SIZEMAX (4 * 1024) /* must be < FSP_FSCTL_TRANSACT_{REQ,RSP}_SIZEMAX */ + +/* marshalling */ +#pragma warning(push) +#pragma warning(disable:4200 4201) /* zero-sized array in struct/union; nameless struct/union */ +enum +{ + FspFsctlTransactReservedKind = 0, + FspFsctlTransactCreateKind, + FspFsctlTransactOverwriteKind, + FspFsctlTransactCleanupKind, + FspFsctlTransactCloseKind, + FspFsctlTransactReadKind, + FspFsctlTransactWriteKind, + FspFsctlTransactQueryInformationKind, + FspFsctlTransactSetInformationKind, + FspFsctlTransactQueryEaKind, + FspFsctlTransactSetEaKind, + FspFsctlTransactFlushBuffersKind, + FspFsctlTransactQueryVolumeInformationKind, + FspFsctlTransactSetVolumeInformationKind, + FspFsctlTransactQueryDirectoryKind, + FspFsctlTransactFileSystemControlKind, + FspFsctlTransactDeviceControlKind, + FspFsctlTransactShutdownKind, + FspFsctlTransactLockControlKind, + FspFsctlTransactQuerySecurityKind, + FspFsctlTransactSetSecurityKind, + FspFsctlTransactQueryStreamInformationKind, + FspFsctlTransactKindCount, +}; +enum +{ + FspFsctlTransactTimeoutMinimum = 1000, + FspFsctlTransactTimeoutMaximum = 10000, + FspFsctlTransactTimeoutDefault = 1000, /* DEPRECATED: default is unspecified */ + FspFsctlIrpTimeoutMinimum = 60000, + FspFsctlIrpTimeoutMaximum = 600000, + FspFsctlIrpTimeoutDefault = 300000, + FspFsctlIrpTimeoutDebug = 142, /* special value for IRP timeout testing */ + FspFsctlIrpCapacityMinimum = 100, + FspFsctlIrpCapacityMaximum = 1000, + FspFsctlIrpCapacityDefault = 1000, +}; +#define FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN\ + UINT16 Version; /* set to 0 or sizeof(FSP_FSCTL_VOLUME_PARAMS) */\ + /* volume information */\ + UINT16 SectorSize;\ + UINT16 SectorsPerAllocationUnit;\ + UINT16 MaxComponentLength; /* maximum file name component length (bytes) */\ + UINT64 VolumeCreationTime;\ + UINT32 VolumeSerialNumber;\ + /* I/O timeouts, capacity, etc. */\ + UINT32 TransactTimeout; /* DEPRECATED: (millis; 1 sec - 10 sec) */\ + UINT32 IrpTimeout; /* pending IRP timeout (millis; 1 min - 10 min) */\ + UINT32 IrpCapacity; /* maximum number of pending IRP's (100 - 1000)*/\ + UINT32 FileInfoTimeout; /* FileInfo/Security/VolumeInfo timeout (millis) */\ + /* FILE_FS_ATTRIBUTE_INFORMATION::FileSystemAttributes */\ + UINT32 CaseSensitiveSearch:1; /* file system supports case-sensitive file names */\ + UINT32 CasePreservedNames:1; /* file system preserves the case of file names */\ + UINT32 UnicodeOnDisk:1; /* file system supports Unicode in file names */\ + UINT32 PersistentAcls:1; /* file system preserves and enforces access control lists */\ + UINT32 ReparsePoints:1; /* file system supports reparse points */\ + UINT32 ReparsePointsAccessCheck:1; /* file system performs reparse point access checks */\ + UINT32 NamedStreams:1; /* file system supports named streams */\ + UINT32 HardLinks:1; /* unimplemented; set to 0 */\ + UINT32 ExtendedAttributes:1; /* file system supports extended attributes */\ + UINT32 ReadOnlyVolume:1;\ + /* kernel-mode flags */\ + UINT32 PostCleanupWhenModifiedOnly:1; /* post Cleanup when a file was modified/deleted */\ + UINT32 PassQueryDirectoryPattern:1; /* pass Pattern during QueryDirectory operations */\ + UINT32 AlwaysUseDoubleBuffering:1;\ + UINT32 PassQueryDirectoryFileName:1; /* pass FileName during QueryDirectory (GetDirInfoByName) */\ + UINT32 FlushAndPurgeOnCleanup:1; /* keeps file off "standby" list */\ + UINT32 DeviceControl:1; /* support user-mode ioctl handling */\ + /* user-mode flags */\ + UINT32 UmFileContextIsUserContext2:1; /* user mode: FileContext parameter is UserContext2 */\ + UINT32 UmFileContextIsFullContext:1; /* user mode: FileContext parameter is FullContext */\ + UINT32 UmNoReparsePointsDirCheck:1; /* user mode: no dir option check for reparse points */\ + UINT32 UmReservedFlags:5;\ + /* additional kernel-mode flags */\ + UINT32 AllowOpenInKernelMode:1; /* allow kernel mode to open files when possible */\ + UINT32 CasePreservedExtendedAttributes:1; /* preserve case of EA (default is UPPERCASE) */\ + UINT32 WslFeatures:1; /* support features required for WSLinux */\ + UINT32 DirectoryMarkerAsNextOffset:1; /* directory marker is next offset instead of last name */\ + UINT32 RejectIrpPriorToTransact0:1; /* reject IRP's prior to FspFsctlTransact with 0 buffers */\ + UINT32 SupportsPosixUnlinkRename:1; /* file system supports POSIX-style unlink and rename */\ + UINT32 KmReservedFlags:2;\ + WCHAR Prefix[FSP_FSCTL_VOLUME_PREFIX_SIZE / sizeof(WCHAR)]; /* UNC prefix (\Server\Share) */\ + WCHAR FileSystemName[FSP_FSCTL_VOLUME_FSNAME_SIZE / sizeof(WCHAR)]; +#define FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN\ + /* additional fields; specify .Version == sizeof(FSP_FSCTL_VOLUME_PARAMS) */\ + UINT32 VolumeInfoTimeoutValid:1; /* VolumeInfoTimeout field is valid */\ + UINT32 DirInfoTimeoutValid:1; /* DirInfoTimeout field is valid */\ + UINT32 SecurityTimeoutValid:1; /* SecurityTimeout field is valid*/\ + UINT32 StreamInfoTimeoutValid:1; /* StreamInfoTimeout field is valid */\ + UINT32 EaTimeoutValid:1; /* EaTimeout field is valid */\ + UINT32 KmAdditionalReservedFlags:27;\ + UINT32 VolumeInfoTimeout; /* volume info timeout (millis); overrides FileInfoTimeout */\ + UINT32 DirInfoTimeout; /* dir info timeout (millis); overrides FileInfoTimeout */\ + UINT32 SecurityTimeout; /* security info timeout (millis); overrides FileInfoTimeout */\ + UINT32 StreamInfoTimeout; /* stream info timeout (millis); overrides FileInfoTimeout */\ + UINT32 EaTimeout; /* EA timeout (millis); overrides FileInfoTimeout */\ + UINT32 FsextControlCode;\ + UINT32 Reserved32[1];\ + UINT64 Reserved64[2]; +typedef struct +{ + FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN +} FSP_FSCTL_VOLUME_PARAMS_V0; +FSP_FSCTL_STATIC_ASSERT(456 == sizeof(FSP_FSCTL_VOLUME_PARAMS_V0), + "sizeof(FSP_FSCTL_VOLUME_PARAMS_V0) must be exactly 456."); +typedef struct +{ + FSP_FSCTL_VOLUME_PARAMS_V0_FIELD_DEFN + FSP_FSCTL_VOLUME_PARAMS_V1_FIELD_DEFN +} FSP_FSCTL_VOLUME_PARAMS; +FSP_FSCTL_STATIC_ASSERT(504 == sizeof(FSP_FSCTL_VOLUME_PARAMS), + "sizeof(FSP_FSCTL_VOLUME_PARAMS) is currently 504. Update this assertion check if it changes."); +typedef struct +{ + UINT64 TotalSize; + UINT64 FreeSize; + UINT16 VolumeLabelLength; + WCHAR VolumeLabel[32]; +} FSP_FSCTL_VOLUME_INFO; +FSP_FSCTL_STATIC_ASSERT(88 == sizeof(FSP_FSCTL_VOLUME_INFO), + "sizeof(FSP_FSCTL_VOLUME_INFO) must be exactly 88."); +typedef struct +{ + UINT32 FileAttributes; + UINT32 ReparseTag; + UINT64 AllocationSize; + UINT64 FileSize; + UINT64 CreationTime; + UINT64 LastAccessTime; + UINT64 LastWriteTime; + UINT64 ChangeTime; + UINT64 IndexNumber; + UINT32 HardLinks; /* unimplemented: set to 0 */ + UINT32 EaSize; +} FSP_FSCTL_FILE_INFO; +FSP_FSCTL_STATIC_ASSERT(72 == sizeof(FSP_FSCTL_FILE_INFO), + "sizeof(FSP_FSCTL_FILE_INFO) must be exactly 72."); +typedef struct +{ + FSP_FSCTL_FILE_INFO FileInfo; + PWSTR NormalizedName; + UINT16 NormalizedNameSize; +} FSP_FSCTL_OPEN_FILE_INFO; +typedef struct +{ + UINT16 Size; + FSP_FSCTL_FILE_INFO FileInfo; + union + { + UINT64 NextOffset; + UINT8 Padding[24]; + /* make struct as big as FILE_ID_BOTH_DIR_INFORMATION; allows for in-place copying */ + } DUMMYUNIONNAME; + WCHAR FileNameBuf[]; +} FSP_FSCTL_DIR_INFO; +FSP_FSCTL_STATIC_ASSERT(104 == sizeof(FSP_FSCTL_DIR_INFO), + "sizeof(FSP_FSCTL_DIR_INFO) must be exactly 104."); +typedef struct +{ + UINT16 Size; + UINT64 StreamSize; + UINT64 StreamAllocationSize; + WCHAR StreamNameBuf[]; +} FSP_FSCTL_STREAM_INFO; +FSP_FSCTL_STATIC_ASSERT(24 == sizeof(FSP_FSCTL_STREAM_INFO), + "sizeof(FSP_FSCTL_STREAM_INFO) must be exactly 24."); +typedef struct +{ + UINT16 Size; + UINT32 Filter; + UINT32 Action; + WCHAR FileNameBuf[]; +} FSP_FSCTL_NOTIFY_INFO; +FSP_FSCTL_STATIC_ASSERT(12 == sizeof(FSP_FSCTL_NOTIFY_INFO), + "sizeof(FSP_FSCTL_NOTIFY_INFO) must be exactly 12."); +typedef struct +{ + UINT64 UserContext; + UINT64 UserContext2; +} FSP_FSCTL_TRANSACT_FULL_CONTEXT; +typedef struct +{ + UINT16 Offset; + UINT16 Size; +} FSP_FSCTL_TRANSACT_BUF; +typedef struct +{ + UINT16 Version; + UINT16 Size; + UINT32 Kind; + UINT64 Hint; + union + { + struct + { + UINT32 CreateOptions; /* Disposition: high 8 bits; Options: low 24 bits */ + UINT32 FileAttributes; /* file attributes for new files */ + FSP_FSCTL_TRANSACT_BUF SecurityDescriptor; /* security descriptor for new files */ + UINT64 AllocationSize; /* initial allocation size */ + UINT64 AccessToken; /* request access token (PID,HANDLE) */ + UINT32 DesiredAccess; /* FILE_{READ_DATA,WRITE_DATA,etc.} */ + UINT32 GrantedAccess; /* FILE_{READ_DATA,WRITE_DATA,etc.} */ + UINT32 ShareAccess; /* FILE_SHARE_{READ,WRITE,DELETE} */ + FSP_FSCTL_TRANSACT_BUF Ea; /* extended attributes or reparse point buffer */ + UINT32 UserMode:1; /* request originated in user mode */ + UINT32 HasTraversePrivilege:1; /* requestor has TOKEN_HAS_TRAVERSE_PRIVILEGE */ + UINT32 HasBackupPrivilege:1; /* requestor has TOKEN_HAS_BACKUP_PRIVILEGE */ + UINT32 HasRestorePrivilege:1; /* requestor has TOKEN_HAS_RESTORE_PRIVILEGE */ + UINT32 OpenTargetDirectory:1; /* open target dir and report FILE_{EXISTS,DOES_NOT_EXIST} */ + UINT32 CaseSensitive:1; /* FileName comparisons should be case-sensitive */ + UINT32 HasTrailingBackslash:1; /* FileName had trailing backslash */ + UINT32 AcceptsSecurityDescriptor:1; + UINT32 EaIsReparsePoint:1; /* Ea buffer is reparse point */ + UINT32 ReservedFlags:24; + UINT16 NamedStream; /* request targets named stream; colon offset in FileName */ + } Create; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + UINT32 FileAttributes; /* file attributes for overwritten/superseded files */ + UINT64 AllocationSize; /* allocation size for overwritten/superseded files */ + UINT32 Supersede:1; /* 0: FILE_OVERWRITE operation, 1: FILE_SUPERSEDE operation */ + FSP_FSCTL_TRANSACT_BUF Ea; /* extended attributes buffer */ + } Overwrite; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + UINT32 Delete:1; /* file must be deleted */ + UINT32 SetAllocationSize:1; + UINT32 SetArchiveBit:1; + UINT32 SetLastAccessTime:1; + UINT32 SetLastWriteTime:1; + UINT32 SetChangeTime:1; + } Cleanup; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + } Close; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + UINT64 Address; + UINT64 Offset; + UINT32 Length; + UINT32 Key; + } Read; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + UINT64 Address; + UINT64 Offset; + UINT32 Length; + UINT32 Key; + UINT32 ConstrainedIo:1; + } Write; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + } QueryInformation; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + UINT32 FileInformationClass; + union + { + struct + { + UINT64 AllocationSize; + } Allocation; + struct + { + UINT32 FileAttributes; + UINT64 CreationTime; + UINT64 LastAccessTime; + UINT64 LastWriteTime; + UINT64 ChangeTime; + } Basic; + struct + { + UINT32 Delete:1; + } Disposition; + struct + { + UINT32 Flags; + } DispositionEx; + struct + { + UINT64 FileSize; + } EndOfFile; + struct + { + FSP_FSCTL_TRANSACT_BUF NewFileName; + UINT64 AccessToken; /* request access token (PID,HANDLE) */ + } Rename; + struct + { + FSP_FSCTL_TRANSACT_BUF NewFileName; + UINT64 AccessToken; /* request access token (PID,HANDLE) */ + UINT32 Flags; + } RenameEx; + } Info; + } SetInformation; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + } QueryEa; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + FSP_FSCTL_TRANSACT_BUF Ea; + } SetEa; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + } FlushBuffers; + struct + { + UINT32 FsInformationClass; + union + { + struct + { + FSP_FSCTL_TRANSACT_BUF VolumeLabel; + } Label; + } Info; + } SetVolumeInformation; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + UINT64 Address; + UINT32 Length; + FSP_FSCTL_TRANSACT_BUF Pattern; + FSP_FSCTL_TRANSACT_BUF Marker; + UINT32 CaseSensitive:1; /* FileName comparisons should be case-sensitive */ + UINT32 PatternIsFileName:1; /* Pattern does not contain wildcards */ + } QueryDirectory; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + UINT32 FsControlCode; + FSP_FSCTL_TRANSACT_BUF Buffer; + UINT16 TargetOnFileSystem; /* the target of the symbolic link is on this file system */ + } FileSystemControl; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + UINT32 IoControlCode; + FSP_FSCTL_TRANSACT_BUF Buffer; + UINT32 OutputLength; + } DeviceControl; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + } QuerySecurity; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + UINT32 SecurityInformation; + FSP_FSCTL_TRANSACT_BUF SecurityDescriptor; + } SetSecurity; + struct + { + UINT64 UserContext; + UINT64 UserContext2; + } QueryStreamInformation; + } Req; + FSP_FSCTL_TRANSACT_BUF FileName; + /* Create,Cleanup,SetInformation{Disposition,Rename},FileSystemControl{ReparsePoint} */ + FSP_FSCTL_DECLSPEC_ALIGN UINT8 Buffer[]; +} FSP_FSCTL_TRANSACT_REQ; +typedef struct +{ + UINT16 Version; + UINT16 Size; + UINT32 Kind; + UINT64 Hint; + struct + { + UINT32 Information; + UINT32 Status; + } IoStatus; + union + { + union + { + /* IoStatus.Status == STATUS_SUCCESS */ + struct + { + UINT64 UserContext; /* user context associated with file node */ + UINT64 UserContext2; /* user context associated with file descriptor (handle) */ + UINT32 GrantedAccess; /* FILE_{READ_DATA,WRITE_DATA,etc.} */ + FSP_FSCTL_TRANSACT_BUF SecurityDescriptor; + FSP_FSCTL_FILE_INFO FileInfo; + FSP_FSCTL_TRANSACT_BUF FileName; + UINT32 DisableCache:1; + UINT32 HasSecurityDescriptor:1; + } Opened; + /* IoStatus.Status == STATUS_REPARSE */ + struct + { + FSP_FSCTL_TRANSACT_BUF Buffer; + } Reparse; + } Create; + struct + { + FSP_FSCTL_FILE_INFO FileInfo; + } Overwrite; + struct + { + FSP_FSCTL_FILE_INFO FileInfo; + } Write; + struct + { + FSP_FSCTL_FILE_INFO FileInfo; + } QueryInformation; + struct + { + FSP_FSCTL_FILE_INFO FileInfo; /* valid: File{Allocation,Basic,EndOfFile}Information */ + } SetInformation; + struct + { + FSP_FSCTL_TRANSACT_BUF Ea; + } QueryEa; + struct + { + FSP_FSCTL_FILE_INFO FileInfo; + FSP_FSCTL_TRANSACT_BUF Ea; /* Size==0 means no extended atttributed returned */ + } SetEa; + struct + { + FSP_FSCTL_FILE_INFO FileInfo; /* valid when flushing file (not volume) */ + } FlushBuffers; + struct + { + FSP_FSCTL_VOLUME_INFO VolumeInfo; + } QueryVolumeInformation; + struct + { + FSP_FSCTL_VOLUME_INFO VolumeInfo; + } SetVolumeInformation; + struct + { + FSP_FSCTL_TRANSACT_BUF Buffer; + } FileSystemControl; + struct + { + FSP_FSCTL_TRANSACT_BUF Buffer; + } DeviceControl; + struct + { + FSP_FSCTL_TRANSACT_BUF SecurityDescriptor; + } QuerySecurity; + struct + { + FSP_FSCTL_TRANSACT_BUF SecurityDescriptor; /* Size==0 means no security descriptor returned */ + } SetSecurity; + struct + { + FSP_FSCTL_TRANSACT_BUF Buffer; + } QueryStreamInformation; + } Rsp; + FSP_FSCTL_DECLSPEC_ALIGN UINT8 Buffer[]; +} FSP_FSCTL_TRANSACT_RSP; +#pragma warning(pop) +FSP_FSCTL_STATIC_ASSERT(FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX > FSP_FSCTL_TRANSACT_PATH_SIZEMAX, + "FSP_FSCTL_TRANSACT_RSP_BUFFER_SIZEMAX must be greater than FSP_FSCTL_TRANSACT_PATH_SIZEMAX " + "to detect when a normalized name has been set during a Create/Open request."); +static inline BOOLEAN FspFsctlTransactCanProduceRequest( + FSP_FSCTL_TRANSACT_REQ *Request, PVOID RequestBufEnd) +{ + return (PUINT8)Request + FSP_FSCTL_TRANSACT_REQ_SIZEMAX <= (PUINT8)RequestBufEnd; +} +static inline FSP_FSCTL_TRANSACT_REQ *FspFsctlTransactProduceRequest( + FSP_FSCTL_TRANSACT_REQ *Request, SIZE_T RequestSize) +{ + PVOID NextRequest = (PUINT8)Request + FSP_FSCTL_DEFAULT_ALIGN_UP(RequestSize); + return (FSP_FSCTL_TRANSACT_REQ *)NextRequest; +} +static inline FSP_FSCTL_TRANSACT_REQ *FspFsctlTransactConsumeRequest( + FSP_FSCTL_TRANSACT_REQ *Request, PVOID RequestBufEnd) +{ + if ((PUINT8)Request + sizeof(Request->Size) > (PUINT8)RequestBufEnd || + sizeof(FSP_FSCTL_TRANSACT_REQ) > Request->Size) + return 0; + PVOID NextRequest = (PUINT8)Request + FSP_FSCTL_DEFAULT_ALIGN_UP(Request->Size); + return NextRequest <= RequestBufEnd ? (FSP_FSCTL_TRANSACT_REQ *)NextRequest : 0; +} +static inline BOOLEAN FspFsctlTransactCanProduceResponse( + FSP_FSCTL_TRANSACT_RSP *Response, PVOID ResponseBufEnd) +{ + return (PUINT8)Response + FSP_FSCTL_TRANSACT_RSP_SIZEMAX <= (PUINT8)ResponseBufEnd; +} +static inline FSP_FSCTL_TRANSACT_RSP *FspFsctlTransactProduceResponse( + FSP_FSCTL_TRANSACT_RSP *Response, SIZE_T ResponseSize) +{ + PVOID NextResponse = (PUINT8)Response + FSP_FSCTL_DEFAULT_ALIGN_UP(ResponseSize); + return (FSP_FSCTL_TRANSACT_RSP *)NextResponse; +} +static inline FSP_FSCTL_TRANSACT_RSP *FspFsctlTransactConsumeResponse( + FSP_FSCTL_TRANSACT_RSP *Response, PVOID ResponseBufEnd) +{ + if ((PUINT8)Response + sizeof(Response->Size) > (PUINT8)ResponseBufEnd || + sizeof(FSP_FSCTL_TRANSACT_RSP) > Response->Size) + return 0; + PVOID NextResponse = (PUINT8)Response + FSP_FSCTL_DEFAULT_ALIGN_UP(Response->Size); + return NextResponse <= ResponseBufEnd ? (FSP_FSCTL_TRANSACT_RSP *)NextResponse : 0; +} + +#if !defined(_KERNEL_MODE) +FSP_API NTSTATUS FspFsctlCreateVolume(PWSTR DevicePath, + const FSP_FSCTL_VOLUME_PARAMS *VolumeParams, + PWCHAR VolumeNameBuf, SIZE_T VolumeNameSize, + PHANDLE PVolumeHandle); +FSP_API NTSTATUS FspFsctlMakeMountdev(HANDLE VolumeHandle, + BOOLEAN Persistent, GUID *UniqueId); +FSP_API NTSTATUS FspFsctlTransact(HANDLE VolumeHandle, + PVOID ResponseBuf, SIZE_T ResponseBufSize, + PVOID RequestBuf, SIZE_T *PRequestBufSize, + BOOLEAN Batch); +FSP_API NTSTATUS FspFsctlStop(HANDLE VolumeHandle); +FSP_API NTSTATUS FspFsctlStop0(HANDLE VolumeHandle); +FSP_API NTSTATUS FspFsctlNotify(HANDLE VolumeHandle, + FSP_FSCTL_NOTIFY_INFO *NotifyInfo, SIZE_T Size); +FSP_API NTSTATUS FspFsctlGetVolumeList(PWSTR DevicePath, + PWCHAR VolumeListBuf, PSIZE_T PVolumeListSize); +FSP_API NTSTATUS FspFsctlPreflight(PWSTR DevicePath); + +typedef struct +{ + /* in */ + HANDLE VolumeHandle; /* volume handle returned by FspFsctlCreateVolume */ + PWSTR VolumeName; /* volume name returned by FspFsctlCreateVolume */ + PSECURITY_DESCRIPTOR Security; /* optional: security descriptor for directories */ + UINT64 Reserved; /* reserved for future use */ + /* in/out */ + PWSTR MountPoint; /* FspMountSet sets drive in buffer when passed "*:" */ + HANDLE MountHandle; /* FspMountSet sets, FspMountRemove uses */ +} FSP_MOUNT_DESC; +FSP_API NTSTATUS FspMountSet(FSP_MOUNT_DESC *Desc); +FSP_API NTSTATUS FspMountRemove(FSP_MOUNT_DESC *Desc); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/inc/winfsp/launch.h b/3rd_party/winfsp-1.10/inc/winfsp/launch.h new file mode 100644 index 00000000..0b29acac --- /dev/null +++ b/3rd_party/winfsp-1.10/inc/winfsp/launch.h @@ -0,0 +1,349 @@ +/** + * @file winfsp/launch.h + * WinFsp Launch API. + * + * In order to use the WinFsp Launch API a program must include <winfsp/launch.h> + * and link with the winfsp_x64.dll (or winfsp_x86.dll) library. + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef WINFSP_LAUNCH_H_INCLUDED +#define WINFSP_LAUNCH_H_INCLUDED + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FSP_LAUNCH_REGKEY "Software\\" FSP_FSCTL_PRODUCT_NAME "\\Services" +#define FSP_LAUNCH_REGKEY_WOW64 KEY_WOW64_32KEY + +#define FSP_LAUNCH_PIPE_NAME "\\\\.\\pipe\\" FSP_FSCTL_PRODUCT_NAME ".{14E7137D-22B4-437A-B0C1-D21D1BDF3767}" +#define FSP_LAUNCH_PIPE_BUFFER_SIZE 4096 +#define FSP_LAUNCH_PIPE_OWNER ((PSID)WinLocalSystemSid) + +/* + * The launcher named pipe SDDL gives full access to LocalSystem and Administrators and + * GENERIC_READ and FILE_WRITE_DATA access to Everyone. We are careful not to give the + * FILE_CREATE_PIPE_INSTANCE right to Everyone to disallow the creation of additional + * pipe instances. + */ +#define FSP_LAUNCH_PIPE_SDDL "O:SYG:SYD:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRDCCR;;;WD)" + +/* + * The default service instance SDDL gives full access to LocalSystem and Administrators. + * The only possible service instance rights are as follows: + * RP SERVICE_START + * WP SERVICE_STOP + * LC SERVICE_QUERY_STATUS + * + * To create a service that can be started, stopped or queried by Everyone, you can set + * the following SDDL: + * D:P(A;;RPWPLC;;;WD) + */ +#define FSP_LAUNCH_SERVICE_DEFAULT_SDDL "D:P(A;;RPWPLC;;;SY)(A;;RPWPLC;;;BA)" +#define FSP_LAUNCH_SERVICE_WORLD_SDDL "D:P(A;;RPWPLC;;;WD)" + +enum +{ + FspLaunchCmdStart = 'S', /* requires: SERVICE_START */ + FspLaunchCmdStartWithSecret = 'X', /* requires: SERVICE_START */ + FspLaunchCmdStop = 'T', /* requires: SERVICE_STOP */ + FspLaunchCmdGetInfo = 'I', /* requires: SERVICE_QUERY_STATUS */ + FspLaunchCmdGetNameList = 'L', /* requires: none*/ + FspLaunchCmdDefineDosDevice = 'D', /* internal: do not use! */ + FspLaunchCmdQuit = 'Q', /* DEBUG version only */ +}; + +enum +{ + FspLaunchCmdSuccess = '$', + FspLaunchCmdFailure = '!', +}; + +/** + * @group Launch Control + */ +/** + * Call launcher pipe. + * + * This function is used to send a command to the launcher and receive a response. + * + * @param Command + * Launcher command to send. For example, the 'L' launcher command instructs + * the launcher to list all running service instances. + * @param Argc + * Command argument count. May be 0. + * @param Argv + * Command argument array. May be NULL. + * @param Argl + * Command argument length array. May be NULL. If this is NULL all command arguments + * are assumed to be NULL-terminated strings. It is also possible for specific arguments + * to be NULL-terminated; in this case pass -1 in the corresponding Argl position. + * @param Buffer + * Buffer that receives the command response. May be NULL. + * @param PSize + * Pointer to a ULONG. On input it contains the size of the Buffer. On output it + * contains the number of bytes transferred. May be NULL. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchCallLauncherPipe( + WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl, + PWSTR Buffer, PULONG PSize, + PULONG PLauncherError); +/** + * Call launcher pipe. + * + * This function is used to send a command to the launcher and receive a response. + * + * @param Command + * Launcher command to send. For example, the 'L' launcher command instructs + * the launcher to list all running service instances. + * @param Argc + * Command argument count. May be 0. + * @param Argv + * Command argument array. May be NULL. + * @param Argl + * Command argument length array. May be NULL. If this is NULL all command arguments + * are assumed to be NULL-terminated strings. It is also possible for specific arguments + * to be NULL-terminated; in this case pass -1 in the corresponding Argl position. + * @param Buffer + * Buffer that receives the command response. May be NULL. + * @param PSize + * Pointer to a ULONG. On input it contains the size of the Buffer. On output it + * contains the number of bytes transferred. May be NULL. + * @param AllowImpersonation + * Allow caller to be impersonated by launcher. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchCallLauncherPipeEx( + WCHAR Command, ULONG Argc, PWSTR *Argv, ULONG *Argl, + PWSTR Buffer, PULONG PSize, + BOOLEAN AllowImpersonation, + PULONG PLauncherError); +/** + * Start a service instance. + * + * @param ClassName + * Class name of the service instance to start. + * @param InstanceName + * Instance name of the service instance to start. + * @param Argc + * Service instance argument count. May be 0. + * @param Argv + * Service instance argument array. May be NULL. + * @param HasSecret + * Whether the last argument in Argv is assumed to be a secret (e.g. password) or not. + * Secrets are passed to service instances through standard input rather than the command + * line. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchStart( + PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, + BOOLEAN HasSecret, + PULONG PLauncherError); +/** + * Start a service instance. + * + * @param ClassName + * Class name of the service instance to start. + * @param InstanceName + * Instance name of the service instance to start. + * @param Argc + * Service instance argument count. May be 0. + * @param Argv + * Service instance argument array. May be NULL. + * @param HasSecret + * Whether the last argument in Argv is assumed to be a secret (e.g. password) or not. + * Secrets are passed to service instances through standard input rather than the command + * line. + * @param AllowImpersonation + * Allow caller to be impersonated by launcher. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchStartEx( + PWSTR ClassName, PWSTR InstanceName, ULONG Argc, PWSTR *Argv, + BOOLEAN HasSecret, + BOOLEAN AllowImpersonation, + PULONG PLauncherError); +/** + * Stop a service instance. + * + * @param ClassName + * Class name of the service instance to stop. + * @param InstanceName + * Instance name of the service instance to stop. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchStop( + PWSTR ClassName, PWSTR InstanceName, + PULONG PLauncherError); +/** + * Get information about a service instance. + * + * The information is a list of NULL-terminated strings: the class name of the service instance, + * the instance name of the service instance and the full command line used to start the service + * instance. + * + * @param ClassName + * Class name of the service instance to stop. + * @param InstanceName + * Instance name of the service instance to stop. + * @param Buffer + * Buffer that receives the command response. May be NULL. + * @param PSize + * Pointer to a ULONG. On input it contains the size of the Buffer. On output it + * contains the number of bytes transferred. May be NULL. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchGetInfo( + PWSTR ClassName, PWSTR InstanceName, + PWSTR Buffer, PULONG PSize, + PULONG PLauncherError); +/** + * List service instances. + * + * The information is a list of pairs of NULL-terminated strings. Each pair contains the class + * name and instance name of a service instance. All currently running service instances are + * listed. + * + * @param Buffer + * Buffer that receives the command response. May be NULL. + * @param PSize + * Pointer to a ULONG. On input it contains the size of the Buffer. On output it + * contains the number of bytes transferred. May be NULL. + * @param PLauncherError + * Receives the launcher error if any. This is always a Win32 error code. May not be NULL. + * @return + * STATUS_SUCCESS if the command is sent successfully to the launcher, even if the launcher + * returns an error. Other status codes indicate a communication error. Launcher errors are + * reported through PLauncherError. + */ +FSP_API NTSTATUS FspLaunchGetNameList( + PWSTR Buffer, PULONG PSize, + PULONG PLauncherError); + +/** + * @group Service Registry + */ +#pragma warning(push) +#pragma warning(disable:4200) /* zero-sized array in struct/union */ +/** + * Service registry record. + */ +typedef struct _FSP_LAUNCH_REG_RECORD +{ + PWSTR Agent; + PWSTR Executable; + PWSTR CommandLine; + PWSTR WorkDirectory; + PWSTR RunAs; + PWSTR Security; + PWSTR AuthPackage; + PWSTR Stderr; + PVOID Reserved0[4]; + ULONG JobControl; + ULONG Credentials; + ULONG AuthPackageId; + ULONG Recovery; + ULONG Reserved1[4]; + UINT8 Buffer[]; +} FSP_LAUNCH_REG_RECORD; +#pragma warning(pop) +/** + * Add/change/delete a service registry record. + * + * @param ClassName + * The service class name. + * @param Record + * The record to set in the registry. If NULL, the registry record is deleted. + * @return + * STATUS_SUCCESS or error code. + */ +FSP_API NTSTATUS FspLaunchRegSetRecord( + PWSTR ClassName, + const FSP_LAUNCH_REG_RECORD *Record); +/** + * Get a service registry record. + * + * @param ClassName + * The service class name. + * @param Agent + * The name of the agent that is retrieving the service record. This API matches + * the supplied Agent against the Agent in the service record and it only returns + * the record if they match. Pass NULL to match any Agent. + * @param PRecord + * Pointer to a record pointer. Memory for the service record will be allocated + * and a pointer to it will be stored at this address. This memory must be later + * freed using FspLaunchRegFreeRecord. + * @return + * STATUS_SUCCESS or error code. + * @see + * FspLaunchRegFreeRecord + */ +FSP_API NTSTATUS FspLaunchRegGetRecord( + PWSTR ClassName, PWSTR Agent, + FSP_LAUNCH_REG_RECORD **PRecord); +/** + * Free a service registry record. + * + * @param Record + * The service record to free. + * @see + * FspLaunchRegGetRecord + */ +FSP_API VOID FspLaunchRegFreeRecord( + FSP_LAUNCH_REG_RECORD *Record); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/inc/winfsp/winfsp.h b/3rd_party/winfsp-1.10/inc/winfsp/winfsp.h new file mode 100644 index 00000000..00b4eb89 --- /dev/null +++ b/3rd_party/winfsp-1.10/inc/winfsp/winfsp.h @@ -0,0 +1,2191 @@ +/** + * @file winfsp/winfsp.h + * WinFsp User Mode API. + * + * In order to use the WinFsp API the user mode file system must include <winfsp/winfsp.h> + * and link with the winfsp_x64.dll (or winfsp_x86.dll) library. + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef WINFSP_WINFSP_H_INCLUDED +#define WINFSP_WINFSP_H_INCLUDED + +#define WIN32_NO_STATUS +#include +#undef WIN32_NO_STATUS +#include +#pragma warning(push) +#pragma warning(disable:4005) /* macro redefinition */ +#include +#pragma warning(pop) + +#if defined(WINFSP_DLL_INTERNAL) +#define FSP_API __declspec(dllexport) +#else +#define FSP_API __declspec(dllimport) +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The REPARSE_DATA_BUFFER definitions appear to be missing from the user mode headers. + */ +#if !defined(SYMLINK_FLAG_RELATIVE) +#define SYMLINK_FLAG_RELATIVE 1 +#define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer) +typedef struct _REPARSE_DATA_BUFFER +{ + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union + { + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct + { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; +} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; +#endif + +/* + * The FILE_FULL_EA_INFORMATION definitions are missing from the user mode headers. + */ +#if !defined(FILE_NEED_EA) +#define FILE_NEED_EA 0x00000080 +#endif +#if !defined(__MINGW32__) +typedef struct _FILE_FULL_EA_INFORMATION +{ + ULONG NextEntryOffset; + UCHAR Flags; + UCHAR EaNameLength; + USHORT EaValueLength; + CHAR EaName[1]; +} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION; +#endif + +/** + * @group File System + * + * A user mode file system is a program that uses the WinFsp API to expose a file system to + * Windows. The user mode file system must implement the operations in FSP_FILE_SYSTEM_INTERFACE, + * create a file system object using FspFileSystemCreate and start its dispatcher using + * FspFileSystemStartDispatcher. At that point it will start receiving file system requests on the + * FSP_FILE_SYSTEM_INTERFACE operations. + */ +typedef struct _FSP_FILE_SYSTEM FSP_FILE_SYSTEM; +typedef NTSTATUS FSP_FILE_SYSTEM_OPERATION_GUARD(FSP_FILE_SYSTEM *, + FSP_FSCTL_TRANSACT_REQ *, FSP_FSCTL_TRANSACT_RSP *); +typedef NTSTATUS FSP_FILE_SYSTEM_OPERATION(FSP_FILE_SYSTEM *, + FSP_FSCTL_TRANSACT_REQ *, FSP_FSCTL_TRANSACT_RSP *); +/** + * User mode file system locking strategy. + * + * Two concurrency models are provided: + * + * 1. A fine-grained concurrency model where file system NAMESPACE accesses + * are guarded using an exclusive-shared (read-write) lock. File I/O is not + * guarded and concurrent reads/writes/etc. are possible. [Note that the FSD + * will still apply an exclusive-shared lock PER INDIVIDUAL FILE, but it will + * not limit I/O operations for different files.] + * + * The fine-grained concurrency model applies the exclusive-shared lock as + * follows: + *
    + *
  • EXCL: SetVolumeLabel, Flush(Volume), + * Create, Cleanup(Delete), SetInformation(Rename)
  • + *
  • SHRD: GetVolumeInfo, Open, SetInformation(Disposition), ReadDirectory
  • + *
  • NONE: all other operations
  • + *
+ * + * 2. A coarse-grained concurrency model where all file system accesses are + * guarded by a mutually exclusive lock. + * + * @see FspFileSystemSetOperationGuardStrategy + */ +typedef enum +{ + FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE = 0, + FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE, +} FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY; +enum +{ + FspCleanupDelete = 0x01, + FspCleanupSetAllocationSize = 0x02, + FspCleanupSetArchiveBit = 0x10, + FspCleanupSetLastAccessTime = 0x20, + FspCleanupSetLastWriteTime = 0x40, + FspCleanupSetChangeTime = 0x80, +}; +/** + * @class FSP_FILE_SYSTEM + * File system interface. + * + * The operations in this interface must be implemented by the user mode + * file system. Not all operations need be implemented. For example, + * a user mode file system that does not wish to support reparse points, + * need not implement the reparse point operations. + * + * Most of the operations accept a FileContext parameter. This parameter + * has different meanings depending on the value of the FSP_FSCTL_VOLUME_PARAMS + * flags UmFileContextIsUserContext2 and UmFileContextIsFullContext. + * + * There are three cases to consider: + *
    + *
  • When both of these flags are unset (default), the FileContext parameter + * represents the file node. The file node is a void pointer (or an integer + * that can fit in a pointer) that is used to uniquely identify an open file. + * Opening the same file name should always yield the same file node value + * for as long as the file with that name remains open anywhere in the system. + *
  • + *
  • When the UmFileContextIsUserContext2 is set, the FileContext parameter + * represents the file descriptor. The file descriptor is a void pointer (or + * an integer that can fit in a pointer) that is used to identify an open + * instance of a file. Opening the same file name may yield a different file + * descriptor. + *
  • + *
  • When the UmFileContextIsFullContext is set, the FileContext parameter + * is a pointer to a FSP_FSCTL_TRANSACT_FULL_CONTEXT. This allows a user mode + * file system to access the low-level UserContext and UserContext2 values. + * The UserContext is used to store the file node and the UserContext2 is + * used to store the file descriptor for an open file. + *
  • + *
+ */ +typedef struct _FSP_FILE_SYSTEM_INTERFACE +{ + /** + * Get volume information. + * + * @param FileSystem + * The file system on which this request is posted. + * @param VolumeInfo [out] + * Pointer to a structure that will receive the volume information on successful return + * from this call. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*GetVolumeInfo)(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_VOLUME_INFO *VolumeInfo); + /** + * Set volume label. + * + * @param FileSystem + * The file system on which this request is posted. + * @param VolumeLabel + * The new label for the volume. + * @param VolumeInfo [out] + * Pointer to a structure that will receive the volume information on successful return + * from this call. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*SetVolumeLabel)(FSP_FILE_SYSTEM *FileSystem, + PWSTR VolumeLabel, + FSP_FSCTL_VOLUME_INFO *VolumeInfo); + /** + * Get file or directory attributes and security descriptor given a file name. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileName + * The name of the file or directory to get the attributes and security descriptor for. + * @param PFileAttributes + * Pointer to a memory location that will receive the file attributes on successful return + * from this call. May be NULL. + * + * If this call returns STATUS_REPARSE, the file system MAY place here the index of the + * first reparse point within FileName. The file system MAY also leave this at its default + * value of 0. + * @param SecurityDescriptor + * Pointer to a buffer that will receive the file security descriptor on successful return + * from this call. May be NULL. + * @param PSecurityDescriptorSize [in,out] + * Pointer to the security descriptor buffer size. On input it contains the size of the + * security descriptor buffer. On output it will contain the actual size of the security + * descriptor copied into the security descriptor buffer. May be NULL. + * @return + * STATUS_SUCCESS, STATUS_REPARSE or error code. + * + * STATUS_REPARSE should be returned by file systems that support reparse points when + * they encounter a FileName that contains reparse points anywhere but the final path + * component. + */ + NTSTATUS (*GetSecurityByName)(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, PUINT32 PFileAttributes/* or ReparsePointIndex */, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize); + /** + * Create new file or directory. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileName + * The name of the file or directory to be created. + * @param CreateOptions + * Create options for this request. This parameter has the same meaning as the + * CreateOptions parameter of the NtCreateFile API. User mode file systems should typically + * only be concerned with the flag FILE_DIRECTORY_FILE, which is an instruction to create a + * directory rather than a file. Some file systems may also want to pay attention to the + * FILE_NO_INTERMEDIATE_BUFFERING and FILE_WRITE_THROUGH flags, although these are + * typically handled by the FSD component. + * @param GrantedAccess + * Determines the specific access rights that have been granted for this request. Upon + * receiving this call all access checks have been performed and the user mode file system + * need not perform any additional checks. However this parameter may be useful to a user + * mode file system; for example the WinFsp-FUSE layer uses this parameter to determine + * which flags to use in its POSIX open() call. + * @param FileAttributes + * File attributes to apply to the newly created file or directory. + * @param SecurityDescriptor + * Security descriptor to apply to the newly created file or directory. This security + * descriptor will always be in self-relative format. Its length can be retrieved using the + * Windows GetSecurityDescriptorLength API. Will be NULL for named streams. + * @param AllocationSize + * Allocation size for the newly created file. + * @param PFileContext [out] + * Pointer that will receive the file context on successful return from this call. + * @param FileInfo [out] + * Pointer to a structure that will receive the file information on successful return + * from this call. This information includes file attributes, file times, etc. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*Create)(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, + PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo); + /** + * Open a file or directory. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileName + * The name of the file or directory to be opened. + * @param CreateOptions + * Create options for this request. This parameter has the same meaning as the + * CreateOptions parameter of the NtCreateFile API. User mode file systems typically + * do not need to do anything special with respect to this parameter. Some file systems may + * also want to pay attention to the FILE_NO_INTERMEDIATE_BUFFERING and FILE_WRITE_THROUGH + * flags, although these are typically handled by the FSD component. + * @param GrantedAccess + * Determines the specific access rights that have been granted for this request. Upon + * receiving this call all access checks have been performed and the user mode file system + * need not perform any additional checks. However this parameter may be useful to a user + * mode file system; for example the WinFsp-FUSE layer uses this parameter to determine + * which flags to use in its POSIX open() call. + * @param PFileContext [out] + * Pointer that will receive the file context on successful return from this call. + * @param FileInfo [out] + * Pointer to a structure that will receive the file information on successful return + * from this call. This information includes file attributes, file times, etc. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*Open)(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo); + /** + * Overwrite a file. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file to overwrite. + * @param FileAttributes + * File attributes to apply to the overwritten file. + * @param ReplaceFileAttributes + * When TRUE the existing file attributes should be replaced with the new ones. + * When FALSE the existing file attributes should be merged (or'ed) with the new ones. + * @param AllocationSize + * Allocation size for the overwritten file. + * @param FileInfo [out] + * Pointer to a structure that will receive the file information on successful return + * from this call. This information includes file attributes, file times, etc. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*Overwrite)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, + FSP_FSCTL_FILE_INFO *FileInfo); + /** + * Cleanup a file. + * + * When CreateFile is used to open or create a file the kernel creates a kernel mode file + * object (type FILE_OBJECT) and a handle for it, which it returns to user-mode. The handle may + * be duplicated (using DuplicateHandle), but all duplicate handles always refer to the same + * file object. When all handles for a particular file object get closed (using CloseHandle) + * the system sends a Cleanup request to the file system. + * + * There will be a Cleanup operation for every Create or Open operation posted to the user mode + * file system. However the Cleanup operation is not the final close operation on a file. + * The file system must be ready to receive additional operations until close time. This is true + * even when the file is being deleted! + * + * The Flags parameter contains information about the cleanup operation: + *
    + *
  • FspCleanupDelete - + * An important function of the Cleanup operation is to complete a delete operation. Deleting + * a file or directory in Windows is a three-stage process where the file is first opened, then + * tested to see if the delete can proceed and if the answer is positive the file is then + * deleted during Cleanup. + * + * If the file system supports POSIX unlink (FSP_FSCTL_VOLUME_PARAMS :: + * SupportsPosixUnlinkRename), then a Cleanup / FspCleanupDelete operation may arrive while + * there are other open file handles for this particular file node. If the file system does not + * support POSIX unlink, then a Cleanup / FspCleanupDelete operation will always be the last + * outstanding cleanup for this particular file node. + *
  • + *
  • FspCleanupSetAllocationSize - + * The NTFS and FAT file systems reset a file's allocation size when they receive the last + * outstanding cleanup for a particular file node. User mode file systems that implement + * allocation size and wish to duplicate the NTFS and FAT behavior can use this flag. + *
  • + *
  • + * FspCleanupSetArchiveBit - + * File systems that support the archive bit should set the file node's archive bit when this + * flag is set. + *
  • + *
  • FspCleanupSetLastAccessTime, FspCleanupSetLastWriteTime, FspCleanupSetChangeTime - File + * systems should set the corresponding file time when each one of these flags is set. Note that + * updating the last access time is expensive and a file system may choose to not implement it. + *
+ * + * There is no way to report failure of this operation. This is a Windows limitation. + * + * As an optimization a file system may specify the FSP_FSCTL_VOLUME_PARAMS :: + * PostCleanupWhenModifiedOnly flag. In this case the FSD will only post Cleanup requests when + * the file was modified/deleted. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file or directory to cleanup. + * @param FileName + * The name of the file or directory to cleanup. Sent only when a Delete is requested. + * @param Flags + * These flags determine whether the file was modified and whether to delete the file. + * @see + * Close + * CanDelete + * SetDelete + */ + VOID (*Cleanup)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PWSTR FileName, ULONG Flags); + /** + * Close a file. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file or directory to be closed. + */ + VOID (*Close)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext); + /** + * Read a file. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file to be read. + * @param Buffer + * Pointer to a buffer that will receive the results of the read operation. + * @param Offset + * Offset within the file to read from. + * @param Length + * Length of data to read. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes read. + * @return + * STATUS_SUCCESS or error code. STATUS_PENDING is supported allowing for asynchronous + * operation. + */ + NTSTATUS (*Read)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PVOID Buffer, UINT64 Offset, ULONG Length, + PULONG PBytesTransferred); + /** + * Write a file. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file to be written. + * @param Buffer + * Pointer to a buffer that contains the data to write. + * @param Offset + * Offset within the file to write to. + * @param Length + * Length of data to write. + * @param WriteToEndOfFile + * When TRUE the file system must write to the current end of file. In this case the Offset + * parameter will contain the value -1. + * @param ConstrainedIo + * When TRUE the file system must not extend the file (i.e. change the file size). + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes written. + * @param FileInfo [out] + * Pointer to a structure that will receive the file information on successful return + * from this call. This information includes file attributes, file times, etc. + * @return + * STATUS_SUCCESS or error code. STATUS_PENDING is supported allowing for asynchronous + * operation. + */ + NTSTATUS (*Write)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PVOID Buffer, UINT64 Offset, ULONG Length, + BOOLEAN WriteToEndOfFile, BOOLEAN ConstrainedIo, + PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO *FileInfo); + /** + * Flush a file or volume. + * + * Note that the FSD will also flush all file/volume caches prior to invoking this operation. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file to be flushed. When NULL the whole volume is being flushed. + * @param FileInfo [out] + * Pointer to a structure that will receive the file information on successful return + * from this call. This information includes file attributes, file times, etc. Used when + * flushing file (not volume). + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*Flush)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + FSP_FSCTL_FILE_INFO *FileInfo); + /** + * Get file or directory information. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file or directory to get information for. + * @param FileInfo [out] + * Pointer to a structure that will receive the file information on successful return + * from this call. This information includes file attributes, file times, etc. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*GetFileInfo)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + FSP_FSCTL_FILE_INFO *FileInfo); + /** + * Set file or directory basic information. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file or directory to set information for. + * @param FileAttributes + * File attributes to apply to the file or directory. If the value INVALID_FILE_ATTRIBUTES + * is sent, the file attributes should not be changed. + * @param CreationTime + * Creation time to apply to the file or directory. If the value 0 is sent, the creation + * time should not be changed. + * @param LastAccessTime + * Last access time to apply to the file or directory. If the value 0 is sent, the last + * access time should not be changed. + * @param LastWriteTime + * Last write time to apply to the file or directory. If the value 0 is sent, the last + * write time should not be changed. + * @param ChangeTime + * Change time to apply to the file or directory. If the value 0 is sent, the change time + * should not be changed. + * @param FileInfo [out] + * Pointer to a structure that will receive the file information on successful return + * from this call. This information includes file attributes, file times, etc. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*SetBasicInfo)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 FileAttributes, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime, + FSP_FSCTL_FILE_INFO *FileInfo); + /** + * Set file/allocation size. + * + * This function is used to change a file's sizes. Windows file systems maintain two kinds + * of sizes: the file size is where the End Of File (EOF) is, and the allocation size is the + * actual size that a file takes up on the "disk". + * + * The rules regarding file/allocation size are: + *
    + *
  • Allocation size must always be aligned to the allocation unit boundary. The allocation + * unit is the product (UINT64)SectorSize * (UINT64)SectorsPerAllocationUnit from + * the FSP_FSCTL_VOLUME_PARAMS structure. The FSD will always send properly aligned allocation + * sizes when setting the allocation size.
  • + *
  • Allocation size is always greater or equal to the file size.
  • + *
  • A file size of more than the current allocation size will also extend the allocation + * size to the next allocation unit boundary.
  • + *
  • An allocation size of less than the current file size should also truncate the current + * file size.
  • + *
+ * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file to set the file/allocation size for. + * @param NewSize + * New file/allocation size to apply to the file. + * @param SetAllocationSize + * If TRUE, then the allocation size is being set. if FALSE, then the file size is being set. + * @param FileInfo [out] + * Pointer to a structure that will receive the file information on successful return + * from this call. This information includes file attributes, file times, etc. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*SetFileSize)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT64 NewSize, BOOLEAN SetAllocationSize, + FSP_FSCTL_FILE_INFO *FileInfo); + /** + * Determine whether a file or directory can be deleted. + * + * This function tests whether a file or directory can be safely deleted. This function does + * not need to perform access checks, but may performs tasks such as check for empty + * directories, etc. + * + * This function should NEVER delete the file or directory in question. Deletion should + * happen during Cleanup with the FspCleanupDelete flag set. + * + * This function gets called when Win32 API's such as DeleteFile or RemoveDirectory are used. + * It does not get called when a file or directory is opened with FILE_DELETE_ON_CLOSE. + * + * NOTE: If both CanDelete and SetDelete are defined, SetDelete takes precedence. However + * most file systems need only implement the CanDelete operation. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file or directory to test for deletion. + * @param FileName + * The name of the file or directory to test for deletion. + * @return + * STATUS_SUCCESS or error code. + * @see + * Cleanup + * SetDelete + */ + NTSTATUS (*CanDelete)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PWSTR FileName); + /** + * Renames a file or directory. + * + * The kernel mode FSD provides certain guarantees prior to posting a rename operation: + *
    + *
  • A file cannot be renamed if a file with the same name exists and has open handles.
  • + *
  • A directory cannot be renamed if it or any of its subdirectories contains a file that + * has open handles.
  • + *
+ * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file or directory to be renamed. + * @param FileName + * The current name of the file or directory to rename. + * @param NewFileName + * The new name for the file or directory. + * @param ReplaceIfExists + * Whether to replace a file that already exists at NewFileName. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*Rename)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PWSTR FileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists); + /** + * Get file or directory security descriptor. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file or directory to get the security descriptor for. + * @param SecurityDescriptor + * Pointer to a buffer that will receive the file security descriptor on successful return + * from this call. May be NULL. + * @param PSecurityDescriptorSize [in,out] + * Pointer to the security descriptor buffer size. On input it contains the size of the + * security descriptor buffer. On output it will contain the actual size of the security + * descriptor copied into the security descriptor buffer. Cannot be NULL. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*GetSecurity)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize); + /** + * Set file or directory security descriptor. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file or directory to set the security descriptor for. + * @param SecurityInformation + * Describes what parts of the file or directory security descriptor should + * be modified. + * @param ModificationDescriptor + * Describes the modifications to apply to the file or directory security descriptor. + * @return + * STATUS_SUCCESS or error code. + * @see + * FspSetSecurityDescriptor + * FspDeleteSecurityDescriptor + */ + NTSTATUS (*SetSecurity)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ModificationDescriptor); + /** + * Read a directory. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the directory to be read. + * @param Pattern + * The pattern to match against files in this directory. Can be NULL. The file system + * can choose to ignore this parameter as the FSD will always perform its own pattern + * matching on the returned results. + * @param Marker + * A file name that marks where in the directory to start reading. Files with names + * that are greater than (not equal to) this marker (in the directory order determined + * by the file system) should be returned. Can be NULL. + * @param Buffer + * Pointer to a buffer that will receive the results of the read operation. + * @param Length + * Length of data to read. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes read. + * @return + * STATUS_SUCCESS or error code. STATUS_PENDING is supported allowing for asynchronous + * operation. + * @see + * FspFileSystemAddDirInfo + */ + NTSTATUS (*ReadDirectory)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PWSTR Pattern, PWSTR Marker, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred); + /** + * Resolve reparse points. + * + * Reparse points are a general mechanism for attaching special behavior to files. + * A file or directory can contain a reparse point. A reparse point is data that has + * special meaning to the file system, Windows or user applications. For example, NTFS + * and Windows use reparse points to implement symbolic links. As another example, + * a particular file system may use reparse points to emulate UNIX FIFO's. + * + * This function is expected to resolve as many reparse points as possible. If a reparse + * point is encountered that is not understood by the file system further reparse point + * resolution should stop; the reparse point data should be returned to the FSD with status + * STATUS_REPARSE/reparse-tag. If a reparse point (symbolic link) is encountered that is + * understood by the file system but points outside it, the reparse point should be + * resolved, but further reparse point resolution should stop; the resolved file name + * should be returned to the FSD with status STATUS_REPARSE/IO_REPARSE. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileName + * The name of the file or directory to have its reparse points resolved. + * @param ReparsePointIndex + * The index of the first reparse point within FileName. + * @param ResolveLastPathComponent + * If FALSE, the last path component of FileName should not be resolved, even + * if it is a reparse point that can be resolved. If TRUE, all path components + * should be resolved if possible. + * @param PIoStatus + * Pointer to storage that will receive the status to return to the FSD. When + * this function succeeds it must set PIoStatus->Status to STATUS_REPARSE and + * PIoStatus->Information to either IO_REPARSE or the reparse tag. + * @param Buffer + * Pointer to a buffer that will receive the resolved file name (IO_REPARSE) or + * reparse data (reparse tag). If the function returns a file name, it should + * not be NULL terminated. + * @param PSize [in,out] + * Pointer to the buffer size. On input it contains the size of the buffer. + * On output it will contain the actual size of data copied. + * @return + * STATUS_REPARSE or error code. + */ + NTSTATUS (*ResolveReparsePoints)(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent, + PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize); + /** + * Get reparse point. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the reparse point. + * @param FileName + * The file name of the reparse point. + * @param Buffer + * Pointer to a buffer that will receive the results of this operation. If + * the function returns a symbolic link path, it should not be NULL terminated. + * @param PSize [in,out] + * Pointer to the buffer size. On input it contains the size of the buffer. + * On output it will contain the actual size of data copied. + * @return + * STATUS_SUCCESS or error code. + * @see + * SetReparsePoint + */ + NTSTATUS (*GetReparsePoint)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PWSTR FileName, PVOID Buffer, PSIZE_T PSize); + /** + * Set reparse point. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the reparse point. + * @param FileName + * The file name of the reparse point. + * @param Buffer + * Pointer to a buffer that contains the data for this operation. If this buffer + * contains a symbolic link path, it should not be assumed to be NULL terminated. + * @param Size + * Size of data to write. + * @return + * STATUS_SUCCESS or error code. + * @see + * GetReparsePoint + */ + NTSTATUS (*SetReparsePoint)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PWSTR FileName, PVOID Buffer, SIZE_T Size); + /** + * Delete reparse point. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the reparse point. + * @param FileName + * The file name of the reparse point. + * @param Buffer + * Pointer to a buffer that contains the data for this operation. + * @param Size + * Size of data to write. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*DeleteReparsePoint)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PWSTR FileName, PVOID Buffer, SIZE_T Size); + /** + * Get named streams information. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file or directory to get stream information for. + * @param Buffer + * Pointer to a buffer that will receive the stream information. + * @param Length + * Length of buffer. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes stored. + * @return + * STATUS_SUCCESS or error code. + * @see + * FspFileSystemAddStreamInfo + */ + NTSTATUS (*GetStreamInfo)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PVOID Buffer, ULONG Length, + PULONG PBytesTransferred); + /** + * Get directory information for a single file or directory within a parent directory. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the parent directory. + * @param FileName + * The name of the file or directory to get information for. This name is relative + * to the parent directory and is a single path component. + * @param DirInfo [out] + * Pointer to a structure that will receive the directory information on successful + * return from this call. This information includes the file name, but also file + * attributes, file times, etc. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*GetDirInfoByName)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PWSTR FileName, + FSP_FSCTL_DIR_INFO *DirInfo); + /** + * Process control code. + * + * This function is called when a program uses the DeviceIoControl API. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file or directory to be controled. + * @param ControlCode + * The control code for the operation. This code must have a DeviceType with bit + * 0x8000 set and must have a TransferType of METHOD_BUFFERED. + * @param InputBuffer + * Pointer to a buffer that contains the input data. + * @param InputBufferLength + * Input data length. + * @param OutputBuffer + * Pointer to a buffer that will receive the output data. + * @param OutputBufferLength + * Output data length. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes transferred. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*Control)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 ControlCode, + PVOID InputBuffer, ULONG InputBufferLength, + PVOID OutputBuffer, ULONG OutputBufferLength, PULONG PBytesTransferred); + /** + * Set the file delete flag. + * + * This function sets a flag to indicates whether the FSD file should delete a file + * when it is closed. This function does not need to perform access checks, but may + * performs tasks such as check for empty directories, etc. + * + * This function should NEVER delete the file or directory in question. Deletion should + * happen during Cleanup with the FspCleanupDelete flag set. + * + * This function gets called when Win32 API's such as DeleteFile or RemoveDirectory are used. + * It does not get called when a file or directory is opened with FILE_DELETE_ON_CLOSE. + * + * NOTE: If both CanDelete and SetDelete are defined, SetDelete takes precedence. However + * most file systems need only implement the CanDelete operation. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file or directory to set the delete flag for. + * @param FileName + * The name of the file or directory to set the delete flag for. + * @param DeleteFile + * If set to TRUE the FSD indicates that the file will be deleted on Cleanup; otherwise + * it will not be deleted. It is legal to receive multiple SetDelete calls for the same + * file with different DeleteFile parameters. + * @return + * STATUS_SUCCESS or error code. + * @see + * Cleanup + * CanDelete + */ + NTSTATUS (*SetDelete)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile); + /** + * Create new file or directory. + * + * This function works like Create, except that it also accepts an extra buffer that + * may contain extended attributes or a reparse point. + * + * NOTE: If both Create and CreateEx are defined, CreateEx takes precedence. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileName + * The name of the file or directory to be created. + * @param CreateOptions + * Create options for this request. This parameter has the same meaning as the + * CreateOptions parameter of the NtCreateFile API. User mode file systems should typically + * only be concerned with the flag FILE_DIRECTORY_FILE, which is an instruction to create a + * directory rather than a file. Some file systems may also want to pay attention to the + * FILE_NO_INTERMEDIATE_BUFFERING and FILE_WRITE_THROUGH flags, although these are + * typically handled by the FSD component. + * @param GrantedAccess + * Determines the specific access rights that have been granted for this request. Upon + * receiving this call all access checks have been performed and the user mode file system + * need not perform any additional checks. However this parameter may be useful to a user + * mode file system; for example the WinFsp-FUSE layer uses this parameter to determine + * which flags to use in its POSIX open() call. + * @param FileAttributes + * File attributes to apply to the newly created file or directory. + * @param SecurityDescriptor + * Security descriptor to apply to the newly created file or directory. This security + * descriptor will always be in self-relative format. Its length can be retrieved using the + * Windows GetSecurityDescriptorLength API. Will be NULL for named streams. + * @param AllocationSize + * Allocation size for the newly created file. + * @param ExtraBuffer + * Extended attributes or reparse point buffer. + * @param ExtraLength + * Extended attributes or reparse point buffer length. + * @param ExtraBufferIsReparsePoint + * FALSE: extra buffer is extended attributes; TRUE: extra buffer is reparse point. + * @param PFileContext [out] + * Pointer that will receive the file context on successful return from this call. + * @param FileInfo [out] + * Pointer to a structure that will receive the file information on successful return + * from this call. This information includes file attributes, file times, etc. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*CreateEx)(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, + PVOID ExtraBuffer, ULONG ExtraLength, BOOLEAN ExtraBufferIsReparsePoint, + PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo); + /** + * Overwrite a file. + * + * This function works like Overwrite, except that it also accepts EA (extended attributes). + * + * NOTE: If both Overwrite and OverwriteEx are defined, OverwriteEx takes precedence. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file to overwrite. + * @param FileAttributes + * File attributes to apply to the overwritten file. + * @param ReplaceFileAttributes + * When TRUE the existing file attributes should be replaced with the new ones. + * When FALSE the existing file attributes should be merged (or'ed) with the new ones. + * @param AllocationSize + * Allocation size for the overwritten file. + * @param Ea + * Extended attributes buffer. + * @param EaLength + * Extended attributes buffer length. + * @param FileInfo [out] + * Pointer to a structure that will receive the file information on successful return + * from this call. This information includes file attributes, file times, etc. + * @return + * STATUS_SUCCESS or error code. + */ + NTSTATUS (*OverwriteEx)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, + FSP_FSCTL_FILE_INFO *FileInfo); + /** + * Get extended attributes. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file to get extended attributes for. + * @param Ea + * Extended attributes buffer. + * @param EaLength + * Extended attributes buffer length. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes transferred. + * @return + * STATUS_SUCCESS or error code. + * @see + * SetEa + * FspFileSystemAddEa + */ + NTSTATUS (*GetEa)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred); + /** + * Set extended attributes. + * + * @param FileSystem + * The file system on which this request is posted. + * @param FileContext + * The file context of the file to set extended attributes for. + * @param Ea + * Extended attributes buffer. + * @param EaLength + * Extended attributes buffer length. + * @param FileInfo [out] + * Pointer to a structure that will receive the file information on successful return + * from this call. This information includes file attributes, file times, etc. + * @return + * STATUS_SUCCESS or error code. + * @see + * GetEa + */ + NTSTATUS (*SetEa)(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, + FSP_FSCTL_FILE_INFO *FileInfo); + + NTSTATUS (*Obsolete0)(VOID); + + /* + * This ensures that this interface will always contain 64 function pointers. + * Please update when changing the interface as it is important for future compatibility. + */ + NTSTATUS (*Reserved[32])(); +} FSP_FILE_SYSTEM_INTERFACE; +FSP_FSCTL_STATIC_ASSERT(sizeof(FSP_FILE_SYSTEM_INTERFACE) == 64 * sizeof(NTSTATUS (*)()), + "FSP_FILE_SYSTEM_INTERFACE must have 64 entries."); +typedef struct _FSP_FILE_SYSTEM +{ + UINT16 Version; + PVOID UserContext; + WCHAR VolumeName[FSP_FSCTL_VOLUME_NAME_SIZEMAX / sizeof(WCHAR)]; + HANDLE VolumeHandle; + FSP_FILE_SYSTEM_OPERATION_GUARD *EnterOperation, *LeaveOperation; + FSP_FILE_SYSTEM_OPERATION *Operations[FspFsctlTransactKindCount]; + const FSP_FILE_SYSTEM_INTERFACE *Interface; + HANDLE DispatcherThread; + ULONG DispatcherThreadCount; + NTSTATUS DispatcherResult; + PWSTR MountPoint; + HANDLE MountHandle; + UINT32 DebugLog; + FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY OpGuardStrategy; + SRWLOCK OpGuardLock; + BOOLEAN UmFileContextIsUserContext2, UmFileContextIsFullContext; + UINT16 UmNoReparsePointsDirCheck:1; + UINT16 UmReservedFlags:15; +} FSP_FILE_SYSTEM; +FSP_FSCTL_STATIC_ASSERT( + (4 == sizeof(PVOID) && 660 == sizeof(FSP_FILE_SYSTEM)) || + (8 == sizeof(PVOID) && 792 == sizeof(FSP_FILE_SYSTEM)), + "sizeof(FSP_FILE_SYSTEM) must be exactly 660 in 32-bit and 792 in 64-bit."); +typedef struct _FSP_FILE_SYSTEM_OPERATION_CONTEXT +{ + FSP_FSCTL_TRANSACT_REQ *Request; + FSP_FSCTL_TRANSACT_RSP *Response; +} FSP_FILE_SYSTEM_OPERATION_CONTEXT; +/** + * Check whether creating a file system object is possible. + * + * @param DevicePath + * The name of the control device for this file system. This must be either + * FSP_FSCTL_DISK_DEVICE_NAME or FSP_FSCTL_NET_DEVICE_NAME. + * @param MountPoint + * The mount point for the new file system. A value of NULL means that the file system should + * use the next available drive letter counting downwards from Z: as its mount point. + * @return + * STATUS_SUCCESS or error code. + */ +FSP_API NTSTATUS FspFileSystemPreflight(PWSTR DevicePath, + PWSTR MountPoint); +/** + * Create a file system object. + * + * @param DevicePath + * The name of the control device for this file system. This must be either + * FSP_FSCTL_DISK_DEVICE_NAME or FSP_FSCTL_NET_DEVICE_NAME. + * @param VolumeParams + * Volume parameters for the newly created file system. + * @param Interface + * A pointer to the actual operations that actually implement this user mode file system. + * @param PFileSystem [out] + * Pointer that will receive the file system object created on successful return from this + * call. + * @return + * STATUS_SUCCESS or error code. + */ +FSP_API NTSTATUS FspFileSystemCreate(PWSTR DevicePath, + const FSP_FSCTL_VOLUME_PARAMS *VolumeParams, + const FSP_FILE_SYSTEM_INTERFACE *Interface, + FSP_FILE_SYSTEM **PFileSystem); +/** + * Delete a file system object. + * + * @param FileSystem + * The file system object. + */ +FSP_API VOID FspFileSystemDelete(FSP_FILE_SYSTEM *FileSystem); +/** + * Set the mount point for a file system. + * + * This function supports drive letters (X:) or directories as mount points: + *
    + *
  • Drive letters: Refer to the documentation of the DefineDosDevice Windows API + * to better understand how they are created.
  • + *
  • Directories: They can be used as mount points for disk based file systems. They cannot + * be used for network file systems. This is a limitation that Windows imposes on junctions.
  • + *
+ * + * @param FileSystem + * The file system object. + * @param MountPoint + * The mount point for the new file system. A value of NULL means that the file system should + * use the next available drive letter counting downwards from Z: as its mount point. + * @return + * STATUS_SUCCESS or error code. + */ +FSP_API NTSTATUS FspFileSystemSetMountPoint(FSP_FILE_SYSTEM *FileSystem, PWSTR MountPoint); +FSP_API NTSTATUS FspFileSystemSetMountPointEx(FSP_FILE_SYSTEM *FileSystem, PWSTR MountPoint, + PSECURITY_DESCRIPTOR SecurityDescriptor); +/** + * Remove the mount point for a file system. + * + * @param FileSystem + * The file system object. + */ +FSP_API VOID FspFileSystemRemoveMountPoint(FSP_FILE_SYSTEM *FileSystem); +/** + * Start the file system dispatcher. + * + * The file system dispatcher is used to dispatch operations posted by the FSD to the user mode + * file system. Once this call starts executing the user mode file system will start receiving + * file system requests from the kernel. + * + * @param FileSystem + * The file system object. + * @param ThreadCount + * The number of threads for the file system dispatcher. A value of 0 will create a default + * number of threads and should be chosen in most cases. + * @return + * STATUS_SUCCESS or error code. + */ +FSP_API NTSTATUS FspFileSystemStartDispatcher(FSP_FILE_SYSTEM *FileSystem, ULONG ThreadCount); +/** + * Stop the file system dispatcher. + * + * @param FileSystem + * The file system object. + */ +FSP_API VOID FspFileSystemStopDispatcher(FSP_FILE_SYSTEM *FileSystem); +/** + * Send a response to the FSD. + * + * This call is not required when the user mode file system performs synchronous processing of + * requests. It is possible however for the following FSP_FILE_SYSTEM_INTERFACE operations to be + * processed asynchronously: + *
    + *
  • Read
  • + *
  • Write
  • + *
  • ReadDirectory
  • + *
+ * + * These operations are allowed to return STATUS_PENDING to postpone sending a response to the FSD. + * At a later time the file system can use FspFileSystemSendResponse to send the response. + * + * @param FileSystem + * The file system object. + * @param Response + * The response buffer. + */ +FSP_API VOID FspFileSystemSendResponse(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_RSP *Response); +/** + * Begin notifying Windows that the file system has file changes. + * + * A file system that wishes to notify Windows about file changes must + * first issue an FspFileSystemBegin call, followed by 0 or more + * FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call. + * + * This operation blocks concurrent file rename operations. File rename + * operations may interfere with file notification, because a file being + * notified may also be concurrently renamed. After all file change + * notifications have been issued, you must make sure to call + * FspFileSystemNotifyEnd to allow file rename operations to proceed. + * + * @param FileSystem + * The file system object. + * @return + * STATUS_SUCCESS or error code. The error code STATUS_CANT_WAIT means that + * a file rename operation is currently in progress and the operation must be + * retried at a later time. + */ +FSP_API NTSTATUS FspFileSystemNotifyBegin(FSP_FILE_SYSTEM *FileSystem, ULONG Timeout); +/** + * End notifying Windows that the file system has file changes. + * + * A file system that wishes to notify Windows about file changes must + * first issue an FspFileSystemBegin call, followed by 0 or more + * FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call. + * + * This operation allows any blocked file rename operations to proceed. + * + * @param FileSystem + * The file system object. + * @return + * STATUS_SUCCESS or error code. + */ +FSP_API NTSTATUS FspFileSystemNotifyEnd(FSP_FILE_SYSTEM *FileSystem); +/** + * Notify Windows that the file system has file changes. + * + * A file system that wishes to notify Windows about file changes must + * first issue an FspFileSystemBegin call, followed by 0 or more + * FspFileSystemNotify calls, followed by an FspFileSystemNotifyEnd call. + * + * Note that FspFileSystemNotify requires file names to be normalized. A + * normalized file name is one that contains the correct case of all characters + * in the file name. + * + * For case-sensitive file systems all file names are normalized by definition. + * For case-insensitive file systems that implement file name normalization, + * a normalized file name is the one that the file system specifies in the + * response to Create or Open (see also FspFileSystemGetOpenFileInfo). For + * case-insensitive file systems that do not implement file name normalization + * a normalized file name is the upper case version of the file name used + * to open the file. + * + * @param FileSystem + * The file system object. + * @param NotifyInfo + * Buffer containing information about file changes. + * @param Size + * Size of buffer. + * @return + * STATUS_SUCCESS or error code. + */ +FSP_API NTSTATUS FspFileSystemNotify(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_NOTIFY_INFO *NotifyInfo, SIZE_T Size); +/** + * Get the current operation context. + * + * This function may be used only when servicing one of the FSP_FILE_SYSTEM_INTERFACE operations. + * The current operation context is stored in thread local storage. It allows access to the + * Request and Response associated with this operation. + * + * @return + * The current operation context. + */ +FSP_API FSP_FILE_SYSTEM_OPERATION_CONTEXT *FspFileSystemGetOperationContext(VOID); +static inline +PWSTR FspFileSystemMountPoint(FSP_FILE_SYSTEM *FileSystem) +{ + return FileSystem->MountPoint; +} +FSP_API PWSTR FspFileSystemMountPointF(FSP_FILE_SYSTEM *FileSystem); +static inline +NTSTATUS FspFileSystemEnterOperation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) +{ + if (0 == FileSystem->EnterOperation) + return STATUS_SUCCESS; + + return FileSystem->EnterOperation(FileSystem, Request, Response); +} +FSP_API NTSTATUS FspFileSystemEnterOperationF(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +static inline +NTSTATUS FspFileSystemLeaveOperation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response) +{ + if (0 == FileSystem->LeaveOperation) + return STATUS_SUCCESS; + + return FileSystem->LeaveOperation(FileSystem, Request, Response); +} +FSP_API NTSTATUS FspFileSystemLeaveOperationF(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +static inline +VOID FspFileSystemSetOperationGuard(FSP_FILE_SYSTEM *FileSystem, + FSP_FILE_SYSTEM_OPERATION_GUARD *EnterOperation, + FSP_FILE_SYSTEM_OPERATION_GUARD *LeaveOperation) +{ + FileSystem->EnterOperation = EnterOperation; + FileSystem->LeaveOperation = LeaveOperation; +} +FSP_API VOID FspFileSystemSetOperationGuardF(FSP_FILE_SYSTEM *FileSystem, + FSP_FILE_SYSTEM_OPERATION_GUARD *EnterOperation, + FSP_FILE_SYSTEM_OPERATION_GUARD *LeaveOperation); +/** + * Set file system locking strategy. + * + * @param FileSystem + * The file system object. + * @param GuardStrategy + * The locking (guard) strategy. + * @see + * FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY + */ +static inline +VOID FspFileSystemSetOperationGuardStrategy(FSP_FILE_SYSTEM *FileSystem, + FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY GuardStrategy) +{ + FileSystem->OpGuardStrategy = GuardStrategy; +} +FSP_API VOID FspFileSystemSetOperationGuardStrategyF(FSP_FILE_SYSTEM *FileSystem, + FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY GuardStrategy); +static inline +VOID FspFileSystemSetOperation(FSP_FILE_SYSTEM *FileSystem, + ULONG Index, + FSP_FILE_SYSTEM_OPERATION *Operation) +{ + FileSystem->Operations[Index] = Operation; +} +FSP_API VOID FspFileSystemSetOperationF(FSP_FILE_SYSTEM *FileSystem, + ULONG Index, + FSP_FILE_SYSTEM_OPERATION *Operation); +static inline +VOID FspFileSystemGetDispatcherResult(FSP_FILE_SYSTEM *FileSystem, + NTSTATUS *PDispatcherResult) +{ + /* 32-bit reads are atomic */ + *PDispatcherResult = FileSystem->DispatcherResult; + MemoryBarrier(); +} +FSP_API VOID FspFileSystemGetDispatcherResultF(FSP_FILE_SYSTEM *FileSystem, + NTSTATUS *PDispatcherResult); +static inline +VOID FspFileSystemSetDispatcherResult(FSP_FILE_SYSTEM *FileSystem, + NTSTATUS DispatcherResult) +{ + if (NT_SUCCESS(DispatcherResult)) + return; + InterlockedCompareExchange(&FileSystem->DispatcherResult, DispatcherResult, 0); +} +FSP_API VOID FspFileSystemSetDispatcherResultF(FSP_FILE_SYSTEM *FileSystem, + NTSTATUS DispatcherResult); +static inline +VOID FspFileSystemSetDebugLog(FSP_FILE_SYSTEM *FileSystem, + UINT32 DebugLog) +{ + FileSystem->DebugLog = DebugLog; +} +FSP_API VOID FspFileSystemSetDebugLogF(FSP_FILE_SYSTEM *FileSystem, + UINT32 DebugLog); +static inline +BOOLEAN FspFileSystemIsOperationCaseSensitive(VOID) +{ + FSP_FSCTL_TRANSACT_REQ *Request = FspFileSystemGetOperationContext()->Request; + return + FspFsctlTransactCreateKind == Request->Kind && Request->Req.Create.CaseSensitive || + FspFsctlTransactQueryDirectoryKind == Request->Kind && Request->Req.QueryDirectory.CaseSensitive; +} +FSP_API BOOLEAN FspFileSystemIsOperationCaseSensitiveF(VOID); +/** + * Gets the originating process ID. + * + * Valid only during Create, Open and Rename requests when the target exists. + */ +static inline +UINT32 FspFileSystemOperationProcessId(VOID) +{ + FSP_FSCTL_TRANSACT_REQ *Request = FspFileSystemGetOperationContext()->Request; + switch (Request->Kind) + { + case FspFsctlTransactCreateKind: + return FSP_FSCTL_TRANSACT_REQ_TOKEN_PID(Request->Req.Create.AccessToken); + case FspFsctlTransactSetInformationKind: + if (10/*FileRenameInformation*/ == Request->Req.SetInformation.FileInformationClass || + 65/*FileRenameInformationEx*/ == Request->Req.SetInformation.FileInformationClass) + return FSP_FSCTL_TRANSACT_REQ_TOKEN_PID(Request->Req.SetInformation.Info.Rename.AccessToken); + /* fall through! */ + default: + return 0; + } +} +FSP_API UINT32 FspFileSystemOperationProcessIdF(VOID); + +/* + * Operations + */ +FSP_API NTSTATUS FspFileSystemOpEnter(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpLeave(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpCreate(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpOverwrite(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpCleanup(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpClose(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpRead(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpWrite(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpQueryInformation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpSetInformation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpQueryEa(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpSetEa(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpFlushBuffers(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpQueryVolumeInformation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpSetVolumeInformation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpQueryDirectory(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpFileSystemControl(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpDeviceControl(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpQuerySecurity(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpSetSecurity(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspFileSystemOpQueryStreamInformation(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, FSP_FSCTL_TRANSACT_RSP *Response); + +/* + * Helpers + */ +/** + * Get open information buffer. + * + * This is a helper for implementing the Create and Open operations. It cannot be used with + * any other operations. + * + * The FileInfo parameter to Create and Open is typed as pointer to FSP_FSCTL_FILE_INFO. The + * true type of this parameter is pointer to FSP_FSCTL_OPEN_FILE_INFO. This simple function + * converts from one type to the other. + * + * The FSP_FSCTL_OPEN_FILE_INFO type contains a FSP_FSCTL_FILE_INFO as well as the fields + * NormalizedName and NormalizedNameSize. These fields can be used for file name normalization. + * File name normalization is used to ensure that the FSD and the OS know the correct case + * of a newly opened file name. + * + * For case-sensitive file systems this functionality should be ignored. The FSD will always + * assume that the normalized file name is the same as the file name used to open the file. + * + * For case-insensitive file systems this functionality may be ignored. In this case the FSD + * will assume that the normalized file name is the upper case version of the file name used + * to open the file. The file system will work correctly and the only way an application will + * be able to tell that the file system does not preserve case in normalized file names is by + * issuing a GetFinalPathNameByHandle API call (or NtQueryInformationFile with + * FileNameInformation/FileNormalizedNameInformation). + * + * For case-insensitive file systems this functionality may also be used. In this case the + * user mode file system may use the NormalizedName and NormalizedNameSize parameters to + * report to the FSD the normalized file name. It should be noted that the normalized file + * name may only differ in case from the file name used to open the file. The NormalizedName + * field will point to a buffer that can receive the normalized file name. The + * NormalizedNameSize field will contain the size of the normalized file name buffer. On + * completion of the Create or Open operation it should contain the actual size of the + * normalized file name copied into the normalized file name buffer. The normalized file name + * should not contain a terminating zero. + * + * @param FileInfo + * The FileInfo parameter as passed to Create or Open operation. + * @return + * A pointer to the open information buffer for this Create or Open operation. + * @see + * Create + * Open + */ +static inline +FSP_FSCTL_OPEN_FILE_INFO *FspFileSystemGetOpenFileInfo(FSP_FSCTL_FILE_INFO *FileInfo) +{ + return (FSP_FSCTL_OPEN_FILE_INFO *)FileInfo; +} +/** + * Add directory information to a buffer. + * + * This is a helper for implementing the ReadDirectory operation. + * + * @param DirInfo + * The directory information to add. A value of NULL acts as an EOF marker for a ReadDirectory + * operation. + * @param Buffer + * Pointer to a buffer that will receive the results of the read operation. This should contain + * the same value passed to the ReadDirectory Buffer parameter. + * @param Length + * Length of data to read. This should contain the same value passed to the ReadDirectory + * Length parameter. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes read. This should + * contain the same value passed to the ReadDirectory PBytesTransferred parameter. + * FspFileSystemAddDirInfo uses the value pointed by this parameter to track how much of the + * buffer has been used so far. + * @return + * TRUE if the directory information was added, FALSE if there was not enough space to add it. + * @see + * ReadDirectory + */ +FSP_API BOOLEAN FspFileSystemAddDirInfo(FSP_FSCTL_DIR_INFO *DirInfo, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred); +/** + * Find reparse point in file name. + * + * Given a file name this function returns an index to the first path component that is a reparse + * point. The function will call the supplied GetReparsePointByName function for every path + * component until it finds a reparse point or the whole path is processed. + * + * This is a helper for implementing the GetSecurityByName operation in file systems + * that support reparse points. + * + * @param FileSystem + * The file system object. + * @param GetReparsePointByName + * Pointer to function that can retrieve reparse point information by name. The + * FspFileSystemFindReparsePoint will call this function with the Buffer and PSize + * arguments set to NULL. The function should return STATUS_SUCCESS if the passed + * FileName is a reparse point or STATUS_NOT_A_REPARSE_POINT (or other error code) + * otherwise. + * @param Context + * User context to supply to GetReparsePointByName. + * @param FileName + * The name of the file or directory. + * @param PReparsePointIndex + * Pointer to a memory location that will receive the index of the first reparse point + * within FileName. A value is only placed in this memory location if the function returns + * TRUE. May be NULL. + * @return + * TRUE if a reparse point was found, FALSE otherwise. + * @see + * GetSecurityByName + */ +FSP_API BOOLEAN FspFileSystemFindReparsePoint(FSP_FILE_SYSTEM *FileSystem, + NTSTATUS (*GetReparsePointByName)( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize), + PVOID Context, + PWSTR FileName, PUINT32 PReparsePointIndex); +/** + * Resolve reparse points. + * + * Given a file name (and an index where to start resolving) this function will attempt to + * resolve as many reparse points as possible. The function will call the supplied + * GetReparsePointByName function for every path component until it resolves the reparse points + * or the whole path is processed. + * + * This is a helper for implementing the ResolveReparsePoints operation in file systems + * that support reparse points. + * + * @param FileSystem + * The file system object. + * @param GetReparsePointByName + * Pointer to function that can retrieve reparse point information by name. The function + * should return STATUS_SUCCESS if the passed FileName is a reparse point or + * STATUS_NOT_A_REPARSE_POINT (or other error code) otherwise. + * @param Context + * User context to supply to GetReparsePointByName. + * @param FileName + * The name of the file or directory to have its reparse points resolved. + * @param ReparsePointIndex + * The index of the first reparse point within FileName. + * @param ResolveLastPathComponent + * If FALSE, the last path component of FileName should not be resolved, even + * if it is a reparse point that can be resolved. If TRUE, all path components + * should be resolved if possible. + * @param PIoStatus + * Pointer to storage that will receive the status to return to the FSD. When + * this function succeeds it must set PIoStatus->Status to STATUS_REPARSE and + * PIoStatus->Information to either IO_REPARSE or the reparse tag. + * @param Buffer + * Pointer to a buffer that will receive the resolved file name (IO_REPARSE) or + * reparse data (reparse tag). If the function returns a file name, it should + * not be NULL terminated. + * @param PSize [in,out] + * Pointer to the buffer size. On input it contains the size of the buffer. + * On output it will contain the actual size of data copied. + * @return + * STATUS_REPARSE or error code. + * @see + * ResolveReparsePoints + */ +FSP_API NTSTATUS FspFileSystemResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, + NTSTATUS (*GetReparsePointByName)( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize), + PVOID Context, + PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent, + PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize); +/** + * Test whether reparse data can be replaced. + * + * This is a helper for implementing the SetReparsePoint/DeleteReparsePoint operation + * in file systems that support reparse points. + * + * @param CurrentReparseData + * Pointer to the current reparse data. + * @param CurrentReparseDataSize + * Pointer to the current reparse data size. + * @param ReplaceReparseData + * Pointer to the replacement reparse data. + * @param ReplaceReparseDataSize + * Pointer to the replacement reparse data size. + * @return + * STATUS_SUCCESS or error code. + * @see + * SetReparsePoint + * DeleteReparsePoint + */ +FSP_API NTSTATUS FspFileSystemCanReplaceReparsePoint( + PVOID CurrentReparseData, SIZE_T CurrentReparseDataSize, + PVOID ReplaceReparseData, SIZE_T ReplaceReparseDataSize); +/** + * Add named stream information to a buffer. + * + * This is a helper for implementing the GetStreamInfo operation. + * + * @param StreamInfo + * The stream information to add. A value of NULL acts as an EOF marker for a GetStreamInfo + * operation. + * @param Buffer + * Pointer to a buffer that will receive the stream information. This should contain + * the same value passed to the GetStreamInfo Buffer parameter. + * @param Length + * Length of buffer. This should contain the same value passed to the GetStreamInfo + * Length parameter. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes stored. This should + * contain the same value passed to the GetStreamInfo PBytesTransferred parameter. + * @return + * TRUE if the stream information was added, FALSE if there was not enough space to add it. + * @see + * GetStreamInfo + */ +FSP_API BOOLEAN FspFileSystemAddStreamInfo(FSP_FSCTL_STREAM_INFO *StreamInfo, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred); +/** + * Enumerate extended attributes in a buffer. + * + * This is a helper for implementing the CreateEx and SetEa operations in file systems + * that support extended attributes. + * + * @param FileSystem + * The file system object. + * @param EnumerateEa + * Pointer to function that receives a single extended attribute. The function + * should return STATUS_SUCCESS or an error code if unsuccessful. + * @param Context + * User context to supply to EnumEa. + * @param Ea + * Extended attributes buffer. + * @param EaLength + * Extended attributes buffer length. + * @return + * STATUS_SUCCESS or error code from EnumerateEa. + */ +FSP_API NTSTATUS FspFileSystemEnumerateEa(FSP_FILE_SYSTEM *FileSystem, + NTSTATUS (*EnumerateEa)( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PFILE_FULL_EA_INFORMATION SingleEa), + PVOID Context, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength); +/** + * Add extended attribute to a buffer. + * + * This is a helper for implementing the GetEa operation. + * + * @param SingleEa + * The extended attribute to add. A value of NULL acts as an EOF marker for a GetEa + * operation. + * @param Ea + * Pointer to a buffer that will receive the extended attribute. This should contain + * the same value passed to the GetEa Ea parameter. + * @param EaLength + * Length of buffer. This should contain the same value passed to the GetEa + * EaLength parameter. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes stored. This should + * contain the same value passed to the GetEa PBytesTransferred parameter. + * @return + * TRUE if the extended attribute was added, FALSE if there was not enough space to add it. + * @see + * GetEa + */ +FSP_API BOOLEAN FspFileSystemAddEa(PFILE_FULL_EA_INFORMATION SingleEa, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred); +/** + * Get extended attribute "packed" size. This computation matches what NTFS reports. + * + * @param SingleEa + * The extended attribute to get the size for. + * @return + * The packed size of the extended attribute. + */ +static inline +UINT32 FspFileSystemGetEaPackedSize(PFILE_FULL_EA_INFORMATION SingleEa) +{ + /* magic computations are courtesy of NTFS */ + return 5 + SingleEa->EaNameLength + SingleEa->EaValueLength; +} +/** + * Add notify information to a buffer. + * + * This is a helper for filling a buffer to use with FspFileSystemNotify. + * + * @param NotifyInfo + * The notify information to add. + * @param Buffer + * Pointer to a buffer that will receive the notify information. + * @param Length + * Length of buffer. + * @param PBytesTransferred [out] + * Pointer to a memory location that will receive the actual number of bytes stored. This should + * be initialized to 0 prior to the first call to FspFileSystemAddNotifyInfo for a particular + * buffer. + * @return + * TRUE if the notify information was added, FALSE if there was not enough space to add it. + * @see + * FspFileSystemNotify + */ +FSP_API BOOLEAN FspFileSystemAddNotifyInfo(FSP_FSCTL_NOTIFY_INFO *NotifyInfo, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred); + +/* + * Directory buffering + */ +FSP_API BOOLEAN FspFileSystemAcquireDirectoryBuffer(PVOID *PDirBuffer, + BOOLEAN Reset, PNTSTATUS PResult); +FSP_API BOOLEAN FspFileSystemFillDirectoryBuffer(PVOID *PDirBuffer, + FSP_FSCTL_DIR_INFO *DirInfo, PNTSTATUS PResult); +FSP_API VOID FspFileSystemReleaseDirectoryBuffer(PVOID *PDirBuffer); +FSP_API VOID FspFileSystemReadDirectoryBuffer(PVOID *PDirBuffer, + PWSTR Marker, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred); +FSP_API VOID FspFileSystemDeleteDirectoryBuffer(PVOID *PDirBuffer); + +/* + * Security + */ +FSP_API PGENERIC_MAPPING FspGetFileGenericMapping(VOID); +FSP_API NTSTATUS FspAccessCheckEx(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + BOOLEAN CheckParentOrMain, BOOLEAN AllowTraverseCheck, + UINT32 DesiredAccess, PUINT32 PGrantedAccess/* or ReparsePointIndex */, + PSECURITY_DESCRIPTOR *PSecurityDescriptor); +FSP_API NTSTATUS FspCreateSecurityDescriptor(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + PSECURITY_DESCRIPTOR ParentDescriptor, + PSECURITY_DESCRIPTOR *PSecurityDescriptor); +/** + * Modify security descriptor. + * + * This is a helper for implementing the SetSecurity operation. + * + * @param InputDescriptor + * The input security descriptor to be modified. + * @param SecurityInformation + * Describes what parts of the InputDescriptor should be modified. This should contain + * the same value passed to the SetSecurity SecurityInformation parameter. + * @param ModificationDescriptor + * Describes the modifications to apply to the InputDescriptor. This should contain + * the same value passed to the SetSecurity ModificationDescriptor parameter. + * @param PSecurityDescriptor [out] + * Pointer to a memory location that will receive the resulting security descriptor. + * This security descriptor can be later freed using FspDeleteSecurityDescriptor. + * @return + * STATUS_SUCCESS or error code. + * @see + * SetSecurity + * FspDeleteSecurityDescriptor + */ +FSP_API NTSTATUS FspSetSecurityDescriptor( + PSECURITY_DESCRIPTOR InputDescriptor, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR ModificationDescriptor, + PSECURITY_DESCRIPTOR *PSecurityDescriptor); +/** + * Delete security descriptor. + * + * This is a helper for implementing the SetSecurity operation. + * + * @param SecurityDescriptor + * The security descriptor to be deleted. + * @param CreateFunc + * Function used to create the security descriptor. This parameter should be + * set to FspSetSecurityDescriptor for the public API. + * @return + * STATUS_SUCCESS or error code. + * @see + * SetSecurity + * FspSetSecurityDescriptor + */ +FSP_API VOID FspDeleteSecurityDescriptor(PSECURITY_DESCRIPTOR SecurityDescriptor, + NTSTATUS (*CreateFunc)()); +static inline +NTSTATUS FspAccessCheck(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_TRANSACT_REQ *Request, + BOOLEAN CheckParentOrMain, BOOLEAN AllowTraverseCheck, + UINT32 DesiredAccess, PUINT32 PGrantedAccess) +{ + return FspAccessCheckEx(FileSystem, Request, + CheckParentOrMain, AllowTraverseCheck, + DesiredAccess, PGrantedAccess, + 0); +} + +/* + * POSIX Interop + */ +FSP_API NTSTATUS FspPosixMapUidToSid(UINT32 Uid, PSID *PSid); +FSP_API NTSTATUS FspPosixMapSidToUid(PSID Sid, PUINT32 PUid); +FSP_API VOID FspDeleteSid(PSID Sid, NTSTATUS (*CreateFunc)()); +FSP_API NTSTATUS FspPosixMapPermissionsToSecurityDescriptor( + UINT32 Uid, UINT32 Gid, UINT32 Mode, + PSECURITY_DESCRIPTOR *PSecurityDescriptor); +FSP_API NTSTATUS FspPosixMergePermissionsToSecurityDescriptor( + UINT32 Uid, UINT32 Gid, UINT32 Mode, + PSECURITY_DESCRIPTOR ExistingSecurityDescriptor, + PSECURITY_DESCRIPTOR *PSecurityDescriptor); +FSP_API NTSTATUS FspPosixMapSecurityDescriptorToPermissions( + PSECURITY_DESCRIPTOR SecurityDescriptor, + PUINT32 PUid, PUINT32 PGid, PUINT32 PMode); +FSP_API NTSTATUS FspPosixMapWindowsToPosixPathEx(PWSTR WindowsPath, char **PPosixPath, + BOOLEAN Translate); +FSP_API NTSTATUS FspPosixMapPosixToWindowsPathEx(const char *PosixPath, PWSTR *PWindowsPath, + BOOLEAN Translate); +static inline +NTSTATUS FspPosixMapWindowsToPosixPath(PWSTR WindowsPath, char **PPosixPath) +{ + return FspPosixMapWindowsToPosixPathEx(WindowsPath, PPosixPath, TRUE); +} +static inline +NTSTATUS FspPosixMapPosixToWindowsPath(const char *PosixPath, PWSTR *PWindowsPath) +{ + return FspPosixMapPosixToWindowsPathEx(PosixPath, PWindowsPath, TRUE); +} +FSP_API VOID FspPosixDeletePath(void *Path); +FSP_API VOID FspPosixEncodeWindowsPath(PWSTR WindowsPath, ULONG Size); +FSP_API VOID FspPosixDecodeWindowsPath(PWSTR WindowsPath, ULONG Size); +static inline +VOID FspPosixFileTimeToUnixTime(UINT64 FileTime0, __int3264 UnixTime[2]) +{ + INT64 FileTime = (INT64)FileTime0 - 116444736000000000LL; + UnixTime[0] = (__int3264)(FileTime / 10000000); + UnixTime[1] = (__int3264)(FileTime % 10000000 * 100); + /* may produce negative nsec for times before UNIX epoch; strictly speaking this is incorrect */ +} +static inline +VOID FspPosixUnixTimeToFileTime(const __int3264 UnixTime[2], PUINT64 PFileTime) +{ + INT64 FileTime = (INT64)UnixTime[0] * 10000000 + (INT64)UnixTime[1] / 100 + + 116444736000000000LL; + *PFileTime = FileTime; +} + +/* + * Path Handling + */ +FSP_API VOID FspPathPrefix(PWSTR Path, PWSTR *PPrefix, PWSTR *PRemain, PWSTR Root); +FSP_API VOID FspPathSuffix(PWSTR Path, PWSTR *PRemain, PWSTR *PSuffix, PWSTR Root); +FSP_API VOID FspPathCombine(PWSTR Prefix, PWSTR Suffix); + +/** + * @group Service Framework + * + * User mode file systems typically are run as Windows services. WinFsp provides an API to make + * the creation of Windows services easier. This API is provided for convenience and is not + * necessary to expose a user mode file system to Windows. + */ +typedef struct _FSP_SERVICE FSP_SERVICE; +typedef NTSTATUS FSP_SERVICE_START(FSP_SERVICE *, ULONG, PWSTR *); +typedef NTSTATUS FSP_SERVICE_STOP(FSP_SERVICE *); +typedef NTSTATUS FSP_SERVICE_CONTROL(FSP_SERVICE *, ULONG, ULONG, PVOID); +#pragma warning(push) +#pragma warning(disable:4200) /* zero-sized array in struct/union */ +typedef struct _FSP_SERVICE +{ + UINT16 Version; + PVOID UserContext; + FSP_SERVICE_START *OnStart; + FSP_SERVICE_STOP *OnStop; + FSP_SERVICE_CONTROL *OnControl; + ULONG AcceptControl; + ULONG ExitCode; + SERVICE_STATUS_HANDLE StatusHandle; + SERVICE_STATUS ServiceStatus; + CRITICAL_SECTION ServiceStatusGuard; + CRITICAL_SECTION ServiceStopGuard; + BOOLEAN AllowConsoleMode; + WCHAR ServiceName[]; +} FSP_SERVICE; +#pragma warning(pop) +/** + * Run a service. + * + * This function wraps calls to FspServiceCreate, FspServiceLoop and FspServiceDelete to create, + * run and delete a service. It is intended to be used from a service's main/wmain function. + * + * This function runs a service with console mode allowed. + * + * @param ServiceName + * The name of the service. + * @param OnStart + * Function to call when the service starts. + * @param OnStop + * Function to call when the service stops. + * @param OnControl + * Function to call when the service receives a service control code. + * @return + * Service process exit code. + */ +FSP_API ULONG FspServiceRunEx(PWSTR ServiceName, + FSP_SERVICE_START *OnStart, + FSP_SERVICE_STOP *OnStop, + FSP_SERVICE_CONTROL *OnControl, + PVOID UserContext); +static inline +ULONG FspServiceRun(PWSTR ServiceName, + FSP_SERVICE_START *OnStart, + FSP_SERVICE_STOP *OnStop, + FSP_SERVICE_CONTROL *OnControl) +{ + return FspServiceRunEx(ServiceName, OnStart, OnStop, OnControl, 0); +} +/** + * Create a service object. + * + * @param ServiceName + * The name of the service. + * @param OnStart + * Function to call when the service starts. + * @param OnStop + * Function to call when the service stops. + * @param OnControl + * Function to call when the service receives a service control code. + * @param PService [out] + * Pointer that will receive the service object created on successful return from this + * call. + * @return + * STATUS_SUCCESS or error code. + */ +FSP_API NTSTATUS FspServiceCreate(PWSTR ServiceName, + FSP_SERVICE_START *OnStart, + FSP_SERVICE_STOP *OnStop, + FSP_SERVICE_CONTROL *OnControl, + FSP_SERVICE **PService); +/** + * Delete a service object. + * + * @param Service + * The service object. + */ +FSP_API VOID FspServiceDelete(FSP_SERVICE *Service); +/** + * Allow a service to run in console mode. + * + * A service that is run in console mode runs with a console attached and outside the control of + * the Service Control Manager. This is useful for debugging and testing a service during + * development. + * + * User mode file systems that wish to use the WinFsp Launcher functionality must also use this + * call. The WinFsp Launcher is a Windows service that can be configured to launch and manage + * multiple instances of a user mode file system. + * + * @param Service + * The service object. + */ +FSP_API VOID FspServiceAllowConsoleMode(FSP_SERVICE *Service); +/** + * Configure the control codes that a service accepts. + * + * This API should be used prior to Start operations. + * + * @param Service + * The service object. + * @param Control + * The control codes to accept. Note that the SERVICE_ACCEPT_PAUSE_CONTINUE code is silently + * ignored. + */ +FSP_API VOID FspServiceAcceptControl(FSP_SERVICE *Service, ULONG Control); +/** + * Request additional time from the Service Control Manager. + * + * This API should be used during Start and Stop operations only. + * + * @param Service + * The service object. + * @param Time + * Additional time (in milliseconds). + */ +FSP_API VOID FspServiceRequestTime(FSP_SERVICE *Service, ULONG Time); +/** + * Set the service process exit code. + * + * @param Service + * The service object. + * @param ExitCode + * Service process exit code. + */ +FSP_API VOID FspServiceSetExitCode(FSP_SERVICE *Service, ULONG ExitCode); +/** + * Get the service process exit code. + * + * @param Service + * The service object. + * @return + * Service process exit code. + */ +FSP_API ULONG FspServiceGetExitCode(FSP_SERVICE *Service); +/** + * Run a service main loop. + * + * This function starts and runs a service. It executes the Windows StartServiceCtrlDispatcher API + * to connect the service process to the Service Control Manager. If the Service Control Manager is + * not available (and console mode is allowed) it will enter console mode. + * + * @param Service + * The service object. + * @return + * STATUS_SUCCESS or error code. + */ +FSP_API NTSTATUS FspServiceLoop(FSP_SERVICE *Service); +/** + * Stops a running service. + * + * Stopping a service usually happens when the Service Control Manager instructs the service to + * stop. In some situations (e.g. fatal errors) the service may wish to stop itself. It can do so + * in a clean manner by calling this function. + * + * @param Service + * The service object. + * @return + * STATUS_SUCCESS or error code. + */ +FSP_API VOID FspServiceStop(FSP_SERVICE *Service); +/** + * Determine if the current process is running in user interactive mode. + * + * @return + * TRUE if the process is running in running user interactive mode. + */ +FSP_API BOOLEAN FspServiceIsInteractive(VOID); +/** + * Check if the supplied token is from the service context. + * + * @param Token + * Token to check. Pass NULL to check the current process token. + * @param PIsLocalSystem + * Pointer to a boolean that will receive a TRUE value if the token belongs to LocalSystem + * and FALSE otherwise. May be NULL. + * @return + * STATUS_SUCCESS if the token is from the service context. STATUS_ACCESS_DENIED if it is not. + * Other error codes are possible. + */ +FSP_API NTSTATUS FspServiceContextCheck(HANDLE Token, PBOOLEAN PIsLocalSystem); +/** + * Log a service message. + * + * This function can be used to log an arbitrary message to the Windows Event Log or to the current + * console if running in user interactive mode. + * + * @param Type + * One of EVENTLOG_INFORMATION_TYPE, EVENTLOG_WARNING_TYPE, EVENTLOG_ERROR_TYPE. + * @param Format + * Format specification. This function uses the Windows wsprintf API for formatting. Refer to + * that API's documentation for details on the format specification. + */ +FSP_API VOID FspServiceLog(ULONG Type, PWSTR Format, ...); +FSP_API VOID FspServiceLogV(ULONG Type, PWSTR Format, va_list ap); + +/* + * Utility + */ +FSP_API NTSTATUS FspNtStatusFromWin32(DWORD Error); +FSP_API DWORD FspWin32FromNtStatus(NTSTATUS Status); +FSP_API VOID FspEventLog(ULONG Type, PWSTR Format, ...); +FSP_API VOID FspEventLogV(ULONG Type, PWSTR Format, va_list ap); +FSP_API VOID FspDebugLogSetHandle(HANDLE Handle); +FSP_API VOID FspDebugLog(const char *Format, ...); +FSP_API VOID FspDebugLogSD(const char *Format, PSECURITY_DESCRIPTOR SecurityDescriptor); +FSP_API VOID FspDebugLogSid(const char *format, PSID Sid); +FSP_API VOID FspDebugLogFT(const char *Format, PFILETIME FileTime); +FSP_API VOID FspDebugLogRequest(FSP_FSCTL_TRANSACT_REQ *Request); +FSP_API VOID FspDebugLogResponse(FSP_FSCTL_TRANSACT_RSP *Response); +FSP_API NTSTATUS FspCallNamedPipeSecurely(PWSTR PipeName, + PVOID InBuffer, ULONG InBufferSize, PVOID OutBuffer, ULONG OutBufferSize, + PULONG PBytesTransferred, ULONG Timeout, + PSID Sid); +FSP_API NTSTATUS FspCallNamedPipeSecurelyEx(PWSTR PipeName, + PVOID InBuffer, ULONG InBufferSize, PVOID OutBuffer, ULONG OutBufferSize, + PULONG PBytesTransferred, ULONG Timeout, BOOLEAN AllowImpersonation, + PSID Sid); +FSP_API NTSTATUS FspVersion(PUINT32 PVersion); + +/* + * Delay load + */ +static inline +NTSTATUS FspLoad(PVOID *PModule) +{ +#if defined(_WIN64) +#define FSP_DLLNAME FSP_FSCTL_PRODUCT_FILE_NAME "-x64.dll" +#else +#define FSP_DLLNAME FSP_FSCTL_PRODUCT_FILE_NAME "-x86.dll" +#endif +#define FSP_DLLPATH "bin\\" FSP_DLLNAME + + WINADVAPI + LSTATUS + APIENTRY + RegGetValueW( + HKEY hkey, + LPCWSTR lpSubKey, + LPCWSTR lpValue, + DWORD dwFlags, + LPDWORD pdwType, + PVOID pvData, + LPDWORD pcbData); + + WCHAR PathBuf[MAX_PATH]; + DWORD Size; + HKEY RegKey; + LONG Result; + HMODULE Module; + + if (0 != PModule) + *PModule = 0; + + Module = LoadLibraryW(L"" FSP_DLLNAME); + if (0 == Module) + { + Result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\" FSP_FSCTL_PRODUCT_NAME, + 0, KEY_READ | KEY_WOW64_32KEY, &RegKey); + if (ERROR_SUCCESS == Result) + { + Size = sizeof PathBuf - sizeof L"" FSP_DLLPATH + sizeof(WCHAR); + Result = RegGetValueW(RegKey, 0, L"InstallDir", + RRF_RT_REG_SZ, 0, PathBuf, &Size); + RegCloseKey(RegKey); + } + if (ERROR_SUCCESS != Result) + return STATUS_OBJECT_NAME_NOT_FOUND; + + RtlCopyMemory(PathBuf + (Size / sizeof(WCHAR) - 1), L"" FSP_DLLPATH, sizeof L"" FSP_DLLPATH); + Module = LoadLibraryW(PathBuf); + if (0 == Module) + return STATUS_DLL_NOT_FOUND; + } + + if (0 != PModule) + *PModule = Module; + + return STATUS_SUCCESS; + +#undef FSP_DLLNAME +#undef FSP_DLLPATH +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/inc/winfsp/winfsp.hpp b/3rd_party/winfsp-1.10/inc/winfsp/winfsp.hpp new file mode 100644 index 00000000..d2270777 --- /dev/null +++ b/3rd_party/winfsp-1.10/inc/winfsp/winfsp.hpp @@ -0,0 +1,1320 @@ +/** + * @file winfsp/winfsp.hpp + * WinFsp C++ Layer. + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef WINFSP_WINFSP_HPP_INCLUDED +#define WINFSP_WINFSP_HPP_INCLUDED + +#ifndef __cplusplus +#error this header requires a C++ compiler +#endif + +#include + +#define FSP_CPP_EXCEPTION_GUARD(...)\ + try { __VA_ARGS__ } catch (...) { return self->ExceptionHandler(); } +#define FSP_CPP_EXCEPTION_GUARD_VOID(...)\ + try { __VA_ARGS__ } catch (...) { self->ExceptionHandler(); return; } + +namespace Fsp { + +inline NTSTATUS Initialize() +{ + static NTSTATUS LoadResult = FspLoad(0); + return LoadResult; +} + +class FileSystemBase +{ +public: + typedef FSP_FSCTL_VOLUME_INFO VolumeInfo; + typedef FSP_FSCTL_FILE_INFO FileInfo; + typedef FSP_FSCTL_OPEN_FILE_INFO OpenFileInfo; + typedef FSP_FSCTL_DIR_INFO DirInfo; + typedef FSP_FSCTL_STREAM_INFO StreamInfo; + enum CleanupFlags + { + CleanupDelete = FspCleanupDelete, + CleanupSetAllocationSize = FspCleanupSetAllocationSize, + CleanupSetArchiveBit = FspCleanupSetArchiveBit, + CleanupSetLastAccessTime = FspCleanupSetLastAccessTime, + CleanupSetLastWriteTime = FspCleanupSetLastWriteTime, + CleanupSetChangeTime = FspCleanupSetChangeTime, + }; + +public: + FileSystemBase() + { + } + virtual ~FileSystemBase() + { + } + + /* operations */ + virtual NTSTATUS ExceptionHandler() + { + return STATUS_UNEXPECTED_IO_ERROR; + } + virtual NTSTATUS Init(PVOID Host) + { + return STATUS_SUCCESS; + } + virtual NTSTATUS Mounted(PVOID Host) + { + return STATUS_SUCCESS; + } + virtual VOID Unmounted(PVOID Host) + { + } + virtual NTSTATUS GetVolumeInfo( + VolumeInfo *VolumeInfo) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS SetVolumeLabel_( + PWSTR VolumeLabel, + VolumeInfo *VolumeInfo) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS GetSecurityByName( + PWSTR FileName, + PUINT32 PFileAttributes/* or ReparsePointIndex */, + PSECURITY_DESCRIPTOR SecurityDescriptor, + SIZE_T *PSecurityDescriptorSize) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS Create( + PWSTR FileName, + UINT32 CreateOptions, + UINT32 GrantedAccess, + UINT32 FileAttributes, + PSECURITY_DESCRIPTOR SecurityDescriptor, + UINT64 AllocationSize, + PVOID *PFileNode, + PVOID *PFileDesc, + OpenFileInfo *OpenFileInfo) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS Open( + PWSTR FileName, + UINT32 CreateOptions, + UINT32 GrantedAccess, + PVOID *PFileNode, + PVOID *PFileDesc, + OpenFileInfo *OpenFileInfo) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS Overwrite( + PVOID FileNode, + PVOID FileDesc, + UINT32 FileAttributes, + BOOLEAN ReplaceFileAttributes, + UINT64 AllocationSize, + FileInfo *FileInfo) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual VOID Cleanup( + PVOID FileNode, + PVOID FileDesc, + PWSTR FileName, + ULONG Flags) + { + } + virtual VOID Close( + PVOID FileNode, + PVOID FileDesc) + { + } + virtual NTSTATUS Read( + PVOID FileNode, + PVOID FileDesc, + PVOID Buffer, + UINT64 Offset, + ULONG Length, + PULONG PBytesTransferred) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS Write( + PVOID FileNode, + PVOID FileDesc, + PVOID Buffer, + UINT64 Offset, + ULONG Length, + BOOLEAN WriteToEndOfFile, + BOOLEAN ConstrainedIo, + PULONG PBytesTransferred, + FileInfo *FileInfo) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS Flush( + PVOID FileNode, + PVOID FileDesc, + FileInfo *FileInfo) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS GetFileInfo( + PVOID FileNode, + PVOID FileDesc, + FileInfo *FileInfo) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS SetBasicInfo( + PVOID FileNode, + PVOID FileDesc, + UINT32 FileAttributes, + UINT64 CreationTime, + UINT64 LastAccessTime, + UINT64 LastWriteTime, + UINT64 ChangeTime, + FileInfo *FileInfo) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS SetFileSize( + PVOID FileNode, + PVOID FileDesc, + UINT64 NewSize, + BOOLEAN SetAllocationSize, + FileInfo *FileInfo) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS CanDelete( + PVOID FileNode, + PVOID FileDesc, + PWSTR FileName) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS Rename( + PVOID FileNode, + PVOID FileDesc, + PWSTR FileName, + PWSTR NewFileName, + BOOLEAN ReplaceIfExists) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS GetSecurity( + PVOID FileNode, + PVOID FileDesc, + PSECURITY_DESCRIPTOR SecurityDescriptor, + SIZE_T *PSecurityDescriptorSize) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS SetSecurity( + PVOID FileNode, + PVOID FileDesc, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR ModificationDescriptor) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS ReadDirectory( + PVOID FileNode, + PVOID FileDesc, + PWSTR Pattern, + PWSTR Marker, + PVOID Buffer, + ULONG Length, + PULONG PBytesTransferred) + { + return SeekableReadDirectory( + FileNode, + FileDesc, + Pattern, + Marker, + Buffer, + Length, + PBytesTransferred); + } + virtual NTSTATUS ReadDirectoryEntry( + PVOID FileNode, + PVOID FileDesc, + PWSTR Pattern, + PWSTR Marker, + PVOID *PContext, + DirInfo *DirInfo) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS ResolveReparsePoints( + PWSTR FileName, + UINT32 ReparsePointIndex, + BOOLEAN ResolveLastPathComponent, + PIO_STATUS_BLOCK PIoStatus, + PVOID Buffer, + PSIZE_T PSize) + { + return FspFileSystemResolveReparsePoints( + 0, + GetReparsePointByName, + this, + FileName, + ReparsePointIndex, + ResolveLastPathComponent, + PIoStatus, + Buffer, + PSize); + } + virtual NTSTATUS GetReparsePointByName( + PWSTR FileName, + BOOLEAN IsDirectory, + PVOID Buffer, + PSIZE_T PSize) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS GetReparsePoint( + PVOID FileNode, + PVOID FileDesc, + PWSTR FileName, + PVOID Buffer, + PSIZE_T PSize) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS SetReparsePoint( + PVOID FileNode, + PVOID FileDesc, + PWSTR FileName, + PVOID Buffer, + SIZE_T Size) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS DeleteReparsePoint( + PVOID FileNode, + PVOID FileDesc, + PWSTR FileName, + PVOID Buffer, + SIZE_T Size) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + virtual NTSTATUS GetStreamInfo( + PVOID FileNode, + PVOID FileDesc, + PVOID Buffer, + ULONG Length, + PULONG PBytesTransferred) + { + return STATUS_INVALID_DEVICE_REQUEST; + } + + /* helpers */ + static NTSTATUS NtStatusFromWin32(DWORD Error) + { + return FspNtStatusFromWin32(Error); + } + static DWORD Win32FromNtStatus(NTSTATUS Status) + { + return FspWin32FromNtStatus(Status); + } + static VOID DeleteDirectoryBuffer(PVOID *PDirBuffer) + { + FspFileSystemDeleteDirectoryBuffer(PDirBuffer); + } + NTSTATUS SeekableReadDirectory( + PVOID FileNode, + PVOID FileDesc, + PWSTR Pattern, + PWSTR Marker, + PVOID Buffer, + ULONG Length, + PULONG PBytesTransferred) + { + PVOID Context = 0; + union + { + UINT8 B[FIELD_OFFSET(FileSystemBase::DirInfo, FileNameBuf) + MAX_PATH * sizeof(WCHAR)]; + FileSystemBase::DirInfo D; + } DirInfoBuf; + FileSystemBase::DirInfo *DirInfo = &DirInfoBuf.D; + NTSTATUS Result = STATUS_SUCCESS; + *PBytesTransferred = 0; + for (;;) + { + Result = ReadDirectoryEntry(FileNode, FileDesc, Pattern, Marker, &Context, DirInfo); + if (STATUS_NO_MORE_FILES == Result) + { + Result = STATUS_SUCCESS; + break; + } + if (!NT_SUCCESS(Result)) + break; + if (!FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred)) + break; + } + if (!NT_SUCCESS(Result)) + return Result; + return STATUS_SUCCESS; + } + NTSTATUS BufferedReadDirectory( + PVOID *PDirBuffer, + PVOID FileNode, + PVOID FileDesc, + PWSTR Pattern, + PWSTR Marker, + PVOID Buffer, + ULONG Length, + PULONG PBytesTransferred) + { + PVOID Context = 0; + union + { + UINT8 B[FIELD_OFFSET(FileSystemBase::DirInfo, FileNameBuf) + MAX_PATH * sizeof(WCHAR)]; + FileSystemBase::DirInfo D; + } DirInfoBuf; + FileSystemBase::DirInfo *DirInfo = &DirInfoBuf.D; + NTSTATUS Result = STATUS_SUCCESS; + *PBytesTransferred = 0; + if (FspFileSystemAcquireDirectoryBuffer(PDirBuffer, 0 == Marker, &Result)) + { + try + { + for (;;) + { + Result = ReadDirectoryEntry(FileNode, FileDesc, Pattern, Marker, &Context, DirInfo); + if (STATUS_NO_MORE_FILES == Result) + { + Result = STATUS_SUCCESS; + break; + } + if (!NT_SUCCESS(Result)) + break; + if (!FspFileSystemFillDirectoryBuffer(PDirBuffer, DirInfo, &Result)) + break; + } + } + catch (...) + { + FspFileSystemReleaseDirectoryBuffer(PDirBuffer); + throw; + } + FspFileSystemReleaseDirectoryBuffer(PDirBuffer); + } + if (!NT_SUCCESS(Result)) + return Result; + FspFileSystemReadDirectoryBuffer(PDirBuffer, Marker, Buffer, Length, PBytesTransferred); + return STATUS_SUCCESS; + } + BOOLEAN FindReparsePoint( + PWSTR FileName, PUINT32 PReparsePointIndex) + { + return FspFileSystemFindReparsePoint( + 0, + GetReparsePointByName, + this, + FileName, + PReparsePointIndex); + } + static NTSTATUS CanReplaceReparsePoint( + PVOID CurrentReparseData, SIZE_T CurrentReparseDataSize, + PVOID ReplaceReparseData, SIZE_T ReplaceReparseDataSize) + { + return FspFileSystemCanReplaceReparsePoint( + CurrentReparseData, CurrentReparseDataSize, + ReplaceReparseData, ReplaceReparseDataSize); + } + static BOOLEAN AddStreamInfo(StreamInfo *StreamInfo, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) + { + return FspFileSystemAddStreamInfo(StreamInfo, Buffer, Length, PBytesTransferred); + } + +private: + static NTSTATUS GetReparsePointByName(FSP_FILE_SYSTEM *FileSystem, + PVOID Context, + PWSTR FileName, + BOOLEAN IsDirectory, + PVOID Buffer, + PSIZE_T PSize) + { + FileSystemBase *self = (FileSystemBase *)Context; + FSP_CPP_EXCEPTION_GUARD( + return self->GetReparsePointByName( + FileName, + IsDirectory, + Buffer, + PSize); + ) + } + +private: + /* disallow copy and assignment */ + FileSystemBase(const FileSystemBase &); + FileSystemBase &operator=(const FileSystemBase &); +}; + +class FileSystemHost +{ +public: + /* ctor/dtor */ + FileSystemHost(FileSystemBase &FileSystem) : + _VolumeParams(), _FileSystemPtr(0), _FileSystem(&FileSystem) + { + Initialize(); + _VolumeParams.UmFileContextIsFullContext = 1; + } + virtual ~FileSystemHost() + { + if (0 != _FileSystemPtr) + FspFileSystemDelete(_FileSystemPtr); + } + + /* properties */ + UINT16 SectorSize() + { + return _VolumeParams.SectorSize; + } + VOID SetSectorSize(UINT16 SectorSize) + { + _VolumeParams.SectorSize = SectorSize; + } + UINT16 SectorsPerAllocationUnit() + { + return _VolumeParams.SectorsPerAllocationUnit; + } + VOID SetSectorsPerAllocationUnit(UINT16 SectorsPerAllocationUnit) + { + _VolumeParams.SectorsPerAllocationUnit = SectorsPerAllocationUnit; + } + UINT16 MaxComponentLength() + { + return _VolumeParams.MaxComponentLength; + } + VOID SetMaxComponentLength(UINT16 MaxComponentLength) + { + _VolumeParams.MaxComponentLength = MaxComponentLength; + } + UINT64 VolumeCreationTime() + { + return _VolumeParams.VolumeCreationTime; + } + VOID SetVolumeCreationTime(UINT64 VolumeCreationTime) + { + _VolumeParams.VolumeCreationTime = VolumeCreationTime; + } + UINT32 VolumeSerialNumber() + { + return _VolumeParams.VolumeSerialNumber; + } + VOID SetVolumeSerialNumber(UINT32 VolumeSerialNumber) + { + _VolumeParams.VolumeSerialNumber = VolumeSerialNumber; + } + UINT32 FileInfoTimeout() + { + return _VolumeParams.FileInfoTimeout; + } + VOID SetFileInfoTimeout(UINT32 FileInfoTimeout) + { + _VolumeParams.FileInfoTimeout = FileInfoTimeout; + } + BOOLEAN CaseSensitiveSearch() + { + return _VolumeParams.CaseSensitiveSearch; + } + VOID SetCaseSensitiveSearch(BOOLEAN CaseSensitiveSearch) + { + _VolumeParams.CaseSensitiveSearch = !!CaseSensitiveSearch; + } + BOOLEAN CasePreservedNames() + { + return _VolumeParams.CasePreservedNames; + } + VOID SetCasePreservedNames(BOOLEAN CasePreservedNames) + { + _VolumeParams.CasePreservedNames = !!CasePreservedNames; + } + BOOLEAN UnicodeOnDisk() + { + return _VolumeParams.UnicodeOnDisk; + } + VOID SetUnicodeOnDisk(BOOLEAN UnicodeOnDisk) + { + _VolumeParams.UnicodeOnDisk = !!UnicodeOnDisk; + } + BOOLEAN PersistentAcls() + { + return _VolumeParams.PersistentAcls; + } + VOID SetPersistentAcls(BOOLEAN PersistentAcls) + { + _VolumeParams.PersistentAcls = !!PersistentAcls; + } + BOOLEAN ReparsePoints() + { + return _VolumeParams.ReparsePoints; + } + VOID SetReparsePoints(BOOLEAN ReparsePoints) + { + _VolumeParams.ReparsePoints = !!ReparsePoints; + } + BOOLEAN ReparsePointsAccessCheck() + { + return _VolumeParams.ReparsePointsAccessCheck; + } + VOID SetReparsePointsAccessCheck(BOOLEAN ReparsePointsAccessCheck) + { + _VolumeParams.ReparsePointsAccessCheck = !!ReparsePointsAccessCheck; + } + BOOLEAN NamedStreams() + { + return _VolumeParams.NamedStreams; + } + VOID SetNamedStreams(BOOLEAN NamedStreams) + { + _VolumeParams.NamedStreams = !!NamedStreams; + } + BOOLEAN PostCleanupWhenModifiedOnly() + { + return _VolumeParams.PostCleanupWhenModifiedOnly; + } + VOID SetPostCleanupWhenModifiedOnly(BOOLEAN PostCleanupWhenModifiedOnly) + { + _VolumeParams.PostCleanupWhenModifiedOnly = !!PostCleanupWhenModifiedOnly; + } + BOOLEAN PassQueryDirectoryPattern() + { + return _VolumeParams.PassQueryDirectoryPattern; + } + VOID SetPassQueryDirectoryPattern(BOOLEAN PassQueryDirectoryPattern) + { + _VolumeParams.PassQueryDirectoryPattern = !!PassQueryDirectoryPattern; + } + BOOLEAN FlushAndPurgeOnCleanup() + { + return _VolumeParams.FlushAndPurgeOnCleanup; + } + VOID SetFlushAndPurgeOnCleanup(BOOLEAN FlushAndPurgeOnCleanup) + { + _VolumeParams.FlushAndPurgeOnCleanup = !!FlushAndPurgeOnCleanup; + } + PWSTR Prefix() + { + return _VolumeParams.Prefix; + } + VOID SetPrefix(PWSTR Prefix) + { + int Size = lstrlenW(Prefix) * sizeof(WCHAR); + if (Size > sizeof _VolumeParams.Prefix - sizeof(WCHAR)) + Size = sizeof _VolumeParams.Prefix - sizeof(WCHAR); + RtlCopyMemory(_VolumeParams.Prefix, Prefix, Size); + _VolumeParams.Prefix[Size / sizeof(WCHAR)] = L'\0'; + } + PWSTR FileSystemName() + { + return _VolumeParams.FileSystemName; + } + VOID SetFileSystemName(PWSTR FileSystemName) + { + int Size = lstrlenW(FileSystemName) * sizeof(WCHAR); + if (Size > sizeof _VolumeParams.FileSystemName - sizeof(WCHAR)) + Size = sizeof _VolumeParams.FileSystemName - sizeof(WCHAR); + RtlCopyMemory(_VolumeParams.FileSystemName, FileSystemName, Size); + _VolumeParams.FileSystemName[Size / sizeof(WCHAR)] = L'\0'; + } + + /* control */ + NTSTATUS Preflight(PWSTR MountPoint) + { + return FspFileSystemPreflight( + _VolumeParams.Prefix[0] ? L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME, + MountPoint); + } + NTSTATUS Mount(PWSTR MountPoint, + PSECURITY_DESCRIPTOR SecurityDescriptor = 0, + BOOLEAN Synchronized = FALSE, + UINT32 DebugLog = 0) + { + NTSTATUS Result; + try + { + Result = _FileSystem->Init(this); + } + catch (...) + { + Result = _FileSystem->ExceptionHandler(); + } + if (!NT_SUCCESS(Result)) + return Result; + Result = FspFileSystemCreate( + _VolumeParams.Prefix[0] ? L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME, + &_VolumeParams, Interface(), &_FileSystemPtr); + if (!NT_SUCCESS(Result)) + return Result; + _FileSystemPtr->UserContext = _FileSystem; + FspFileSystemSetOperationGuardStrategy(_FileSystemPtr, Synchronized ? + FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE : + FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_FINE); + FspFileSystemSetDebugLog(_FileSystemPtr, DebugLog); + Result = FspFileSystemSetMountPointEx(_FileSystemPtr, MountPoint, SecurityDescriptor); + if (NT_SUCCESS(Result)) + { + try + { + Result = _FileSystem->Mounted(this); + } + catch (...) + { + Result = _FileSystem->ExceptionHandler(); + } + if (NT_SUCCESS(Result)) + { + Result = FspFileSystemStartDispatcher(_FileSystemPtr, 0); + if (!NT_SUCCESS(Result)) + try + { + _FileSystem->Unmounted(this); + } + catch (...) + { + _FileSystem->ExceptionHandler(); + } + } + } + if (!NT_SUCCESS(Result)) + { + FspFileSystemDelete(_FileSystemPtr); + _FileSystemPtr = 0; + } + return Result; + } + VOID Unmount() + { + FspFileSystemStopDispatcher(_FileSystemPtr); + try + { + _FileSystem->Unmounted(this); + } + catch (...) + { + _FileSystem->ExceptionHandler(); + } + _FileSystemPtr->UserContext = 0; + FspFileSystemDelete(_FileSystemPtr); + _FileSystemPtr = 0; + } + PWSTR MountPoint() + { + return 0 != _FileSystemPtr ? FspFileSystemMountPoint(_FileSystemPtr) : 0; + } + FSP_FILE_SYSTEM *FileSystemHandle() + { + return _FileSystemPtr; + } + FileSystemBase &FileSystem() + { + return *_FileSystem; + } + static NTSTATUS SetDebugLogFile(PWSTR FileName) + { + HANDLE Handle; + if ('-' == FileName[0] && '\0' == FileName[1]) + Handle = GetStdHandle(STD_ERROR_HANDLE); + else + Handle = CreateFileW( + FileName, + FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0); + if (INVALID_HANDLE_VALUE == Handle) + return FspNtStatusFromWin32(GetLastError()); + FspDebugLogSetHandle(Handle); + return STATUS_SUCCESS; + } + +private: + /* FSP_FILE_SYSTEM_INTERFACE */ + static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem0, + FSP_FSCTL_VOLUME_INFO *VolumeInfo) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->GetVolumeInfo( + VolumeInfo); + ) + } + static NTSTATUS SetVolumeLabel_(FSP_FILE_SYSTEM *FileSystem0, + PWSTR VolumeLabel, + FSP_FSCTL_VOLUME_INFO *VolumeInfo) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->SetVolumeLabel_( + VolumeLabel, + VolumeInfo); + ) + } + static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem0, + PWSTR FileName, + PUINT32 PFileAttributes/* or ReparsePointIndex */, + PSECURITY_DESCRIPTOR SecurityDescriptor, + SIZE_T *PSecurityDescriptorSize) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->GetSecurityByName( + FileName, + PFileAttributes, + SecurityDescriptor, + PSecurityDescriptorSize); + ) + } + static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem0, + PWSTR FileName, + UINT32 CreateOptions, + UINT32 GrantedAccess, + UINT32 FileAttributes, + PSECURITY_DESCRIPTOR SecurityDescriptor, + UINT64 AllocationSize, + PVOID *FullContext, + FSP_FSCTL_FILE_INFO *FileInfo) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + PVOID FileNode, FileDesc; + NTSTATUS Result; + FSP_CPP_EXCEPTION_GUARD( + Result = self->Create( + FileName, + CreateOptions, + GrantedAccess, + FileAttributes, + SecurityDescriptor, + AllocationSize, + &FileNode, + &FileDesc, + FspFileSystemGetOpenFileInfo(FileInfo)); + ) + ((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext = (UINT64)(UINT_PTR)FileNode; + ((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2 = (UINT64)(UINT_PTR)FileDesc; + return Result; + } + static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem0, + PWSTR FileName, + UINT32 CreateOptions, + UINT32 GrantedAccess, + PVOID *FullContext, + FSP_FSCTL_FILE_INFO *FileInfo) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + PVOID FileNode, FileDesc; + NTSTATUS Result; + FSP_CPP_EXCEPTION_GUARD( + Result = self->Open( + FileName, + CreateOptions, + GrantedAccess, + &FileNode, + &FileDesc, + FspFileSystemGetOpenFileInfo(FileInfo)); + ) + ((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext = (UINT64)(UINT_PTR)FileNode; + ((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2 = (UINT64)(UINT_PTR)FileDesc; + return Result; + } + static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + UINT32 FileAttributes, + BOOLEAN ReplaceFileAttributes, + UINT64 AllocationSize, + FSP_FSCTL_FILE_INFO *FileInfo) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->Overwrite( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + FileAttributes, + ReplaceFileAttributes, + AllocationSize, + FileInfo); + ) + } + static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + PWSTR FileName, + ULONG Flags) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD_VOID( + return self->Cleanup( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + FileName, + Flags); + ) + } + static VOID Close(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD_VOID( + return self->Close( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2); + ) + } + static NTSTATUS Read(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + PVOID Buffer, + UINT64 Offset, + ULONG Length, + PULONG PBytesTransferred) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->Read( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + Buffer, + Offset, + Length, + PBytesTransferred); + ) + } + static NTSTATUS Write(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + PVOID Buffer, + UINT64 Offset, + ULONG Length, + BOOLEAN WriteToEndOfFile, + BOOLEAN ConstrainedIo, + PULONG PBytesTransferred, + FSP_FSCTL_FILE_INFO *FileInfo) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->Write( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + Buffer, + Offset, + Length, + WriteToEndOfFile, + ConstrainedIo, + PBytesTransferred, + FileInfo); + ) + } + static NTSTATUS Flush(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + FSP_FSCTL_FILE_INFO *FileInfo) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->Flush( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + FileInfo); + ) + } + static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + FSP_FSCTL_FILE_INFO *FileInfo) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->GetFileInfo( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + FileInfo); + ) + } + static NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + UINT32 FileAttributes, + UINT64 CreationTime, + UINT64 LastAccessTime, + UINT64 LastWriteTime, + UINT64 ChangeTime, + FSP_FSCTL_FILE_INFO *FileInfo) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->SetBasicInfo( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + FileAttributes, + CreationTime, + LastAccessTime, + LastWriteTime, + ChangeTime, + FileInfo); + ) + } + static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + UINT64 NewSize, + BOOLEAN SetAllocationSize, + FSP_FSCTL_FILE_INFO *FileInfo) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->SetFileSize( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + NewSize, + SetAllocationSize, + FileInfo); + ) + } + static NTSTATUS CanDelete(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + PWSTR FileName) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->CanDelete( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + FileName); + ) + } + static NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + PWSTR FileName, + PWSTR NewFileName, + BOOLEAN ReplaceIfExists) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->Rename( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + FileName, + NewFileName, + ReplaceIfExists); + ) + } + static NTSTATUS GetSecurity(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + PSECURITY_DESCRIPTOR SecurityDescriptor, + SIZE_T *PSecurityDescriptorSize) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->GetSecurity( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + SecurityDescriptor, + PSecurityDescriptorSize); + ) + } + static NTSTATUS SetSecurity(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + SECURITY_INFORMATION SecurityInformation, + PSECURITY_DESCRIPTOR ModificationDescriptor) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->SetSecurity( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + SecurityInformation, + ModificationDescriptor); + ) + } + static NTSTATUS ReadDirectory(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + PWSTR Pattern, + PWSTR Marker, + PVOID Buffer, + ULONG Length, + PULONG PBytesTransferred) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->ReadDirectory( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + Pattern, + Marker, + Buffer, + Length, + PBytesTransferred); + ) + } + static NTSTATUS ResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem0, + PWSTR FileName, + UINT32 ReparsePointIndex, + BOOLEAN ResolveLastPathComponent, + PIO_STATUS_BLOCK PIoStatus, + PVOID Buffer, + PSIZE_T PSize) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->ResolveReparsePoints( + FileName, + ReparsePointIndex, + ResolveLastPathComponent, + PIoStatus, + Buffer, + PSize); + ) + } + static NTSTATUS GetReparsePoint(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + PWSTR FileName, + PVOID Buffer, + PSIZE_T PSize) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->GetReparsePoint( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + FileName, + Buffer, + PSize); + ) + } + static NTSTATUS SetReparsePoint(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + PWSTR FileName, + PVOID Buffer, + SIZE_T Size) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->SetReparsePoint( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + FileName, + Buffer, + Size); + ) + } + static NTSTATUS DeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + PWSTR FileName, + PVOID Buffer, + SIZE_T Size) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->DeleteReparsePoint( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + FileName, + Buffer, + Size); + ) + } + static NTSTATUS GetStreamInfo(FSP_FILE_SYSTEM *FileSystem0, + PVOID FullContext, + PVOID Buffer, + ULONG Length, + PULONG PBytesTransferred) + { + FileSystemBase *self = (FileSystemBase *)FileSystem0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->GetStreamInfo( + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext, + (PVOID)(UINT_PTR)((FSP_FSCTL_TRANSACT_FULL_CONTEXT *)FullContext)->UserContext2, + Buffer, + Length, + PBytesTransferred); + ) + } + static FSP_FILE_SYSTEM_INTERFACE *Interface() + { + static FSP_FILE_SYSTEM_INTERFACE _Interface = + { + GetVolumeInfo, + SetVolumeLabel_, + GetSecurityByName, + Create, + Open, + Overwrite, + Cleanup, + Close, + Read, + Write, + Flush, + GetFileInfo, + SetBasicInfo, + SetFileSize, + CanDelete, + Rename, + GetSecurity, + SetSecurity, + ReadDirectory, + ResolveReparsePoints, + GetReparsePoint, + SetReparsePoint, + DeleteReparsePoint, + GetStreamInfo, + }; + return &_Interface; + } + +private: + /* disallow copy and assignment */ + FileSystemHost(const FileSystemHost &); + FileSystemHost &operator=(const FileSystemHost &); + +private: + FSP_FSCTL_VOLUME_PARAMS _VolumeParams; + FSP_FILE_SYSTEM *_FileSystemPtr; + FileSystemBase *_FileSystem; +}; + +class Service +{ +public: + /* ctor/dtor */ + Service(PWSTR ServiceName) : _Service(0) + { + Initialize(); + FspServiceCreate(ServiceName, OnStart, OnStop, 0, &_Service); + if (0 != _Service) + _Service->UserContext = this; + } + virtual ~Service() + { + if (0 != _Service) + FspServiceDelete(_Service); + } + + /* control */ + ULONG Run() + { + if (0 == _Service) + { + FspServiceLog(EVENTLOG_ERROR_TYPE, + L"The service cannot be created (Status=%lx).", + STATUS_INSUFFICIENT_RESOURCES); + return FspWin32FromNtStatus(STATUS_INSUFFICIENT_RESOURCES); + } + FspServiceAllowConsoleMode(_Service); + NTSTATUS Result = FspServiceLoop(_Service); + ULONG ExitCode = FspServiceGetExitCode(_Service); + if (!NT_SUCCESS(Result)) + { + FspServiceLog(EVENTLOG_ERROR_TYPE, + L"The service has failed to run (Status=%lx).", + Result); + return FspWin32FromNtStatus(Result); + } + return ExitCode; + } + VOID Stop() + { + if (0 == _Service) + return; + FspServiceStop(_Service); + } + VOID RequestTime(ULONG Time) + { + if (0 == _Service) + return; + FspServiceRequestTime(_Service, Time); + } + ULONG GetExitCode() + { + return 0 != _Service ? FspServiceGetExitCode(_Service) : ERROR_NO_SYSTEM_RESOURCES; + } + VOID SetExitCode(ULONG ExitCode) + { + if (0 == _Service) + return; + FspServiceSetExitCode(_Service, ExitCode); + } + FSP_SERVICE *ServiceHandle() + { + return _Service; + } + static VOID Log(ULONG Type, PWSTR Format, ...) + { + va_list ap; + va_start(ap, Format); + FspServiceLogV(Type, Format, ap); + va_end(ap); + } + static VOID LogV(ULONG Type, PWSTR Format, va_list ap) + { + FspServiceLogV(Type, Format, ap); + } + +protected: + /* start/stop */ + virtual NTSTATUS ExceptionHandler() + { + return 0xE06D7363/*STATUS_CPP_EH_EXCEPTION*/; + } + virtual NTSTATUS OnStart(ULONG Argc, PWSTR *Argv) + { + return STATUS_SUCCESS; + } + virtual NTSTATUS OnStop() + { + return STATUS_SUCCESS; + } + +private: + /* callbacks */ + static NTSTATUS OnStart(FSP_SERVICE *Service0, ULONG Argc, PWSTR *Argv) + { + Service *self = (Service *)Service0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->OnStart(Argc, Argv); + ) + } + static NTSTATUS OnStop(FSP_SERVICE *Service0) + { + Service *self = (Service *)Service0->UserContext; + FSP_CPP_EXCEPTION_GUARD( + return self->OnStop(); + ) + } + +private: + /* disallow copy and assignment */ + Service(const Service &); + Service &operator=(const Service &); + +private: + FSP_SERVICE *_Service; +}; + +} + +#undef FSP_CPP_EXCEPTION_GUARD +#undef FSP_CPP_EXCEPTION_GUARD_VOID + +#endif diff --git a/3rd_party/winfsp-1.10/lib/fuse.pc b/3rd_party/winfsp-1.10/lib/fuse.pc new file mode 100644 index 00000000..e61267a1 --- /dev/null +++ b/3rd_party/winfsp-1.10/lib/fuse.pc @@ -0,0 +1,11 @@ +arch=x64 +prefix=${pcfiledir}/.. +incdir=${prefix}/inc/fuse +implib=${prefix}/bin/winfsp-${arch}.dll + +Name: fuse +Description: WinFsp FUSE compatible API +Version: 2.8 +URL: http://www.secfs.net/winfsp/ +Libs: "${implib}" +Cflags: -I"${incdir}" diff --git a/3rd_party/winfsp-1.10/lib/fuse3.pc b/3rd_party/winfsp-1.10/lib/fuse3.pc new file mode 100644 index 00000000..967ce40c --- /dev/null +++ b/3rd_party/winfsp-1.10/lib/fuse3.pc @@ -0,0 +1,11 @@ +arch=x64 +prefix=${pcfiledir}/.. +incdir=${prefix}/inc/fuse3 +implib=${prefix}/bin/winfsp-${arch}.dll + +Name: fuse3 +Description: WinFsp FUSE3 compatible API +Version: 3.2 +URL: http://www.secfs.net/winfsp/ +Libs: "${implib}" +Cflags: -I"${incdir}" diff --git a/3rd_party/winfsp-1.10/lib/winfsp-x64.lib b/3rd_party/winfsp-1.10/lib/winfsp-x64.lib new file mode 100644 index 00000000..a2af257d Binary files /dev/null and b/3rd_party/winfsp-1.10/lib/winfsp-x64.lib differ diff --git a/3rd_party/winfsp-1.10/lib/winfsp-x86.lib b/3rd_party/winfsp-1.10/lib/winfsp-x86.lib new file mode 100644 index 00000000..89a875d4 Binary files /dev/null and b/3rd_party/winfsp-1.10/lib/winfsp-x86.lib differ diff --git a/3rd_party/winfsp-1.10/samples/airfs/airfs.cpp b/3rd_party/winfsp-1.10/samples/airfs/airfs.cpp new file mode 100644 index 00000000..0e8f0b69 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/airfs/airfs.cpp @@ -0,0 +1,1448 @@ +/** + * @file airfs.cpp + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ +/* + * Airfs is based on Memfs with changes contributed by John Oberschelp. + * The contributed changes are under joint copyright by Bill Zissimopoulos + * and John Oberschelp per the Contributor Agreement found at the + * root of this project. + */ + +#include "common.h" + +enum +{ + AirfsDisk = 0x00000000, + AirfsNet = 0x00000001, + AirfsDeviceMask = 0x0000000f, + AirfsCaseInsensitive = 0x80000000, + AirfsFlushAndPurgeOnCleanup = 0x40000000, +}; + +////////////////////////////////////////////////////////////////////// + +NTSTATUS CreateNode(AIRFS_ Airfs, PWSTR Name, NODE_ *PNode) +{ + static UINT64 IndexNumber = 1; + + NODE_ Node = (NODE_) StorageAllocate(Airfs, sizeof NODE); + + if (!Node) + { + *PNode = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + + memset(Node, 0, sizeof NODE); + + size_t NameNumBytes = (wcslen(Name)+1) * sizeof WCHAR; + WCHAR* NameAllocation = (WCHAR*) StorageAllocate(Airfs, NameNumBytes); + if (!NameAllocation) + { + StorageFree(Airfs, Node); + *PNode = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } + memcpy(NameAllocation, Name, NameNumBytes); + Node->Name = NameAllocation; + + Node->FileInfo.CreationTime = + Node->FileInfo.LastAccessTime = + Node->FileInfo.LastWriteTime = + Node->FileInfo.ChangeTime = SystemTime(); + Node->FileInfo.IndexNumber = IndexNumber++; + + *PNode = Node; + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +void DeleteNode(AIRFS_ Airfs, NODE_ Node) +{ + NODE_ Block; + while (Block = Node->FileBlocks) + { + Detach(Node->FileBlocks, Block); + StorageFree(Airfs, Block); + } + + StorageFree(Airfs, Node->Name); + StorageFree(Airfs, Node->ReparseData); + StorageFree(Airfs, Node->SecurityDescriptor); + StorageFree(Airfs, Node); +} + +////////////////////////////////////////////////////////////////////// + +inline void ReferenceNode(NODE_ Node) +{ + InterlockedIncrement(&Node->RefCount); +} + +////////////////////////////////////////////////////////////////////// + +inline void DereferenceNode(AIRFS_ Airfs, NODE_ Node) +{ + if (InterlockedDecrement(&Node->RefCount) == 0) + DeleteNode(Airfs, Node); +} + +////////////////////////////////////////////////////////////////////// + +void GetFileInfo(NODE_ Node, FSP_FSCTL_FILE_INFO *FileInfo) +{ + if (Node->IsAStream) + { + *FileInfo = Node->Parent->FileInfo; + FileInfo->FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY; + // Named streams cannot be directories. + FileInfo->AllocationSize = Node->FileInfo.AllocationSize; + FileInfo->FileSize = Node->FileInfo.FileSize; + } + else + *FileInfo = Node->FileInfo; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS FindNode(AIRFS_ Airfs, PWSTR Name, PWSTR *BaseName, NODE_ *PParent, NODE_ *PNode) +{ + CompareFunction* NodeCmp = Airfs->CaseInsensitive ? CaselessNameCmp : ExactNameCmp; + + // Special case root. + if (Name[0] == 0 || Name[1] == 0) + { + if (BaseName) *BaseName = Name; + if (PParent) *PParent = Airfs->Root; + *PNode = Airfs->Root; + return 0; + } + + WCHAR ParsedName[AIRFS_MAX_PATH]; + wcscpy_s(ParsedName, sizeof ParsedName / sizeof WCHAR, Name); + + // From root, for each ancestor... + NODE_ Ancestor = Airfs->Root; + WCHAR* fm; + WCHAR* to = ParsedName; + WCHAR* Colon = 0; + for (;;) + { + // Isolate the next base name. + for (fm = to+1; *fm == L'\\'; fm++) {} + for (to = fm; *to != L'\0' && *to != L'\\'; to++) + if (*to == ':') {Colon = to; break;} + if (*to == 0) break; + *to = 0; + + // Find this name. + NODE_ Child = Find(Ancestor->Children, fm, NodeCmp); + if (!Child) + { + if (PParent) *PParent = 0; + *PNode = 0; + if (Colon) return STATUS_OBJECT_NAME_NOT_FOUND; + return STATUS_OBJECT_PATH_NOT_FOUND; + } + + Ancestor = Child; + + if (Colon) + { + fm = to+1; + break; + } + + if (!(Ancestor->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + if (PParent) *PParent = 0; + *PNode = 0; + return STATUS_NOT_A_DIRECTORY; + } + } + + if (BaseName) + *BaseName = Name + ( ( (UINT_PTR)fm - (UINT_PTR)ParsedName ) / sizeof WCHAR); + + if (PParent) + *PParent = Ancestor; + + if (Colon) + { + // Find the stream, if it exists. + if (!Ancestor->Streams) + { + *PNode = 0; + return STATUS_OBJECT_NAME_NOT_FOUND; + } + NODE_ Stream = Find(Ancestor->Streams, fm, NodeCmp); + if (!Stream) + { + *PNode = 0; + return STATUS_OBJECT_NAME_NOT_FOUND; + } + *PNode = Stream; + return 0; + } + + // Find the directory entry, if it exists. + NODE_ Found = Find(Ancestor->Children, fm, NodeCmp); + if (!Found) + { + *PNode = 0; + return STATUS_OBJECT_NAME_NOT_FOUND; + } + *PNode = Found; + return 0; +} + +////////////////////////////////////////////////////////////////////// + +BOOLEAN AddStreamInfo(NODE_ Node, PWSTR StreamName, PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + UINT8 StreamInfoBuf[sizeof FSP_FSCTL_STREAM_INFO + AIRFS_MAX_PATH * sizeof WCHAR]; + FSP_FSCTL_STREAM_INFO *StreamInfo = (FSP_FSCTL_STREAM_INFO *)StreamInfoBuf; + + StreamInfo->Size = (UINT16)(sizeof FSP_FSCTL_STREAM_INFO + wcslen(StreamName) * sizeof WCHAR); + StreamInfo->StreamSize = Node->FileInfo.FileSize; + StreamInfo->StreamAllocationSize = Node->FileInfo.AllocationSize; + memcpy(StreamInfo->StreamNameBuf, StreamName, StreamInfo->Size - sizeof FSP_FSCTL_STREAM_INFO); + + return FspFileSystemAddStreamInfo(StreamInfo, Buffer, Length, PBytesTransferred); +} + +////////////////////////////////////////////////////////////////////// + +inline void TouchNode(NODE_ Node) +{ + Node->FileInfo.LastAccessTime = + Node->FileInfo.LastWriteTime = + Node->FileInfo.ChangeTime = SystemTime(); +} + +////////////////////////////////////////////////////////////////////// + +void InsertNode(AIRFS_ Airfs, NODE_ Parent, NODE_ Node) +{ + CompareFunction* NodeCmp = Airfs->CaseInsensitive ? CaselessNameCmp : ExactNameCmp; + + WCHAR* key = Node->Name; + if (Node->IsAStream) Attach(Parent->Streams , Node, NodeCmp, key); + else Attach(Parent->Children , Node, NodeCmp, key); + + Node->Parent = Parent; + ReferenceNode(Node); + TouchNode(Parent); +} + +////////////////////////////////////////////////////////////////////// + +void RemoveNode(AIRFS_ Airfs, NODE_ Node) +{ + NODE_ Parent = Node->Parent; + TouchNode(Parent); + + if (Node->IsAStream) + { + if (Parent->Streams) Detach(Parent->Streams, Node); + } + else + Detach(Parent->Children, Node); + + DereferenceNode(Airfs, Node); +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS GetReparsePointByName(FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PWSTR Name, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node; + + NTSTATUS Result = FindNode(Airfs, Name, 0, 0, &Node); + + if (!Node) return STATUS_OBJECT_NAME_NOT_FOUND; + if (!(Node->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) return STATUS_NOT_A_REPARSE_POINT; + + if (Buffer) + { + if (Node->ReparseDataSize > *PSize) return STATUS_BUFFER_TOO_SMALL; + *PSize = Node->ReparseDataSize; + memcpy(Buffer, Node->ReparseData.Address(), Node->ReparseDataSize); + } + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS SetAllocSize(AIRFS_ Airfs, NODE_ Node, UINT64 RequestedAllocSize) +{ + NTSTATUS Result = StorageSetFileCapacity(Airfs, Node, RequestedAllocSize); + if (!Result) + { + Node->FileInfo.AllocationSize = RequestedAllocSize; + if (Node->FileInfo.FileSize > Node->FileInfo.AllocationSize) + Node->FileInfo.FileSize = Node->FileInfo.AllocationSize; + + } + return Result; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS SetFileSize(AIRFS_ Airfs, NODE_ Node, UINT64 RequestedFileSize) +{ + if (Node->FileInfo.FileSize != RequestedFileSize) + { + if (Node->FileInfo.AllocationSize < RequestedFileSize) + { + NTSTATUS Result = SetAllocSize(Airfs, Node, RequestedFileSize); + if (!NT_SUCCESS(Result)) + return Result; + } + + if (Node->FileInfo.FileSize < RequestedFileSize) + StorageAccessFile(ZERO, Node, Node->FileInfo.FileSize, RequestedFileSize - Node->FileInfo.FileSize, 0); + + Node->FileInfo.FileSize = RequestedFileSize; + } + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +BOOLEAN AddDirInfo(NODE_ Node, PWSTR Name, PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + size_t NameBytes = wcslen(Name) * sizeof WCHAR; + UINT8 DirInfoBuf[sizeof FSP_FSCTL_DIR_INFO + AIRFS_MAX_PATH * sizeof WCHAR]; + FSP_FSCTL_DIR_INFO *DirInfo = (FSP_FSCTL_DIR_INFO *)DirInfoBuf; + DirInfo->Size = (UINT16)(sizeof FSP_FSCTL_DIR_INFO + NameBytes); + DirInfo->FileInfo = Node->FileInfo; + memset(DirInfo->Padding, 0, sizeof DirInfo->Padding); + memcpy(DirInfo->FileNameBuf, Name, NameBytes + sizeof WCHAR); + + return FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred); +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiGetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + + VolumeInfo->TotalSize = Airfs->VolumeSize; + VolumeInfo->FreeSize = Airfs->FreeSize; + VolumeInfo->VolumeLabelLength = Airfs->VolumeLabelLength; + memcpy(VolumeInfo->VolumeLabel, Airfs->VolumeLabel, Airfs->VolumeLabelLength); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiSetVolumeLabel(FSP_FILE_SYSTEM *FileSystem, PWSTR VolumeLabel, + FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + + Airfs->VolumeLabelLength = (UINT16)(wcslen(VolumeLabel) * sizeof WCHAR); + if (Airfs->VolumeLabelLength > sizeof Airfs->VolumeLabel) + Airfs->VolumeLabelLength = sizeof Airfs->VolumeLabel; + memcpy(Airfs->VolumeLabel, VolumeLabel, Airfs->VolumeLabelLength); + + VolumeInfo->TotalSize = Airfs->VolumeSize; + VolumeInfo->FreeSize = Airfs->FreeSize; + VolumeInfo->VolumeLabelLength = Airfs->VolumeLabelLength; + memcpy(VolumeInfo->VolumeLabel, Airfs->VolumeLabel, Airfs->VolumeLabelLength); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiGetSecurityByName(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, PUINT32 PFileAttributes, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node; + NTSTATUS Result = FindNode(Airfs, Name, 0, 0, &Node); + if (!Node) + { + if (FspFileSystemFindReparsePoint(FileSystem, GetReparsePointByName, 0, + Name, PFileAttributes)) + { + return STATUS_REPARSE; + } + return Result; + } + + UINT32 FileAttributesMask = ~(UINT32)0; + if (Node->IsAStream) + { + FileAttributesMask = ~(UINT32)FILE_ATTRIBUTE_DIRECTORY; + Node = Node->Parent; + } + + if (PFileAttributes) + *PFileAttributes = Node->FileInfo.FileAttributes & FileAttributesMask; + + if (PSecurityDescriptorSize) + { + if (Node->SecurityDescriptorSize > *PSecurityDescriptorSize) + { + *PSecurityDescriptorSize = Node->SecurityDescriptorSize; + return STATUS_BUFFER_OVERFLOW; + } + + *PSecurityDescriptorSize = Node->SecurityDescriptorSize; + if (SecurityDescriptor) + memcpy(SecurityDescriptor, Node->SecurityDescriptor.Address(), Node->SecurityDescriptorSize); + } + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiCreate(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, UINT32 CreateOptions, + UINT32 GrantedAccess, UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, + UINT64 AllocationSize, PVOID *PNode, FSP_FSCTL_FILE_INFO *FileInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node; + NODE_ Parent; + NTSTATUS Result; + PWSTR BaseName; + + if (AIRFS_MAX_PATH <= wcslen(Name)) + return STATUS_OBJECT_NAME_INVALID; + + if (CreateOptions & FILE_DIRECTORY_FILE) + AllocationSize = 0; + + Result = FindNode(Airfs, Name, &BaseName, &Parent, &Node); + if (Node) return STATUS_OBJECT_NAME_COLLISION; + if (!Parent) return Result; + + Result = CreateNode(Airfs, BaseName, &Node); + if (!NT_SUCCESS(Result)) return Result; + + Node->IsAStream = BaseName[-1] == L':'; + + Node->FileInfo.FileAttributes = (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? + FileAttributes : FileAttributes | FILE_ATTRIBUTE_ARCHIVE; + + if (SecurityDescriptor) + { + Node->SecurityDescriptorSize = GetSecurityDescriptorLength(SecurityDescriptor); + Node->SecurityDescriptor = StorageAllocate(Airfs, (int)Node->SecurityDescriptorSize); + if (!Node->SecurityDescriptor) + { + DeleteNode(Airfs, Node); + return STATUS_INSUFFICIENT_RESOURCES; + } + memcpy(Node->SecurityDescriptor.Address(), SecurityDescriptor, Node->SecurityDescriptorSize); + } + + if (AllocationSize) + { + NTSTATUS Result = SetAllocSize(Airfs, Node, AllocationSize); + if (Result) + { + DeleteNode(Airfs, Node); + return Result; + } + } + + InsertNode(Airfs, Parent, Node); + ReferenceNode(Node); + *PNode = Node; + GetFileInfo(Node, FileInfo); + + if (Airfs->CaseInsensitive) + { + int ParentPathNumBytes = (int)( (char*)BaseName - (char*)Name ); + int NodeNameNumBytes = (int)( wcslen(Node->Name) * sizeof WCHAR ); + + FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = FspFileSystemGetOpenFileInfo(FileInfo); + memcpy(OpenFileInfo->NormalizedName, Name, ParentPathNumBytes); + memcpy(OpenFileInfo->NormalizedName+ParentPathNumBytes / sizeof WCHAR, Node->Name, NodeNameNumBytes); + OpenFileInfo->NormalizedNameSize = ParentPathNumBytes + NodeNameNumBytes; + } + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiOpen(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, UINT32 CreateOptions, + UINT32 GrantedAccess, PVOID *PNode, FSP_FSCTL_FILE_INFO *FileInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node; + NTSTATUS Result; + + if (AIRFS_MAX_PATH <= wcslen(Name)) + return STATUS_OBJECT_NAME_INVALID; + + PWSTR baseName; + Result = FindNode(Airfs, Name, &baseName, 0, &Node); + if (Result) return Result; + if (!Node) + { + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + ReferenceNode(Node); + *PNode = Node; + GetFileInfo(Node, FileInfo); + + if (Airfs->CaseInsensitive) + { + int ParentPathNumBytes = (int)( (char*)baseName - (char*)Name ); + int NodeNameNumBytes = (int)( wcslen(Node->Name) * sizeof WCHAR ); + + FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = FspFileSystemGetOpenFileInfo(FileInfo); + memcpy(OpenFileInfo->NormalizedName, Name, ParentPathNumBytes); + memcpy(OpenFileInfo->NormalizedName+ParentPathNumBytes / sizeof WCHAR, Node->Name, NodeNameNumBytes); + OpenFileInfo->NormalizedNameSize = ParentPathNumBytes + NodeNameNumBytes; + } + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiOverwrite(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, UINT32 FileAttributes, + BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, FSP_FSCTL_FILE_INFO *FileInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node = (NODE_) Node0; + + for (NODE_ Stream = First(Node->Streams); Stream; ) + { + NODE_ NextStream = Next(Stream); + LONG RefCount = Stream->RefCount; + MemoryBarrier(); + if (RefCount <= 1) + { + RemoveNode(Airfs, Stream); + } + Stream = NextStream; + } + + NTSTATUS Result = SetAllocSize(Airfs, Node, AllocationSize); + if (!NT_SUCCESS(Result)) + return Result; + + if (ReplaceFileAttributes) Node->FileInfo.FileAttributes = FileAttributes | FILE_ATTRIBUTE_ARCHIVE; + else Node->FileInfo.FileAttributes |= FileAttributes | FILE_ATTRIBUTE_ARCHIVE; + + Node->FileInfo.FileSize = 0; + Node->FileInfo.LastAccessTime = + Node->FileInfo.LastWriteTime = + Node->FileInfo.ChangeTime = SystemTime(); + + GetFileInfo(Node, FileInfo); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +void ApiCleanup(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name, ULONG Flags) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node = (NODE_) Node0; + + NODE_ MainNode = Node->IsAStream ? Node->Parent : Node; + + if (Flags & FspCleanupSetArchiveBit) + { + if (!(MainNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + MainNode->FileInfo.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; + } + + if (Flags & (FspCleanupSetLastAccessTime | FspCleanupSetLastWriteTime | FspCleanupSetChangeTime)) + { + UINT64 Time = SystemTime(); + if (Flags & FspCleanupSetLastAccessTime ) MainNode->FileInfo.LastAccessTime = Time; + if (Flags & FspCleanupSetLastWriteTime ) MainNode->FileInfo.LastWriteTime = Time; + if (Flags & FspCleanupSetChangeTime ) MainNode->FileInfo.ChangeTime = Time; + } + + if (Flags & FspCleanupSetAllocationSize) + { + SetAllocSize(Airfs, Node, Node->FileInfo.FileSize); + } + + if ((Flags & FspCleanupDelete) && !Node->Children) + { + NODE_ Stream; + while (Stream = Node->Streams) + { + Detach(Node->Streams, Stream); + DeleteNode(Airfs, Stream); + } + RemoveNode(Airfs, Node); + } +} + +////////////////////////////////////////////////////////////////////// + +void ApiClose(FSP_FILE_SYSTEM *FileSystem, PVOID Node0) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node = (NODE_) Node0; + DereferenceNode(Airfs, Node); +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiRead(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PVOID Buffer, + UINT64 BegOffset, ULONG Length, PULONG PBytesTransferred) +{ + NODE_ Node = (NODE_) Node0; + UINT64 EndOffset; + + if (BegOffset >= Node->FileInfo.FileSize) + return STATUS_END_OF_FILE; + + EndOffset = BegOffset + Length; + if (EndOffset > Node->FileInfo.FileSize) + EndOffset = Node->FileInfo.FileSize; + + StorageAccessFile(READ, Node, BegOffset, EndOffset - BegOffset, (char*)Buffer); + + *PBytesTransferred = (ULONG)(EndOffset - BegOffset); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiWrite(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PVOID Buffer, + UINT64 BegOffset, ULONG Length, BOOLEAN WriteToEndOfFile, + BOOLEAN ConstrainedIo, PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO *FileInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node = (NODE_) Node0; + UINT64 EndOffset; + NTSTATUS Result; + + if (ConstrainedIo) + { + if (BegOffset >= Node->FileInfo.FileSize) + return STATUS_SUCCESS; + EndOffset = BegOffset + Length; + if (EndOffset > Node->FileInfo.FileSize) + EndOffset = Node->FileInfo.FileSize; + } + else + { + if (WriteToEndOfFile) + BegOffset = Node->FileInfo.FileSize; + EndOffset = BegOffset + Length; + if (EndOffset > Node->FileInfo.FileSize) + { + Result = SetFileSize(Airfs, Node, EndOffset); + if (!NT_SUCCESS(Result)) + return Result; + } + } + + StorageAccessFile(WRITE, Node, BegOffset, EndOffset - BegOffset, (char*)Buffer); + + *PBytesTransferred = (ULONG)(EndOffset - BegOffset); + GetFileInfo(Node, FileInfo); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiFlush(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, FSP_FSCTL_FILE_INFO *FileInfo) +{ + NODE_ Node = (NODE_) Node0; + + if (Node) GetFileInfo(Node, FileInfo); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiGetFileInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + NODE_ Node = (NODE_) Node0; + + GetFileInfo(Node, FileInfo); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiSetBasicInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, UINT32 FileAttributes, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + NODE_ Node = (NODE_) Node0; + + if (Node->IsAStream) Node = Node->Parent; + + if (INVALID_FILE_ATTRIBUTES != FileAttributes) + Node->FileInfo.FileAttributes = FileAttributes; + if (CreationTime ) Node->FileInfo.CreationTime = CreationTime; + if (LastAccessTime ) Node->FileInfo.LastAccessTime = LastAccessTime; + if (LastWriteTime ) Node->FileInfo.LastWriteTime = LastWriteTime; + if (ChangeTime ) Node->FileInfo.ChangeTime = ChangeTime; + + GetFileInfo(Node, FileInfo); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiSetFileSize(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + UINT64 NewSize, BOOLEAN SetAllocationSize, FSP_FSCTL_FILE_INFO *FileInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node = (NODE_) Node0; + NTSTATUS Result = SetAllocationSize + ? SetAllocSize (Airfs, Node, NewSize) + : SetFileSize (Airfs, Node, NewSize); + if (!NT_SUCCESS(Result)) + return Result; + + GetFileInfo(Node, FileInfo); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiCanDelete(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name) +{ + NODE_ Node = (NODE_) Node0; + + if (Node->Children) + return STATUS_DIRECTORY_NOT_EMPTY; + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiRename(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Name, + PWSTR NewFileName, BOOLEAN ReplaceIfExists) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node = (NODE_) Node0; + NODE_ NewNode; + NTSTATUS Result; + PWSTR NewBaseName; + NODE_ NewParent; + Result = FindNode(Airfs, NewFileName, &NewBaseName, &NewParent, &NewNode); + + // Create the replacement name. + size_t NewBaseNameNumBytes = (wcslen(NewBaseName)+1) * sizeof WCHAR; + WCHAR* NewBaseNameAlloc = (WCHAR*) StorageAllocate(Airfs, NewBaseNameNumBytes); + if (!NewBaseNameAlloc) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + memcpy(NewBaseNameAlloc, NewBaseName, NewBaseNameNumBytes); + + if (NewNode && Node != NewNode) + { + if (!ReplaceIfExists) + { + StorageFree(Airfs, NewBaseNameAlloc); + return STATUS_OBJECT_NAME_COLLISION; + } + + if (NewNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + StorageFree(Airfs, NewBaseNameAlloc); + return STATUS_ACCESS_DENIED; + } + + ReferenceNode(NewNode); + RemoveNode(Airfs, NewNode); + DereferenceNode(Airfs, NewNode); + } + + ReferenceNode(Node); + RemoveNode(Airfs, Node); + + StorageFree(Airfs, Node->Name); + Node->Name = NewBaseNameAlloc; + + InsertNode(Airfs, NewParent, Node); + DereferenceNode(Airfs, Node); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiGetSecurity(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + NODE_ Node = (NODE_) Node0; + + if (Node->IsAStream) Node = Node->Parent; + + if (Node->SecurityDescriptorSize > *PSecurityDescriptorSize) + { + *PSecurityDescriptorSize = Node->SecurityDescriptorSize; + return STATUS_BUFFER_OVERFLOW; + } + + *PSecurityDescriptorSize = Node->SecurityDescriptorSize; + if (SecurityDescriptor) + memcpy(SecurityDescriptor, Node->SecurityDescriptor.Address(), Node->SecurityDescriptorSize); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiSetSecurity(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ModificationDescriptor) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node = (NODE_) Node0; + PSECURITY_DESCRIPTOR NewSecurityDescriptor, MallocSecurityDescriptor; + + if (Node->IsAStream) Node = Node->Parent; + + NTSTATUS Result = FspSetSecurityDescriptor( + Node->SecurityDescriptor.Address(), + SecurityInformation, + ModificationDescriptor, + &NewSecurityDescriptor); + if (!NT_SUCCESS(Result)) + return Result; + + uint64_t SecurityDescriptorSize = GetSecurityDescriptorLength(NewSecurityDescriptor); + MallocSecurityDescriptor = (PSECURITY_DESCRIPTOR) malloc(SecurityDescriptorSize); + if (!MallocSecurityDescriptor) + { + FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor); + return STATUS_INSUFFICIENT_RESOURCES; + } + memcpy(MallocSecurityDescriptor, NewSecurityDescriptor, SecurityDescriptorSize); + FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor); + + NODE_ NewHunk = (NODE_) StorageReallocate(Airfs, Node->SecurityDescriptor, SecurityDescriptorSize); + if (!NewHunk && SecurityDescriptorSize) + { + free(MallocSecurityDescriptor); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Node->SecurityDescriptorSize = SecurityDescriptorSize; + if (!SecurityDescriptorSize) Node->SecurityDescriptor = 0; + else + { + Node->SecurityDescriptor = NewHunk; + memcpy(Node->SecurityDescriptor.Address(), MallocSecurityDescriptor, SecurityDescriptorSize); + } + + free(MallocSecurityDescriptor); + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiReadDirectory(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, PWSTR Pattern, + PWSTR Marker, PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + + CompareFunction* NodeCmp = Airfs->CaseInsensitive ? CaselessNameCmp : ExactNameCmp; + + NODE_ Node = (NODE_) Node0; + NODE_ Parent = Node->Parent; + if (Parent) + { + // If this is not the root directory, add the dot entries. + if (!Marker) + { + if (!AddDirInfo(Node, L".", Buffer, Length, PBytesTransferred)) + return STATUS_SUCCESS; + } + if (!Marker || (L'.' == Marker[0] && L'\0' == Marker[1])) + { + if (!AddDirInfo(Parent, L"..", Buffer, Length, PBytesTransferred)) + return STATUS_SUCCESS; + Marker = 0; + } + } + + // Find the next Child, even if the Marker child no longer exists. + NODE_ Child; + if (!Marker) Child = First(Node->Children); + else Child = Near(Node->Children, Marker, NodeCmp, GT); + + for (; Child; Child = Next(Child)) + { + if (!AddDirInfo(Child, Child->Name, Buffer, Length, PBytesTransferred)) + goto bufferReady; + } + FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred); + + bufferReady: + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiGetDirInfoByName(FSP_FILE_SYSTEM *FileSystem, PVOID ParentNode0, + PWSTR Name, FSP_FSCTL_DIR_INFO *DirInfo) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + + CompareFunction* NodeCmp = Airfs->CaseInsensitive ? CaselessNameCmp : ExactNameCmp; + + NODE_ Parent = (NODE_) ParentNode0; + NODE_ Node = Find(Parent->Children, Name, NodeCmp); + if (!Node) + return STATUS_OBJECT_NAME_NOT_FOUND; + + DirInfo->Size = (UINT16)(sizeof FSP_FSCTL_DIR_INFO + wcslen(Node->Name) * sizeof WCHAR); + DirInfo->FileInfo = Node->FileInfo; + memcpy(DirInfo->FileNameBuf, Node->Name, DirInfo->Size - sizeof FSP_FSCTL_DIR_INFO); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, PWSTR Name, + UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent, + PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize) +{ + return FspFileSystemResolveReparsePoints(FileSystem, GetReparsePointByName, 0, + Name, ReparsePointIndex, ResolveLastPathComponent, + PIoStatus, Buffer, PSize); +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiGetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + PWSTR Name, PVOID Buffer, PSIZE_T PSize) +{ + NODE_ Node = (NODE_) Node0; + + if (Node->IsAStream) Node = Node->Parent; + + if (!(Node->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + return STATUS_NOT_A_REPARSE_POINT; + + if (Node->ReparseDataSize > *PSize) + return STATUS_BUFFER_TOO_SMALL; + + *PSize = Node->ReparseDataSize; + memcpy(Buffer, Node->ReparseData.Address(), Node->ReparseDataSize); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiSetReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + PWSTR Name, PVOID Buffer, SIZE_T Size) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node = (NODE_) Node0; + NTSTATUS Result; + + if (Node->IsAStream) Node = Node->Parent; + + if (Node->Children) + return STATUS_DIRECTORY_NOT_EMPTY; + + if (Node->ReparseData) + { + Result = FspFileSystemCanReplaceReparsePoint( + Node->ReparseData.Address(), Node->ReparseDataSize, + Buffer, Size); + if (!NT_SUCCESS(Result)) + return Result; + } + + NODE_ ReparseData = (NODE_) StorageReallocate(Airfs, Node->ReparseData, Size); + if (!ReparseData && Size) + return STATUS_INSUFFICIENT_RESOURCES; + + Node->FileInfo.FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; + Node->FileInfo.ReparseTag = *(PULONG)Buffer; + // The first field in a reparse buffer is the reparse tag. + Node->ReparseDataSize = Size; + Node->ReparseData = ReparseData; + memcpy(Node->ReparseData.Address(), Buffer, Size); + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiDeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + PWSTR Name, PVOID Buffer, SIZE_T Size) +{ + AIRFS_ Airfs = (AIRFS_) FileSystem->UserContext; + NODE_ Node = (NODE_) Node0; + NTSTATUS Result; + + if (Node->IsAStream) Node = Node->Parent; + + if (Node->ReparseData) + { + Result = FspFileSystemCanReplaceReparsePoint( + Node->ReparseData.Address(), Node->ReparseDataSize, + Buffer, Size); + if (!NT_SUCCESS(Result)) + return Result; + } + else + return STATUS_NOT_A_REPARSE_POINT; + + StorageFree(Airfs, Node->ReparseData); + + Node->FileInfo.FileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT; + Node->FileInfo.ReparseTag = 0; + Node->ReparseDataSize = 0; + Node->ReparseData = 0; + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiGetStreamInfo(FSP_FILE_SYSTEM *FileSystem, PVOID Node0, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + NODE_ Node = (NODE_) Node0; + + if (Node->IsAStream) Node = Node->Parent; + + if (!(Node->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + if (!AddStreamInfo(Node, L"", Buffer, Length, PBytesTransferred)) + return STATUS_SUCCESS; + + for (NODE_ Stream = First(Node->Streams); Stream; Stream = Next(Stream)) + { + BOOLEAN added = AddStreamInfo(Stream, Stream->Name, Buffer, Length, PBytesTransferred); + if (!added) goto done; + } + + FspFileSystemAddStreamInfo(0, Buffer, Length, PBytesTransferred); + + done: + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS ApiControl(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 ControlCode, + PVOID InputBuffer, ULONG InputBufferLength, + PVOID OutputBuffer, ULONG OutputBufferLength, PULONG PBytesTransferred) +{ + // Trivial example: Perform a ROT13 translation on alphas. + if (CTL_CODE(0x8000 + 'M', 'R', METHOD_BUFFERED, FILE_ANY_ACCESS) == ControlCode) + { + if (OutputBufferLength != InputBufferLength) return STATUS_INVALID_PARAMETER; + for (ULONG i = 0; i < InputBufferLength; i++) + { + char c = ((char*)InputBuffer)[i]; + if (('A' <= c && c <= 'M') || ('a' <= c && c <= 'm')) c += 13; + else + if (('N' <= c && c <= 'Z') || ('n' <= c && c <= 'z')) c -= 13; + ((char*)OutputBuffer)[i] = c; + } + *PBytesTransferred = InputBufferLength; + return STATUS_SUCCESS; + } + + return STATUS_INVALID_DEVICE_REQUEST; +} + +////////////////////////////////////////////////////////////////////// + +FSP_FILE_SYSTEM_INTERFACE AirfsInterface = +{ + ApiGetVolumeInfo, + ApiSetVolumeLabel, + ApiGetSecurityByName, + ApiCreate, + ApiOpen, + ApiOverwrite, + ApiCleanup, + ApiClose, + ApiRead, + ApiWrite, + ApiFlush, + ApiGetFileInfo, + ApiSetBasicInfo, + ApiSetFileSize, + ApiCanDelete, + ApiRename, + ApiGetSecurity, + ApiSetSecurity, + ApiReadDirectory, + ApiResolveReparsePoints, + ApiGetReparsePoint, + ApiSetReparsePoint, + ApiDeleteReparsePoint, + ApiGetStreamInfo, + ApiGetDirInfoByName, + ApiControl, +}; + +////////////////////////////////////////////////////////////////////// + +void AirfsDelete(AIRFS_ Airfs) +{ + FspFileSystemDelete(Airfs->FileSystem); + StorageShutdown(Airfs); +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS AirfsCreate( + PWSTR StorageFileName, + PWSTR MapName, + ULONG Flags, + ULONG FileInfoTimeout, + UINT64 VolumeSize, + PWSTR FileSystemName, + PWSTR VolumePrefix, + PWSTR RootSddl, + AIRFS_ *PAirfs) +{ + NTSTATUS Result; + BOOLEAN CaseInsensitive = !!(Flags & AirfsCaseInsensitive); + BOOLEAN FlushAndPurgeOnCleanup = !!(Flags & AirfsFlushAndPurgeOnCleanup); + PWSTR DevicePath = AirfsNet == (Flags & AirfsDeviceMask) ? + L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME; + AIRFS_ Airfs; + PSECURITY_DESCRIPTOR RootSecurity = 0; + ULONG RootSecuritySize; + + *PAirfs = 0; + + boolean StorageFileExists = *StorageFileName && (_waccess(StorageFileName, 0) != -1); + + Result = StorageStartup(Airfs, MapName, StorageFileName, VolumeSize); + if (Result) return Result; + + boolean ShouldFormat = !StorageFileExists || memcmp(Airfs->Signature, "Airfs\0\0\0", 8); + + if (ShouldFormat) + { + memcpy(Airfs->Signature,"Airfs\0\0\0" "\1\0\0\0" "\0\0\0\0", 16); + + if (!RootSddl) + RootSddl = L"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)"; + if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(RootSddl, SDDL_REVISION_1, + &RootSecurity, &RootSecuritySize)) + return GetLastErrorAsStatus(); + + Airfs->VolumeSize = ROUND_DOWN(VolumeSize, ALLOCATION_UNIT); + Airfs->CaseInsensitive = CaseInsensitive; + Airfs->VolumeLabelLength = sizeof L"AIRFS" - sizeof WCHAR; + memcpy(Airfs->VolumeLabel, L"AIRFS", Airfs->VolumeLabelLength); + + FSP_FSCTL_VOLUME_PARAMS V; + memset(&V, 0, sizeof V); + V.Version = sizeof FSP_FSCTL_VOLUME_PARAMS; + V.SectorSize = SECTOR_SIZE; + V.SectorsPerAllocationUnit = SECTORS_PER_ALLOCATION_UNIT; + V.VolumeCreationTime = SystemTime(); + V.VolumeSerialNumber = (UINT32)(SystemTime() / (10000 * 1000)); + V.FileInfoTimeout = FileInfoTimeout; + V.CaseSensitiveSearch = !CaseInsensitive; + V.CasePreservedNames = 1; + V.UnicodeOnDisk = 1; + V.PersistentAcls = 1; + V.ReparsePoints = 1; + V.ReparsePointsAccessCheck = 0; + V.NamedStreams = 1; + V.PostCleanupWhenModifiedOnly = 1; + V.PassQueryDirectoryFileName = 1; + V.FlushAndPurgeOnCleanup = FlushAndPurgeOnCleanup; + V.DeviceControl = 1; + wcscpy_s(V.Prefix, sizeof V.Prefix / sizeof WCHAR, VolumePrefix); + wcscpy_s(V.FileSystemName, sizeof V.FileSystemName / sizeof WCHAR, + FileSystemName ? FileSystemName : L"-AIRFS"); + Airfs->VolumeParams = V; + + // Set up the available storage in chunks. + Airfs->FreeSize = 0; + Airfs->Available = 0; + uint64_t to = 4096; + for (;;) + { + uint64_t fm = to + sizeof int32_t; + to = fm + MAXIMUM_ALLOCSIZE; + if (to > Airfs->VolumeSize - MINIMUM_ALLOCSIZE) to = Airfs->VolumeSize; + int32_t Size = (int32_t)( to - fm ); + char* Addr = (char*)Airfs + fm;// + sizeof int32_t; + NODE_ NewItem = (NODE_) Addr; + ((int32_t*)NewItem)[-1] = Size; + Attach(Airfs->Available, NewItem, SizeCmp, &Size); + Airfs->FreeSize += Size; + if (to == Airfs->VolumeSize) break; + } + + // Create the root directory. + Airfs->Root = 0; + NODE_ RootNode; + Result = CreateNode(Airfs, L"", &RootNode); + if (!NT_SUCCESS(Result)) + { + AirfsDelete(Airfs); + LocalFree(RootSecurity); + return Result; + } + RootNode->P = RootNode->L = RootNode->R = RootNode->E = RootNode->Parent = 0; + RootNode->FileInfo.FileAttributes = FILE_ATTRIBUTE_DIRECTORY; + RootNode->SecurityDescriptor = StorageAllocate(Airfs, RootSecuritySize); + if (!RootNode->SecurityDescriptor) + { + DeleteNode(Airfs, RootNode); + AirfsDelete(Airfs); + LocalFree(RootSecurity); + return STATUS_INSUFFICIENT_RESOURCES; + } + RootNode->SecurityDescriptorSize = RootSecuritySize; + memcpy(RootNode->SecurityDescriptor.Address(), RootSecurity, RootSecuritySize); + Airfs->Root = RootNode; + ReferenceNode(RootNode); + LocalFree(RootSecurity); + } + + Result = FspFileSystemCreate(DevicePath, &Airfs->VolumeParams, &AirfsInterface, &Airfs->FileSystem); + if (!NT_SUCCESS(Result)) + { + LocalFree(RootSecurity); + return Result; + } + + Airfs->FileSystem->UserContext = Airfs; + + *PAirfs = Airfs; + + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) +{ + wchar_t **argp, **arge; + ULONG DebugFlags = 0; + PWSTR DebugLogFile = 0; + ULONG Flags = AirfsDisk; + ULONG OtherFlags = 0; + ULONG FileInfoTimeout = INFINITE; + PWSTR StorageFileName = L""; + PWSTR MapName = L""; + UINT64 VolumeSize = 16LL * 1024 * 1024; + PWSTR FileSystemName = 0; + PWSTR MountPoint = 0; + PWSTR VolumePrefix = L""; + PWSTR RootSddl = 0; + HANDLE DebugLogHandle = INVALID_HANDLE_VALUE; + AIRFS_ Airfs = 0; + NTSTATUS Result; + + for (argp = argv + 1, arge = argv + argc; arge > argp; argp++) + { + if (L'-' != argp[0][0]) + break; + switch (argp[0][1]) + { + case L'?': goto usage; + case L'd': ARG_TO_4(DebugFlags); break; + case L'D': ARG_TO_S(DebugLogFile); break; + case L'f': OtherFlags = AirfsFlushAndPurgeOnCleanup; break; + case L'F': ARG_TO_S(FileSystemName); break; + case L'i': OtherFlags = AirfsCaseInsensitive; break; + case L'm': ARG_TO_S(MountPoint); break; + case L'N': ARG_TO_S(StorageFileName); break; + case L'n': ARG_TO_S(MapName); break; + case L'S': ARG_TO_S(RootSddl); break; + case L's': ARG_TO_8(VolumeSize); break; + case L't': ARG_TO_4(FileInfoTimeout); break; + case L'u': + ARG_TO_S(VolumePrefix); + if (*VolumePrefix) Flags = AirfsNet; + break; + default: + goto usage; + } + } + + if (arge > argp) + goto usage; + + if (AirfsDisk == Flags && !MountPoint) + goto usage; + + if (DebugLogFile) + { + if (!wcscmp(L"-", DebugLogFile)) + DebugLogHandle = GetStdHandle(STD_ERROR_HANDLE); + else + DebugLogHandle = CreateFileW( + DebugLogFile, + FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0); + if (INVALID_HANDLE_VALUE == DebugLogHandle) + { + FAIL(L"cannot open debug log file"); + goto usage; + } + + FspDebugLogSetHandle(DebugLogHandle); + } + + Result = AirfsCreate( + StorageFileName, + MapName, + Flags | OtherFlags, + FileInfoTimeout, + VolumeSize, + FileSystemName, + VolumePrefix, + RootSddl, + &Airfs); + + if (!NT_SUCCESS(Result)) + { + FAIL(L"cannot create AIRFS"); + goto exit; + } + + FspFileSystemSetDebugLog(Airfs->FileSystem, DebugFlags); + + if (MountPoint && L'\0' != MountPoint[0]) + { + Result = FspFileSystemSetMountPoint(Airfs->FileSystem, + L'*' == MountPoint[0] && L'\0' == MountPoint[1] ? 0 : MountPoint); + if (!NT_SUCCESS(Result)) + { + FAIL(L"cannot mount AIRFS"); + goto exit; + } + } + + Result = FspFileSystemStartDispatcher(Airfs->FileSystem, 0); + if (!NT_SUCCESS(Result)) + { + FAIL(L"cannot start AIRFS"); + goto exit; + } + + MountPoint = FspFileSystemMountPoint(Airfs->FileSystem); + + WCHAR buffer[1024]; + _snwprintf_s(buffer, 1024, L"%S%S%s%S%s -t %ld -s %lld%S%s%S%s%S%s", + PROGNAME, + *StorageFileName ? " -N " : "", *StorageFileName ? StorageFileName : L"", + *MapName ? " -n " : "", *MapName ? MapName : L"", + FileInfoTimeout, VolumeSize, + RootSddl ? " -S " : "", RootSddl ? RootSddl : L"", + *VolumePrefix ? " -u " : "", *VolumePrefix ? VolumePrefix : L"", + MountPoint ? " -m " : "", MountPoint ? MountPoint : L""); + INFO(buffer); + + Service->UserContext = Airfs; + Result = STATUS_SUCCESS; + + exit: + + if (!NT_SUCCESS(Result) && Airfs) + AirfsDelete(Airfs); + return Result; + + usage: + + static wchar_t usage[] = L"" + "usage: %s OPTIONS\n" + "\n" + "options:\n" + " -d DebugFlags [-1: enable all debug logs]\n" + " -D DebugLogFile [file path; use - for stderr]\n" + " -f [flush and purge cache on cleanup]\n" + " -F FileSystemName\n" + " -i [case insensitive file system]\n" + " -m MountPoint [X:|* (required if no UNC prefix)]\n" + " -n MapName [(ex) \"Local\\Airfs\"]\n" + " -N StorageFileName [\"\": in memory only]\n" + " -s VolumeSize [bytes]\n" + " -S RootSddl [file rights: FA, etc; NO generic rights: GA, etc.]\n" + " -t FileInfoTimeout [millis]\n" + " -u \\Server\\Share [UNC prefix (single backslash)]\n"; + + FAIL(usage, L"" PROGNAME); + + return STATUS_UNSUCCESSFUL; +} + +////////////////////////////////////////////////////////////////////// + +NTSTATUS SvcStop(FSP_SERVICE *Service) +{ + AIRFS_ Airfs = (AIRFS_) Service->UserContext; + FspFileSystemStopDispatcher(Airfs->FileSystem); + AirfsDelete(Airfs); + return STATUS_SUCCESS; +} + +////////////////////////////////////////////////////////////////////// + +int wmain(int argc, wchar_t **argv) +{ + if (!NT_SUCCESS(FspLoad(0))) + return ERROR_DELAY_LOAD_FAILED; + + return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0); +} + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// diff --git a/3rd_party/winfsp-1.10/samples/airfs/airfs.sln b/3rd_party/winfsp-1.10/samples/airfs/airfs.sln new file mode 100644 index 00000000..b0dd506b --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/airfs/airfs.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "airfs", "airfs.vcxproj", "{CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Debug|x64.ActiveCfg = Debug|x64 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Debug|x64.Build.0 = Debug|x64 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Debug|x86.ActiveCfg = Debug|Win32 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Debug|x86.Build.0 = Debug|Win32 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Release|x64.ActiveCfg = Release|x64 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Release|x64.Build.0 = Release|x64 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Release|x86.ActiveCfg = Release|Win32 + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/3rd_party/winfsp-1.10/samples/airfs/airfs.vcxproj b/3rd_party/winfsp-1.10/samples/airfs/airfs.vcxproj new file mode 100644 index 00000000..aa326b62 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/airfs/airfs.vcxproj @@ -0,0 +1,182 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {CA441CE7-C4DE-4B5E-AA72-D4D483413EF0} + Win32Proj + airfs + $(LatestTargetPlatformVersion) + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + + + + + + + diff --git a/3rd_party/winfsp-1.10/samples/airfs/airfs.vcxproj.filters b/3rd_party/winfsp-1.10/samples/airfs/airfs.vcxproj.filters new file mode 100644 index 00000000..69468e85 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/airfs/airfs.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source + + + Source + + + + + Source + + + diff --git a/3rd_party/winfsp-1.10/samples/airfs/common.h b/3rd_party/winfsp-1.10/samples/airfs/common.h new file mode 100644 index 00000000..2476d07e --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/airfs/common.h @@ -0,0 +1,210 @@ +/** + * @file common.h + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ +/* + * Airfs is based on Memfs with changes contributed by John Oberschelp. + * The contributed changes are under joint copyright by Bill Zissimopoulos + * and John Oberschelp per the Contributor Agreement found at the + * root of this project. + */ + +#include +#include +#include +#include +#include + +#define PROGNAME "airfs" +#define ROUND_UP( bytes, units ) (((bytes) + (units) - 1) / (units) * (units)) +#define ROUND_DOWN( bytes, units ) (((bytes) ) / (units) * (units)) +#define MINIMUM_ALLOCSIZE 196 +#define MAXIMUM_ALLOCSIZE ROUND_DOWN(10*1024*1024, MINIMUM_ALLOCSIZE) +#define SECTOR_SIZE 512 +#define SECTORS_PER_ALLOCATION_UNIT 1 +#define ALLOCATION_UNIT ( SECTOR_SIZE * SECTORS_PER_ALLOCATION_UNIT ) +#define INFO(format, ...) FspServiceLog(EVENTLOG_INFORMATION_TYPE , format, __VA_ARGS__) +#define WARN(format, ...) FspServiceLog(EVENTLOG_WARNING_TYPE , format, __VA_ARGS__) +#define FAIL(format, ...) FspServiceLog(EVENTLOG_ERROR_TYPE , format, __VA_ARGS__) +#define AIRFS_MAX_PATH 512 +#define FILEBLOCK_OVERHEAD 40 // size of ( P + E + L + R + FileOffset ) = 8 * 5 = 40 +#define ARG_TO_S(v) if (arge > ++argp) v = *argp; else goto usage +#define ARG_TO_4(v) if (arge > ++argp) v = (int32_t) wcstoll_default(*argp, v); else goto usage +#define ARG_TO_8(v) if (arge > ++argp) v = wcstoll_default(*argp, v); else goto usage + +enum StorageFileAccessType {ZERO=0,READ,WRITE}; +enum Neighbor {LT=-2,LE=-1,EQ=0,GE=1,GT=2}; + +struct NODE; +typedef NODE* NODE_; + +typedef int CompareFunction (void* key, NODE_); + +inline NTSTATUS GetLastErrorAsStatus() +{ + return FspNtStatusFromWin32(GetLastError()); +} + +inline UINT64 SystemTime() +{ + FILETIME FileTime; + GetSystemTimeAsFileTime(&FileTime); + return ((PLARGE_INTEGER)&FileTime)->QuadPart; +} + +static int64_t wcstoll_default(wchar_t *w, int64_t deflt) +{ + wchar_t *endp; + int64_t i = wcstoll(w, &endp, 0); + return L'\0' != w[0] && L'\0' == *endp ? i : deflt; +} + +////////////////////////////////////////////////////////////////////// +// +// Where Class: This class manages an offset within our memory-mapped +// volume to another location within our memory-mapped volume. Because it is +// a self-relative offset, this delta is constant regardless of where in +// memory the file system is mapped, so we can always reobtain its address. +// A delta of 0 is the special case for "null". +// + +template class Where +{ + int64_t delta; + + public: + + Where() = default; + ~Where() = default; + Where(T t) : delta( t ?( (char*)t -(char*)this ):0) {} + Where(Where& w) : delta( w.delta?( ((char*)&w+w.delta)-(char*)this ):0) {} + + operator bool () { return delta != 0; } + operator T () { return (T) ( delta?( (char*)this+delta ):0); } + T operator -> () { return (T) ( delta?( (char*)this+delta ):0); } + operator void* () { return (void*)( delta?( (char*)this+delta ):0); } + + bool operator == (Where& rhs) { return (char*)this+delta == (char*)&rhs+rhs.delta; } + bool operator != (Where& rhs) { return (char*)this+delta != (char*)&rhs+rhs.delta; } + bool operator == (T rhs) { return (char*)this+delta == (char*)rhs; } + bool operator != (T rhs) { return (char*)this+delta != (char*)rhs; } + + Where& operator = (Where& rhs) { delta = rhs.delta?( ((char*)&rhs+rhs.delta) - ((char*)this) ):0; return *this; } + Where& operator = (void* rhs) { delta = rhs ?( (char*)rhs - ((char*)this) ):0; return *this; } + + char* Address () { return (char*)this+delta; } +}; + +////////////////////////////////////////////////////////////////////// +// +// The header for an Airfs volume +// + +typedef struct +{ + char Signature[8]; // Airfs\0\0\0 + char MapFormatVersion[4]; // Major.Minor.Patch.Build + char filler[4]; + Where Root; + Where Available; + UINT64 VolumeSize; + UINT64 FreeSize; + WCHAR VolumeLabel[32]; + UINT16 VolumeLabelLength; + UINT16 filler1,filler2,filler3; + UINT32 CaseInsensitive; + UINT32 filler4; + int64_t VolumeLength; + FSP_FSCTL_VOLUME_PARAMS VolumeParams; + FSP_FILE_SYSTEM *FileSystem; + HANDLE MapFileHandle; + HANDLE MapHandle; +} AIRFS, *AIRFS_; + +////////////////////////////////////////////////////////////////////// +// +// Information per file or directory +// +struct NODE +{ + Where P,L,R,E; // Sorted sibling tree: Parent, Left, Right, and Equal + union + { + Where Name; + int64_t FileOffset; + }; + Where Parent; + Where Children; + FSP_FSCTL_FILE_INFO FileInfo; + uint64_t SecurityDescriptorSize; + Where SecurityDescriptor; + Where FileBlocks; + uint64_t ReparseDataSize; + Where ReparseData; + volatile LONG RefCount; + Where Streams; + BOOLEAN IsAStream; +}; + +////////////////////////////////////////////////////////////////////// + +class SpinLock +{ + LONG C; // Counter + HANDLE S; // Semaphore + + public: + + SpinLock() { C = 0; S = CreateSemaphore(NULL, 0, 1, NULL); } + ~SpinLock() { CloseHandle(S); } + + void Acquire() { if (_InterlockedIncrement(&C) > 1) WaitForSingleObject(S, INFINITE); } + void Release() { if (_InterlockedDecrement(&C) > 0) ReleaseSemaphore(S, 1, NULL); } +}; + +////////////////////////////////////////////////////////////////////// + +void Airprint (const char * format, ...); + +int SizeCmp (void* key, NODE_); +int ExactNameCmp (void* key, NODE_); +int CaselessNameCmp (void* key, NODE_); + +NODE_ Find (Where &root, void* key, CompareFunction); +NODE_ Near (Where &root, void* key, CompareFunction, Neighbor); +void Attach (Where &root, NODE_ attach, CompareFunction, void* key); +void Detach (Where &root, NODE_ detach); +NODE_ First (NODE_ start); +NODE_ Last (NODE_ start); +NODE_ Next (NODE_); +NODE_ Prev (NODE_); + +NTSTATUS StorageStartup (AIRFS_ &, WCHAR* MapName, WCHAR* StorageFileName, int64_t Length); +NTSTATUS StorageShutdown (AIRFS_); +void* StorageAllocate (AIRFS_, int64_t RequestedSize); +void* StorageReallocate (AIRFS_, void* Reallocate, int64_t RequestedSize); +void StorageFree (AIRFS_, void* Release); +NTSTATUS StorageSetFileCapacity (AIRFS_, NODE_, int64_t MinimumRequiredCapacity); +void StorageAccessFile (StorageFileAccessType, NODE_, int64_t Offset, int64_t NumBytes, char* Address); + +static_assert(AIRFS_MAX_PATH > MAX_PATH, "AIRFS_MAX_PATH must be greater than MAX_PATH."); +static_assert(sizeof NODE + sizeof int32_t == MINIMUM_ALLOCSIZE, "MINIMUM_ALLOCSIZE should be 196."); + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// diff --git a/3rd_party/winfsp-1.10/samples/airfs/persistence.cpp b/3rd_party/winfsp-1.10/samples/airfs/persistence.cpp new file mode 100644 index 00000000..39e83b36 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/airfs/persistence.cpp @@ -0,0 +1,594 @@ +/** + * @file persistence.cpp + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ +/* + * Airfs is based on Memfs with changes contributed by John Oberschelp. + * The contributed changes are under joint copyright by Bill Zissimopoulos + * and John Oberschelp per the Contributor Agreement found at the + * root of this project. + */ +/* + * Airfs uses a memory-mapped file per volume to achieve persistence. + * The primary advantage of this is that volume loads and saves are automatic. + * The two primary disadvantages, and our workarounds are: + * 1. We can't use standard containers or memory management, + * so the below Rubbertree and Storage functions are used instead. + * 2. Each process will map the volume to an arbitrary address, + * so Where offsets are used in place of pointers. + */ + +#include "common.h" + +SpinLock StorageLock, AirprintLock, SetLock; + +int SizeCmp ( void* key, NODE_ x) +{ + return *(int32_t*)key - ((int32_t*)x)[-1]; +} + +int BlockCmp ( void* key, NODE_ x) +{ + int64_t left = *(int64_t*)key; + int64_t right = x->FileOffset; + return left == right ? 0 : (left < right ? -1 : 1); +} + +int CaselessNameCmp ( void* key, NODE_ x) +{ + WCHAR* c1 = (WCHAR*) key; + WCHAR* c2 = x->Name; + return _wcsicmp(c1,c2); +} + +int ExactNameCmp ( void* key, NODE_ x) +{ + WCHAR* c1 = (WCHAR*) key; + WCHAR* c2 = x->Name; + return wcscmp(c1,c2); +} + +////////////////////////////////////////////////////////////////////// + +void Airprint (const char * format, ...) +{ + AirprintLock.Acquire(); + va_list args; + va_start(args, format); + char szBuffer[512]; + sprintf_s(szBuffer, 511, "Airfs %5.5f ----", SystemTime() / 10'000'000.0); + vsnprintf(szBuffer+25, 511-25, format, args); + OutputDebugStringA(szBuffer); + va_end(args); + AirprintLock.Release(); +} + +////////////////////////////////////////////////////////////////////// +// +// Rubbertree (because it is flexible!) +// Implements a sorted set of elements, using a binary tree. +// Has a function, Near, that finds nodes at or adjacent to a key. +// Attach, Find, and Near use splay to improve random access times. +// First, Last, Next, and Prev do not, to improve sequential access times. +// Replacing each Where with NODE_ would make this a pointer-based tree. +// In addition to Left, Right, and Parent references, each node has an Equal +// reference that may be used to keep "equivalent" nodes. This is used by +// our memory heap manager's tree of available memory blocks, sorted by size. + +//-------------------------------------------------------------------- + +inline void rotateL(Where &root, NODE_ x) +{ + NODE_ y = x->R, p = x->P; + if (x->R = y->L) y->L->P = x; + if (!(y->P = p)) + root = y; + else + { + if (x == p->L) p->L = y; + else p->R = y; + } + (y->L = x)->P = y; +} + +//-------------------------------------------------------------------- + +inline void rotateR(Where &root, NODE_ y) +{ + NODE_ x = y->L, p = y->P; + if (y->L = x->R) x->R->P = y; + if (!(x->P = p)) + root = x; + else + { + if (y == p->L) p->L = x; + else p->R = x; + } + (x->R = y)->P = x; +} + +//-------------------------------------------------------------------- + +static void splay(Where &root, NODE_ x) +{ + while (NODE_ p = x->P) + { + if (!p->P) + { + if (p->L == x) rotateR(root, p); + else rotateL(root, p); + } + else + { + if (p == p->P->R) + { + if (p->R == x) rotateL(root, p->P); + else rotateR(root, p); + rotateL(root, x->P); + } + else + { + if (p->L == x) rotateR(root, p->P); + else rotateL(root, p); + rotateR(root, x->P); + } + } + } +} + +//-------------------------------------------------------------------- + +inline int seek(Where &root, NODE_ &x, void* key, CompareFunction CMP) +{ + x = root; + for (;;) + { + int diff = CMP(key, x); + if (diff < 0) + { + if (!x->L) return -1; + x = x->L; + } + else if (diff > 0) + { + if (!x->R) return 1; + x = x->R; + } + else return 0; + } +} + +//-------------------------------------------------------------------- + +inline NODE_ next(NODE_ x) +{ + if (x->R) { x = x->R; while (x->L) x = x->L; return x; } + NODE_ p = x->P; + while (p && x == p->R) { x = p; p = p->P; } + return p; +} + +//-------------------------------------------------------------------- + +inline NODE_ prev(NODE_ x) +{ + if (x->L) { x = x->L; while (x->R) x = x->R; return x; } + NODE_ p = x->P; + while (p && x == p->L) { x = p; p = p->P; } + return p; +} + +//-------------------------------------------------------------------- + +NODE_ First(NODE_ x) +{ + SetLock.Acquire(); + if (x) while (x->L) x = x->L; + SetLock.Release(); + return x; +} + +//-------------------------------------------------------------------- + +NODE_ Last(NODE_ x) +{ + SetLock.Acquire(); + if (x) while (x->R) x = x->R; + SetLock.Release(); + return x; +} + +//-------------------------------------------------------------------- + +NODE_ Next(NODE_ x) +{ + SetLock.Acquire(); + x = next(x); + SetLock.Release(); + return x; +} + +//-------------------------------------------------------------------- + +NODE_ Prev(NODE_ x) +{ + SetLock.Acquire(); + x = prev(x); + SetLock.Release(); + return x; +} + +//-------------------------------------------------------------------- + +NODE_ Near(Where &root, void* key, CompareFunction CMP, Neighbor want) +{ + // Return a node relative to (just <, <=, ==, >=, or >) a key. + if (!root) return 0; + SetLock.Acquire(); + NODE_ x; + int dir = seek(root, x, key, CMP); + if ((dir == 0 && want == GT) || (dir > 0 && want >= GE)) x = next(x); + else + if ((dir == 0 && want == LT) || (dir < 0 && want <= LE)) x = prev(x); + else + if (dir != 0 && want == EQ) x = 0; + if (x) splay(root, x); + SetLock.Release(); + return x; +} + +//-------------------------------------------------------------------- + +NODE_ Find(Where &root, void* key, CompareFunction CMP) +{ + if (!root) return 0; + SetLock.Acquire(); + NODE_ x; + int direction = seek(root, x, key, CMP); + splay(root, x); + SetLock.Release(); + return direction?0:x; +} + +//-------------------------------------------------------------------- + +void Attach(Where &root, NODE_ x, CompareFunction CMP, void* key) +{ + SetLock.Acquire(); + if (!root) + { + root = x; + x->P = x->L = x->R = x->E = 0; + SetLock.Release(); + return; + } + NODE_ f; + int diff = seek(root, f, key, CMP); + if (!diff) + { + if (x->L = f->L) x->L->P = x; + if (x->R = f->R) x->R->P = x; + NODE_ p = f->P; + if (x->P = p) { if (p->L == f) p->L = x; else p->R = x; } + else root = x; + (x->E = f)->P = x; + f->L = f->R = 0; + splay(root, x); + SetLock.Release(); + return; + } + if (diff < 0) f->L = x; else f->R = x; + x->P = f; + x->L = x->R = x->E = 0; + splay(root, x); + SetLock.Release(); +} + +//-------------------------------------------------------------------- + +void Detach(Where &root, NODE_ x) +{ + SetLock.Acquire(); + NODE_ e = x->E, p = x->P; + if (p && p->E == x) { if (p->E = e) e->P = p; } + else if (e) + { + if (e->L = x->L) e->L->P = e; + if (e->R = x->R) e->R->P = e; + if (e->P = p) { if (p->L == x) p->L = e; else p->R = e; } + else root = e; + } + else if (!x->L) + { + if (p) { if ( p->L == x) p->L = x->R; else p->R = x->R; } + else root = x->R; + if (x->R) x->R->P = p; + } + else if (!x->R) + { + if (p) { if ( p->L == x) p->L = x->L; else p->R = x->L; } + else root = x->L; + if (x->L) x->L->P = p; + } + else + { + e = x->L; + if (e->R) + { + do { e = e->R; } while (e->R); + if (e->P->R = e->L) e->L->P = e->P; + (e->L = x->L)->P = e; + } + (e->R = x->R)->P = e; + if (e->P = x->P) { if (e->P->L == x) e->P->L = e; else e->P->R = e; } + else root = e; + } + SetLock.Release(); +} + +////////////////////////////////////////////////////////////////////// +// +// Storage Functions for our memory-mapped file-based persistent volumes + +//-------------------------------------------------------------------- + +void* StorageAllocate(AIRFS_ Airfs, int64_t RequestedSize) +{ + if (!RequestedSize) return 0; + if (RequestedSize + sizeof int32_t > MAXIMUM_ALLOCSIZE) return 0; + + StorageLock.Acquire(); + int32_t RoundedSize = (int32_t) ROUND_UP(RequestedSize, MINIMUM_ALLOCSIZE - sizeof int32_t); + int32_t SplitableSize = RoundedSize + MINIMUM_ALLOCSIZE; + + // See if we have a freed node of the size we requested. + NODE_ NewItem = Near(Airfs->Available, &RoundedSize, SizeCmp, GE); + if (NewItem) + { + int32_t FoundSize = ((int32_t*)NewItem)[-1]; + if (FoundSize < SplitableSize) + { + Detach(Airfs->Available, NewItem); + Airfs->FreeSize -= FoundSize; + StorageLock.Release(); + return NewItem; + } + } + + // If not, see if we can downsize a larger freed element. + NewItem = Near(Airfs->Available, &SplitableSize, SizeCmp, GE); + if (NewItem) + { + int32_t FoundSize = ((int32_t*)NewItem)[-1]; + Detach(Airfs->Available, NewItem); + Airfs->FreeSize -= FoundSize; + char* Addr = (char*)NewItem + RoundedSize + sizeof int32_t; + NODE_ Remainder = (NODE_) Addr; + int32_t RemainderSize = FoundSize - (RoundedSize + sizeof int32_t); + ((int32_t*)Remainder)[-1] = RemainderSize; + Attach(Airfs->Available, Remainder, SizeCmp, &RemainderSize); + Airfs->FreeSize += RemainderSize; + ((int32_t*)NewItem)[-1] = RoundedSize; + StorageLock.Release(); + return NewItem; + } + + // If not, give up. + StorageLock.Release(); + return 0; +} + +//-------------------------------------------------------------------- + +void* StorageReallocate(AIRFS_ Airfs, void* OldAlloc, int64_t RequestedSize) +{ + if (!OldAlloc) + { + return StorageAllocate(Airfs, RequestedSize); + } + + if (!RequestedSize) + { + StorageFree(Airfs, OldAlloc); + return 0; + } + + int32_t OldSize = ((int32_t*)OldAlloc)[-1]; + void* NewAlloc = StorageAllocate(Airfs, RequestedSize); + if (!NewAlloc) return 0; + memcpy(NewAlloc, OldAlloc, min(RequestedSize, OldSize)); + StorageFree(Airfs, OldAlloc); + return NewAlloc; +} + +//-------------------------------------------------------------------- + +void StorageFree(AIRFS_ Airfs, void* r) +{ + if (!r) return; + StorageLock.Acquire(); + NODE_ release = (NODE_) r; + int32_t Size = ((int32_t*)r)[-1]; + Attach(Airfs->Available, release, SizeCmp, &Size); + Airfs->FreeSize += Size; + StorageLock.Release(); +} + +//-------------------------------------------------------------------- + +void StorageAccessFile(StorageFileAccessType Type, NODE_ Node, int64_t AccessOffset, int64_t NumBytes, char* MemoryAddress) +{ + StorageLock.Acquire(); + + NODE_ Block = Near(Node->FileBlocks, &AccessOffset, BlockCmp, LE); + for (;;) + { + int32_t BlockSize = ((int32_t*)Block)[-1]; + int64_t BlockOffset = Block->FileOffset; + int64_t BlockIndex = AccessOffset - BlockOffset + FILEBLOCK_OVERHEAD; + int64_t BlockNum = min(BlockSize-BlockIndex, NumBytes); + + switch (Type) + { + case ZERO : { memset((char*)Block + BlockIndex, 0, BlockNum); break; } + case READ : { memcpy(MemoryAddress, (char*)Block + BlockIndex, BlockNum); break; } + case WRITE : { memcpy((char*)Block + BlockIndex, MemoryAddress, BlockNum); break; } + } + NumBytes -= BlockNum; + if (!NumBytes) break; + MemoryAddress += BlockNum; + AccessOffset += BlockNum; + Block = Next(Block); + } + + StorageLock.Release(); +} + +//-------------------------------------------------------------------- + +NTSTATUS StorageSetFileCapacity(AIRFS_ Airfs, NODE_ Node, int64_t minimumRequiredCapacity) +{ + StorageLock.Acquire(); + + int64_t TargetCapacity = ROUND_UP(minimumRequiredCapacity, ALLOCATION_UNIT); + NODE_ Block = Last(Node->FileBlocks); + int32_t BlockSize = Block ? ((int32_t*)Block)[-1] : 0; + int64_t CurrentCapacity = Block ? Block->FileOffset + BlockSize - FILEBLOCK_OVERHEAD: 0; + int64_t Add = TargetCapacity - CurrentCapacity; + + while (Add > 0) + { + // Add a block if we can, preferably as large or larger than we need. + Add += FILEBLOCK_OVERHEAD; + Block = Near(Airfs->Available, &Add, SizeCmp, GE); + if (!Block) Block = Near(Airfs->Available, &Add, SizeCmp, LT); + Add -= FILEBLOCK_OVERHEAD; + if (Block) + { + Detach(Airfs->Available, Block); + BlockSize = ((int32_t*)Block)[-1]; + Airfs->FreeSize -= BlockSize; + Block->FileOffset = CurrentCapacity; + Attach(Node->FileBlocks, Block, BlockCmp, &CurrentCapacity); + CurrentCapacity += BlockSize - FILEBLOCK_OVERHEAD; + Add -= BlockSize - FILEBLOCK_OVERHEAD; + continue; + } + + StorageLock.Release(); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Throw away any trailing blocks that are no longer needed. + while (Add < 0) + { + Block = Last(Node->FileBlocks); + BlockSize = ((int32_t*)Block)[-1]; + if (BlockSize - FILEBLOCK_OVERHEAD > -Add) break; + Add += BlockSize - FILEBLOCK_OVERHEAD; + Detach(Node->FileBlocks, Block); + Attach(Airfs->Available, Block, SizeCmp, &BlockSize); + Airfs->FreeSize += BlockSize; + } + + // Possibly downsize the last block. + if (Add < 0) + { + Block = Last(Node->FileBlocks); + int32_t OldBlockSize = ((int32_t*)Block)[-1]; + int32_t NewBlockSize = OldBlockSize - (int32_t) ROUND_DOWN(-Add, MINIMUM_ALLOCSIZE); + if (NewBlockSize < MINIMUM_ALLOCSIZE) NewBlockSize = MINIMUM_ALLOCSIZE; + int32_t RemainderBlockSize = OldBlockSize - NewBlockSize - sizeof int32_t; + if (RemainderBlockSize >= MINIMUM_ALLOCSIZE) // i.e. if not too near the end + { + char* Addr = (char*)Block + NewBlockSize + sizeof int32_t; + NODE_ Remainder = (NODE_) Addr; + ((int32_t*)Remainder)[-1] = RemainderBlockSize; + Attach(Airfs->Available, Remainder, SizeCmp, &RemainderBlockSize); + Airfs->FreeSize += RemainderBlockSize; + ((int32_t*)Block)[-1] = NewBlockSize; + } + } + + StorageLock.Release(); + return 0; +} + +//-------------------------------------------------------------------- + +NTSTATUS StorageStartup(AIRFS_ &Airfs, WCHAR* MapName, WCHAR* StorageFileName, int64_t VolumeLength) +{ + HANDLE MapFileHandle = INVALID_HANDLE_VALUE; + Airfs = 0; + + // Open. + if (*StorageFileName) + { + MapFileHandle = CreateFileW(StorageFileName, GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, 0, NULL, OPEN_ALWAYS, NULL, NULL); + if (MapFileHandle == INVALID_HANDLE_VALUE) return GetLastErrorAsStatus(); + } + + // Map. + HANDLE MapHandle = CreateFileMappingW(MapFileHandle, NULL, PAGE_EXECUTE_READWRITE, VolumeLength>>32, VolumeLength & 0xFFFFFFFF, MapName); + if (!MapHandle) return GetLastErrorAsStatus(); + + // Point. + char* MappedAddress = (char*) MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, VolumeLength); + if (!MappedAddress) return GetLastErrorAsStatus(); + + // Keep. + Airfs = (AIRFS_) MappedAddress; + Airfs->MapFileHandle = MapFileHandle; + Airfs->MapHandle = MapHandle; + Airfs->VolumeLength = VolumeLength; + + return 0; +} + +//-------------------------------------------------------------------- + +NTSTATUS StorageShutdown(AIRFS_ Airfs) +{ + BOOL Ok; + NTSTATUS Result = 0; + HANDLE M = Airfs->MapHandle; + HANDLE F = Airfs->MapFileHandle; + + Ok = FlushViewOfFile(Airfs, 0); if (!Ok && !Result) Result = GetLastErrorAsStatus(); + if (F != INVALID_HANDLE_VALUE) + { + Ok = FlushFileBuffers(F); if (!Ok && !Result) Result = GetLastErrorAsStatus(); + // TODO: Set and write a flag something like Airfs->UpdatesCompleted here? + } + Ok = UnmapViewOfFile(Airfs); if (!Ok && !Result) Result = GetLastErrorAsStatus(); + + if (M) + { + Ok = CloseHandle(M); if (!Ok && !Result) Result = GetLastErrorAsStatus(); + } + if (F != INVALID_HANDLE_VALUE) + { + Ok = CloseHandle(F); if (!Ok && !Result) Result = GetLastErrorAsStatus(); + } + + return Result; +} + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// diff --git a/3rd_party/winfsp-1.10/samples/memfs-dotnet/Program.cs b/3rd_party/winfsp-1.10/samples/memfs-dotnet/Program.cs new file mode 100644 index 00000000..fd61e385 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-dotnet/Program.cs @@ -0,0 +1,1526 @@ +/** + * @file Program.cs + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#define MEMFS_SLOWIO + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.AccessControl; +using System.Threading; +#if MEMFS_SLOWIO +using System.Threading.Tasks; +#endif + +using Fsp; +using VolumeInfo = Fsp.Interop.VolumeInfo; +using FileInfo = Fsp.Interop.FileInfo; + +namespace memfs +{ + class Path + { + public static String GetDirectoryName(String Path) + { + int Index = Path.LastIndexOf('\\'); + if (0 > Index) + return Path; + else if (0 == Index) + return "\\"; + else + return Path.Substring(0, Index); + } + + public static String GetFileName(String Path) + { + int Index = Path.LastIndexOf('\\'); + if (0 > Index) + return Path; + else + return Path.Substring(Index + 1); + } + } + + struct EaValueData + { + public Byte[] EaValue; + public Boolean NeedEa; + } + + class FileNode + { + public FileNode(String FileName) + { + this.FileName = FileName; + FileInfo.CreationTime = + FileInfo.LastAccessTime = + FileInfo.LastWriteTime = + FileInfo.ChangeTime = (UInt64)DateTime.Now.ToFileTimeUtc(); + FileInfo.IndexNumber = IndexNumber++; + } + public FileInfo GetFileInfo() + { + if (null == MainFileNode) + return this.FileInfo; + else + { + FileInfo FileInfo = MainFileNode.FileInfo; + FileInfo.FileAttributes &= ~(UInt32)FileAttributes.Directory; + /* named streams cannot be directories */ + FileInfo.AllocationSize = this.FileInfo.AllocationSize; + FileInfo.FileSize = this.FileInfo.FileSize; + return FileInfo; + } + } + public SortedDictionary GetEaMap(Boolean Force) + { + FileNode FileNode = null == MainFileNode ? this : MainFileNode; + if (null == EaMap && Force) + EaMap = new SortedDictionary(StringComparer.OrdinalIgnoreCase); + return EaMap; + } + + private static UInt64 IndexNumber = 1; + public String FileName; + public FileInfo FileInfo; + public Byte[] FileSecurity; + public Byte[] FileData; + public Byte[] ReparseData; + private SortedDictionary EaMap; + public FileNode MainFileNode; + public int OpenCount; + } + + class FileNodeMap + { + public FileNodeMap(Boolean CaseInsensitive) + { + this.CaseInsensitive = CaseInsensitive; + StringComparer Comparer = CaseInsensitive ? + StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal; + Set = new SortedSet(Comparer); + Map = new Dictionary(Comparer); + } + public UInt32 Count() + { + return (UInt32)Map.Count; + } + public FileNode Get(String FileName) + { + FileNode FileNode; + return Map.TryGetValue(FileName, out FileNode) ? FileNode : null; + } + public FileNode GetMain(String FileName) + { + int Index = FileName.IndexOf(':'); + if (0 > Index) + return null; + FileNode FileNode; + return Map.TryGetValue(FileName.Substring(0, Index), out FileNode) ? FileNode : null; + } + public FileNode GetParent(String FileName, ref Int32 Result) + { + FileNode FileNode; + Map.TryGetValue(Path.GetDirectoryName(FileName), out FileNode); + if (null == FileNode) + { + Result = FileSystemBase.STATUS_OBJECT_PATH_NOT_FOUND; + return null; + } + if (0 == (FileNode.FileInfo.FileAttributes & (UInt32)FileAttributes.Directory)) + { + Result = FileSystemBase.STATUS_NOT_A_DIRECTORY; + return null; + } + return FileNode; + } + public void TouchParent(FileNode FileNode) + { + if ("\\" == FileNode.FileName) + return; + Int32 Result = FileSystemBase.STATUS_SUCCESS; + FileNode Parent = GetParent(FileNode.FileName, ref Result); + if (null == Parent) + return; + Parent.FileInfo.LastAccessTime = + Parent.FileInfo.LastWriteTime = + Parent.FileInfo.ChangeTime = (UInt64)DateTime.Now.ToFileTimeUtc(); + } + public void Insert(FileNode FileNode) + { + Set.Add(FileNode.FileName); + Map.Add(FileNode.FileName, FileNode); + TouchParent(FileNode); + } + public void Remove(FileNode FileNode) + { + if (Set.Remove(FileNode.FileName)) + { + Map.Remove(FileNode.FileName); + TouchParent(FileNode); + } + } + public Boolean HasChild(FileNode FileNode) + { + foreach (String Name in GetChildrenFileNames(FileNode, null)) + return true; + return false; + } + public IEnumerable GetChildrenFileNames(FileNode FileNode, String Marker) + { + String MinName = "\\"; + String MaxName = "]"; + if ("\\" != FileNode.FileName) + { + MinName = FileNode.FileName + "\\"; + MaxName = FileNode.FileName + "]"; + } + if (null != Marker) + MinName += Marker; + foreach (String Name in Set.GetViewBetween(MinName, MaxName)) + if (Name != MinName && + Name.Length > MaxName.Length && -1 == Name.IndexOfAny(Delimiters, MaxName.Length)) + yield return Name; + } + public IEnumerable GetStreamFileNames(FileNode FileNode) + { + String MinName = FileNode.FileName + ":"; + String MaxName = FileNode.FileName + ";"; + foreach (String Name in Set.GetViewBetween(MinName, MaxName)) + if (Name.Length > MinName.Length) + yield return Name; + } + public IEnumerable GetDescendantFileNames(FileNode FileNode) + { + yield return FileNode.FileName; + String MinName = FileNode.FileName + ":"; + String MaxName = FileNode.FileName + ";"; + foreach (String Name in Set.GetViewBetween(MinName, MaxName)) + if (Name.Length > MinName.Length) + yield return Name; + MinName = "\\"; + MaxName = "]"; + if ("\\" != FileNode.FileName) + { + MinName = FileNode.FileName + "\\"; + MaxName = FileNode.FileName + "]"; + } + foreach (String Name in Set.GetViewBetween(MinName, MaxName)) + if (Name.Length > MinName.Length) + yield return Name; + } + + private static readonly Char[] Delimiters = new Char[] { '\\', ':' }; + public Boolean CaseInsensitive; + private SortedSet Set; + private Dictionary Map; + } + + class Memfs : FileSystemBase + { + private FileSystemHost Host; + public const UInt16 MEMFS_SECTOR_SIZE = 512; + public const UInt16 MEMFS_SECTORS_PER_ALLOCATION_UNIT = 1; + + public Memfs( + Boolean CaseInsensitive, UInt32 MaxFileNodes, UInt32 MaxFileSize, String RootSddl, + UInt64 SlowioMaxDelay, UInt64 SlowioPercentDelay, UInt64 SlowioRarefyDelay) + { + this.FileNodeMap = new FileNodeMap(CaseInsensitive); + this.MaxFileNodes = MaxFileNodes; + this.MaxFileSize = MaxFileSize; + this.SlowioMaxDelay = SlowioMaxDelay; + this.SlowioPercentDelay = SlowioPercentDelay; + this.SlowioRarefyDelay = SlowioRarefyDelay; + + /* + * Create root directory. + */ + + FileNode RootNode = new FileNode("\\"); + RootNode.FileInfo.FileAttributes = (UInt32)FileAttributes.Directory; + if (null == RootSddl) + RootSddl = "O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)"; + RawSecurityDescriptor RootSecurityDescriptor = new RawSecurityDescriptor(RootSddl); + RootNode.FileSecurity = new Byte[RootSecurityDescriptor.BinaryLength]; + RootSecurityDescriptor.GetBinaryForm(RootNode.FileSecurity, 0); + + FileNodeMap.Insert(RootNode); + } + + public override Int32 Init(Object Host0) + { + Host = (FileSystemHost)Host0; + Host.SectorSize = Memfs.MEMFS_SECTOR_SIZE; + Host.SectorsPerAllocationUnit = Memfs.MEMFS_SECTORS_PER_ALLOCATION_UNIT; + Host.VolumeCreationTime = (UInt64)DateTime.Now.ToFileTimeUtc(); + Host.VolumeSerialNumber = (UInt32)(Host.VolumeCreationTime / (10000 * 1000)); + Host.CaseSensitiveSearch = !FileNodeMap.CaseInsensitive; + Host.CasePreservedNames = true; + Host.UnicodeOnDisk = true; + Host.PersistentAcls = true; + Host.ReparsePoints = true; + Host.ReparsePointsAccessCheck = false; + Host.NamedStreams = true; + Host.PostCleanupWhenModifiedOnly = true; + Host.PassQueryDirectoryFileName = true; + Host.ExtendedAttributes = true; + Host.WslFeatures = true; + Host.RejectIrpPriorToTransact0 = true; + Host.SupportsPosixUnlinkRename = true; + return STATUS_SUCCESS; + } + +#if MEMFS_SLOWIO + public override int Mounted(object Host) + { + SlowioTasksRunning = 0; + return STATUS_SUCCESS; + } + + public override void Unmounted(object Host) + { + while (SlowioTasksRunning != 0) + Thread.Sleep(1000); + } +#endif + + public override Int32 GetVolumeInfo( + out VolumeInfo VolumeInfo) + { + VolumeInfo = default(VolumeInfo); + VolumeInfo.TotalSize = MaxFileNodes * (UInt64)MaxFileSize; + VolumeInfo.FreeSize = (MaxFileNodes - FileNodeMap.Count()) * (UInt64)MaxFileSize; + VolumeInfo.SetVolumeLabel(VolumeLabel); + return STATUS_SUCCESS; + } + + public override Int32 SetVolumeLabel( + String VolumeLabel, + out VolumeInfo VolumeInfo) + { + this.VolumeLabel = VolumeLabel; + return GetVolumeInfo(out VolumeInfo); + } + + public override Int32 GetSecurityByName( + String FileName, + out UInt32 FileAttributes/* or ReparsePointIndex */, + ref Byte[] SecurityDescriptor) + { + FileNode FileNode = FileNodeMap.Get(FileName); + if (null == FileNode) + { + Int32 Result = STATUS_OBJECT_NAME_NOT_FOUND; + if (FindReparsePoint(FileName, out FileAttributes)) + Result = STATUS_REPARSE; + else + FileNodeMap.GetParent(FileName, ref Result); + return Result; + } + + UInt32 FileAttributesMask = ~(UInt32)0; + if (null != FileNode.MainFileNode) + { + FileAttributesMask = ~(UInt32)System.IO.FileAttributes.Directory; + FileNode = FileNode.MainFileNode; + } + FileAttributes = FileNode.FileInfo.FileAttributes & FileAttributesMask; + if (null != SecurityDescriptor) + SecurityDescriptor = FileNode.FileSecurity; + + return STATUS_SUCCESS; + } + + public override Int32 CreateEx( + String FileName, + UInt32 CreateOptions, + UInt32 GrantedAccess, + UInt32 FileAttributes, + Byte[] SecurityDescriptor, + UInt64 AllocationSize, + IntPtr ExtraBuffer, + UInt32 ExtraLength, + Boolean ExtraBufferIsReparsePoint, + out Object FileNode0, + out Object FileDesc, + out FileInfo FileInfo, + out String NormalizedName) + { + FileNode0 = default(Object); + FileDesc = default(Object); + FileInfo = default(FileInfo); + NormalizedName = default(String); + + FileNode FileNode; + FileNode ParentNode; + Int32 Result = STATUS_SUCCESS; + + FileNode = FileNodeMap.Get(FileName); + if (null != FileNode) + return STATUS_OBJECT_NAME_COLLISION; + ParentNode = FileNodeMap.GetParent(FileName, ref Result); + if (null == ParentNode) + return Result; + + if (0 != (CreateOptions & FILE_DIRECTORY_FILE)) + AllocationSize = 0; + if (FileNodeMap.Count() >= MaxFileNodes) + return STATUS_CANNOT_MAKE; + if (AllocationSize > MaxFileSize) + return STATUS_DISK_FULL; + + if ("\\" != ParentNode.FileName) + /* normalize name */ + FileName = ParentNode.FileName + "\\" + Path.GetFileName(FileName); + FileNode = new FileNode(FileName); + FileNode.MainFileNode = FileNodeMap.GetMain(FileName); + FileNode.FileInfo.FileAttributes = 0 != (FileAttributes & (UInt32)System.IO.FileAttributes.Directory) ? + FileAttributes : FileAttributes | (UInt32)System.IO.FileAttributes.Archive; + FileNode.FileSecurity = SecurityDescriptor; + if (IntPtr.Zero != ExtraBuffer) + { + if (!ExtraBufferIsReparsePoint) + { + Result = SetEaEntries(FileNode, null, ExtraBuffer, ExtraLength); + if (0 > Result) + return Result; + } + else + { + Byte[] ReparseData = MakeReparsePoint(ExtraBuffer, ExtraLength); + FileNode.FileInfo.FileAttributes |= (UInt32)System.IO.FileAttributes.ReparsePoint; + FileNode.FileInfo.ReparseTag = GetReparseTag(ReparseData); + FileNode.ReparseData = ReparseData; + } + } + if (0 != AllocationSize) + { + Result = SetFileSizeInternal(FileNode, AllocationSize, true); + if (0 > Result) + return Result; + } + FileNodeMap.Insert(FileNode); + + Interlocked.Increment(ref FileNode.OpenCount); + FileNode0 = FileNode; + FileInfo = FileNode.GetFileInfo(); + NormalizedName = FileNode.FileName; + + return STATUS_SUCCESS; + } + + public override Int32 Open( + String FileName, + UInt32 CreateOptions, + UInt32 GrantedAccess, + out Object FileNode0, + out Object FileDesc, + out FileInfo FileInfo, + out String NormalizedName) + { + FileNode0 = default(Object); + FileDesc = default(Object); + FileInfo = default(FileInfo); + NormalizedName = default(String); + + FileNode FileNode; + Int32 Result; + + FileNode = FileNodeMap.Get(FileName); + if (null == FileNode) + { + Result = STATUS_OBJECT_NAME_NOT_FOUND; + FileNodeMap.GetParent(FileName, ref Result); + return Result; + } + + if (0 != (CreateOptions & FILE_NO_EA_KNOWLEDGE) && + null == FileNode.MainFileNode) + { + SortedDictionary EaMap = FileNode.GetEaMap(false); + if (null != EaMap) + { + foreach (KeyValuePair Pair in EaMap) + if (Pair.Value.NeedEa) + return STATUS_ACCESS_DENIED; + } + } + + Interlocked.Increment(ref FileNode.OpenCount); + FileNode0 = FileNode; + FileInfo = FileNode.GetFileInfo(); + NormalizedName = FileNode.FileName; + + return STATUS_SUCCESS; + } + + public override Int32 OverwriteEx( + Object FileNode0, + Object FileDesc, + UInt32 FileAttributes, + Boolean ReplaceFileAttributes, + UInt64 AllocationSize, + IntPtr Ea, + UInt32 EaLength, + out FileInfo FileInfo) + { + FileInfo = default(FileInfo); + + FileNode FileNode = (FileNode)FileNode0; + Int32 Result; + + List StreamFileNames = new List(FileNodeMap.GetStreamFileNames(FileNode)); + foreach (String StreamFileName in StreamFileNames) + { + FileNode StreamNode = FileNodeMap.Get(StreamFileName); + if (null == StreamNode) + continue; /* should not happen */ + if (0 == StreamNode.OpenCount) + FileNodeMap.Remove(StreamNode); + } + + SortedDictionary EaMap = FileNode.GetEaMap(false); + if (null != EaMap) + { + EaMap.Clear(); + FileNode.FileInfo.EaSize = 0; + } + if (IntPtr.Zero != Ea) + { + Result = SetEaEntries(FileNode, null, Ea, EaLength); + if (0 > Result) + return Result; + } + + Result = SetFileSizeInternal(FileNode, AllocationSize, true); + if (0 > Result) + return Result; + if (ReplaceFileAttributes) + FileNode.FileInfo.FileAttributes = FileAttributes | (UInt32)System.IO.FileAttributes.Archive; + else + FileNode.FileInfo.FileAttributes |= FileAttributes | (UInt32)System.IO.FileAttributes.Archive; + FileNode.FileInfo.FileSize = 0; + FileNode.FileInfo.LastAccessTime = + FileNode.FileInfo.LastWriteTime = + FileNode.FileInfo.ChangeTime = (UInt64)DateTime.Now.ToFileTimeUtc(); + + FileInfo = FileNode.GetFileInfo(); + + return STATUS_SUCCESS; + } + + public override void Cleanup( + Object FileNode0, + Object FileDesc, + String FileName, + UInt32 Flags) + { + FileNode FileNode = (FileNode)FileNode0; + FileNode MainFileNode = null != FileNode.MainFileNode ? + FileNode.MainFileNode : FileNode; + + if (0 != (Flags & CleanupSetArchiveBit)) + { + if (0 == (MainFileNode.FileInfo.FileAttributes & (UInt32)FileAttributes.Directory)) + MainFileNode.FileInfo.FileAttributes |= (UInt32)FileAttributes.Archive; + } + + if (0 != (Flags & (CleanupSetLastAccessTime | CleanupSetLastWriteTime | CleanupSetChangeTime))) + { + UInt64 SystemTime = (UInt64)DateTime.Now.ToFileTimeUtc(); + + if (0 != (Flags & CleanupSetLastAccessTime)) + MainFileNode.FileInfo.LastAccessTime = SystemTime; + if (0 != (Flags & CleanupSetLastWriteTime)) + MainFileNode.FileInfo.LastWriteTime = SystemTime; + if (0 != (Flags & CleanupSetChangeTime)) + MainFileNode.FileInfo.ChangeTime = SystemTime; + } + + if (0 != (Flags & CleanupSetAllocationSize)) + { + UInt64 AllocationUnit = MEMFS_SECTOR_SIZE * MEMFS_SECTORS_PER_ALLOCATION_UNIT; + UInt64 AllocationSize = (FileNode.FileInfo.FileSize + AllocationUnit - 1) / + AllocationUnit * AllocationUnit; + SetFileSizeInternal(FileNode, AllocationSize, true); + } + + if (0 != (Flags & CleanupDelete) && !FileNodeMap.HasChild(FileNode)) + { + List StreamFileNames = new List(FileNodeMap.GetStreamFileNames(FileNode)); + foreach (String StreamFileName in StreamFileNames) + { + FileNode StreamNode = FileNodeMap.Get(StreamFileName); + if (null == StreamNode) + continue; /* should not happen */ + FileNodeMap.Remove(StreamNode); + } + FileNodeMap.Remove(FileNode); + } + } + + public override void Close( + Object FileNode0, + Object FileDesc) + { + FileNode FileNode = (FileNode)FileNode0; + Interlocked.Decrement(ref FileNode.OpenCount); + } + +#if MEMFS_SLOWIO + private UInt64 Hash(UInt64 X) + { + X = (X ^ (X >> 30)) * 0xbf58476d1ce4e5b9ul; + X = (X ^ (X >> 27)) * 0x94d049bb133111ebul; + X = X ^ (X >> 31); + return X; + } + + private static int Spin = 0; + + private UInt64 PseudoRandom(UInt64 To) + { + /* John Oberschelp's PRNG */ + Interlocked.Increment(ref Spin); + return Hash((UInt64)Spin) % To; + } + + private bool SlowioReturnPending() + { + if (0 == SlowioMaxDelay) + { + return false; + } + return PseudoRandom(100) < SlowioPercentDelay; + } + + private void SlowioSnooze() + { + double Millis = PseudoRandom(SlowioMaxDelay + 1) >> (int) PseudoRandom(SlowioRarefyDelay + 1); + Thread.Sleep(TimeSpan.FromMilliseconds(Millis)); + } + + private void SlowioReadTask( + Object FileNode0, + IntPtr Buffer, + UInt64 Offset, + UInt64 EndOffset, + UInt64 RequestHint) + { + SlowioSnooze(); + + UInt32 BytesTransferred = (UInt32)(EndOffset - Offset); + FileNode FileNode = (FileNode)FileNode0; + Marshal.Copy(FileNode.FileData, (int)Offset, Buffer, (int)BytesTransferred); + + Host.SendReadResponse(RequestHint, STATUS_SUCCESS, BytesTransferred); + Interlocked.Decrement(ref SlowioTasksRunning); + } + + private void SlowioWriteTask( + Object FileNode0, + IntPtr Buffer, + UInt64 Offset, + UInt64 EndOffset, + UInt64 RequestHint) + { + SlowioSnooze(); + + UInt32 BytesTransferred = (UInt32)(EndOffset - Offset); + FileNode FileNode = (FileNode)FileNode0; + FileInfo FileInfo = FileNode.GetFileInfo(); + Marshal.Copy(Buffer, FileNode.FileData, (int)Offset, (int)BytesTransferred); + + Host.SendWriteResponse(RequestHint, STATUS_SUCCESS, BytesTransferred, ref FileInfo); + Interlocked.Decrement(ref SlowioTasksRunning); + } + + private void SlowioReadDirectoryTask( + Object FileNode0, + Object FileDesc, + String Pattern, + String Marker, + IntPtr Buffer, + UInt32 Length, + UInt64 RequestHint) + { + SlowioSnooze(); + + UInt32 BytesTransferred; + var Status = SeekableReadDirectory(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, out BytesTransferred); + + Host.SendReadDirectoryResponse(RequestHint, Status, BytesTransferred); + Interlocked.Decrement(ref SlowioTasksRunning); + } +#endif + + public override Int32 Read( + Object FileNode0, + Object FileDesc, + IntPtr Buffer, + UInt64 Offset, + UInt32 Length, + out UInt32 BytesTransferred) + { + FileNode FileNode = (FileNode)FileNode0; + UInt64 EndOffset; + + if (Offset >= FileNode.FileInfo.FileSize) + { + BytesTransferred = default(UInt32); + return STATUS_END_OF_FILE; + } + + EndOffset = Offset + Length; + if (EndOffset > FileNode.FileInfo.FileSize) + EndOffset = FileNode.FileInfo.FileSize; + +#if MEMFS_SLOWIO + if (SlowioReturnPending()) + { + var Hint = Host.GetOperationRequestHint(); + try + { + Interlocked.Increment(ref SlowioTasksRunning); + Task.Run(() => SlowioReadTask(FileNode0, Buffer, Offset, EndOffset, Hint)).ConfigureAwait(false); + + BytesTransferred = 0; + return STATUS_PENDING; + } + catch (Exception) + { + Interlocked.Decrement(ref SlowioTasksRunning); + } + } +#endif + + BytesTransferred = (UInt32)(EndOffset - Offset); + Marshal.Copy(FileNode.FileData, (int)Offset, Buffer, (int)BytesTransferred); + + return STATUS_SUCCESS; + } + + public override Int32 Write( + Object FileNode0, + Object FileDesc, + IntPtr Buffer, + UInt64 Offset, + UInt32 Length, + Boolean WriteToEndOfFile, + Boolean ConstrainedIo, + out UInt32 BytesTransferred, + out FileInfo FileInfo) + { + FileNode FileNode = (FileNode)FileNode0; + UInt64 EndOffset; + + if (ConstrainedIo) + { + if (Offset >= FileNode.FileInfo.FileSize) + { + BytesTransferred = default(UInt32); + FileInfo = default(FileInfo); + return STATUS_SUCCESS; + } + EndOffset = Offset + Length; + if (EndOffset > FileNode.FileInfo.FileSize) + EndOffset = FileNode.FileInfo.FileSize; + } + else + { + if (WriteToEndOfFile) + Offset = FileNode.FileInfo.FileSize; + EndOffset = Offset + Length; + if (EndOffset > FileNode.FileInfo.FileSize) + { + Int32 Result = SetFileSizeInternal(FileNode, EndOffset, false); + if (0 > Result) + { + BytesTransferred = default(UInt32); + FileInfo = default(FileInfo); + return Result; + } + } + } + +#if MEMFS_SLOWIO + if (SlowioReturnPending()) + { + var hint = Host.GetOperationRequestHint(); + try + { + Interlocked.Increment(ref SlowioTasksRunning); + Task.Run(() => SlowioWriteTask(FileNode0, Buffer, Offset, EndOffset, hint)).ConfigureAwait(false); + + BytesTransferred = 0; + FileInfo = default(FileInfo); + return STATUS_PENDING; + } + catch (Exception) + { + Interlocked.Decrement(ref SlowioTasksRunning); + } + } +#endif + + BytesTransferred = (UInt32)(EndOffset - Offset); + Marshal.Copy(Buffer, FileNode.FileData, (int)Offset, (int)BytesTransferred); + + FileInfo = FileNode.GetFileInfo(); + + return STATUS_SUCCESS; + } + + public override Int32 Flush( + Object FileNode0, + Object FileDesc, + out FileInfo FileInfo) + { + FileNode FileNode = (FileNode)FileNode0; + + /* nothing to flush, since we do not cache anything */ + FileInfo = null != FileNode ? FileNode.GetFileInfo() : default(FileInfo); + + return STATUS_SUCCESS; + } + + public override Int32 GetFileInfo( + Object FileNode0, + Object FileDesc, + out FileInfo FileInfo) + { + FileNode FileNode = (FileNode)FileNode0; + + FileInfo = FileNode.GetFileInfo(); + + return STATUS_SUCCESS; + } + + public override Int32 SetBasicInfo( + Object FileNode0, + Object FileDesc, + UInt32 FileAttributes, + UInt64 CreationTime, + UInt64 LastAccessTime, + UInt64 LastWriteTime, + UInt64 ChangeTime, + out FileInfo FileInfo) + { + FileNode FileNode = (FileNode)FileNode0; + + if (null != FileNode.MainFileNode) + FileNode = FileNode.MainFileNode; + + if (unchecked((UInt32)(-1)) != FileAttributes) + FileNode.FileInfo.FileAttributes = FileAttributes; + if (0 != CreationTime) + FileNode.FileInfo.CreationTime = CreationTime; + if (0 != LastAccessTime) + FileNode.FileInfo.LastAccessTime = LastAccessTime; + if (0 != LastWriteTime) + FileNode.FileInfo.LastWriteTime = LastWriteTime; + if (0 != ChangeTime) + FileNode.FileInfo.ChangeTime = ChangeTime; + + FileInfo = FileNode.GetFileInfo(); + + return STATUS_SUCCESS; + } + + public override Int32 SetFileSize( + Object FileNode0, + Object FileDesc, + UInt64 NewSize, + Boolean SetAllocationSize, + out FileInfo FileInfo) + { + FileNode FileNode = (FileNode)FileNode0; + Int32 Result; + + Result = SetFileSizeInternal(FileNode, NewSize, SetAllocationSize); + FileInfo = 0 <= Result ? FileNode.GetFileInfo() : default(FileInfo); + + return STATUS_SUCCESS; + } + + private Int32 SetFileSizeInternal( + FileNode FileNode, + UInt64 NewSize, + Boolean SetAllocationSize) + { + if (SetAllocationSize) + { + if (FileNode.FileInfo.AllocationSize != NewSize) + { + if (NewSize > MaxFileSize) + return STATUS_DISK_FULL; + + byte[] FileData = null; + if (0 != NewSize) + try + { + FileData = new byte[NewSize]; + } + catch + { + return STATUS_INSUFFICIENT_RESOURCES; + } + int CopyLength = (int)Math.Min(FileNode.FileInfo.AllocationSize, NewSize); + if (0 != CopyLength) + Array.Copy(FileNode.FileData, FileData, CopyLength); + + FileNode.FileData = FileData; + FileNode.FileInfo.AllocationSize = NewSize; + if (FileNode.FileInfo.FileSize > NewSize) + FileNode.FileInfo.FileSize = NewSize; + } + } + else + { + if (FileNode.FileInfo.FileSize != NewSize) + { + if (FileNode.FileInfo.AllocationSize < NewSize) + { + UInt64 AllocationUnit = MEMFS_SECTOR_SIZE * MEMFS_SECTORS_PER_ALLOCATION_UNIT; + UInt64 AllocationSize = (NewSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit; + Int32 Result = SetFileSizeInternal(FileNode, AllocationSize, true); + if (0 > Result) + return Result; + } + + if (FileNode.FileInfo.FileSize < NewSize) + { + int CopyLength = (int)(NewSize - FileNode.FileInfo.FileSize); + if (0 != CopyLength) + Array.Clear(FileNode.FileData, (int)FileNode.FileInfo.FileSize, CopyLength); + } + FileNode.FileInfo.FileSize = NewSize; + } + } + + return STATUS_SUCCESS; + } + + public override Int32 CanDelete( + Object FileNode0, + Object FileDesc, + String FileName) + { + FileNode FileNode = (FileNode)FileNode0; + + if (FileNodeMap.HasChild(FileNode)) + return STATUS_DIRECTORY_NOT_EMPTY; + + return STATUS_SUCCESS; + } + + public override Int32 Rename( + Object FileNode0, + Object FileDesc, + String FileName, + String NewFileName, + Boolean ReplaceIfExists) + { + FileNode FileNode = (FileNode)FileNode0; + FileNode NewFileNode; + + NewFileNode = FileNodeMap.Get(NewFileName); + if (null != NewFileNode && FileNode != NewFileNode) + { + if (!ReplaceIfExists) + return STATUS_OBJECT_NAME_COLLISION; + if (0 != (NewFileNode.FileInfo.FileAttributes & (UInt32)FileAttributes.Directory)) + return STATUS_ACCESS_DENIED; + } + + if (null != NewFileNode && FileNode != NewFileNode) + FileNodeMap.Remove(NewFileNode); + + List DescendantFileNames = new List(FileNodeMap.GetDescendantFileNames(FileNode)); + foreach (String DescendantFileName in DescendantFileNames) + { + FileNode DescendantFileNode = FileNodeMap.Get(DescendantFileName); + if (null == DescendantFileNode) + continue; /* should not happen */ + FileNodeMap.Remove(DescendantFileNode); + DescendantFileNode.FileName = + NewFileName + DescendantFileNode.FileName.Substring(FileName.Length); + FileNodeMap.Insert(DescendantFileNode); + } + + return STATUS_SUCCESS; + } + + public override Int32 GetSecurity( + Object FileNode0, + Object FileDesc, + ref Byte[] SecurityDescriptor) + { + FileNode FileNode = (FileNode)FileNode0; + + if (null != FileNode.MainFileNode) + FileNode = FileNode.MainFileNode; + + SecurityDescriptor = FileNode.FileSecurity; + + return STATUS_SUCCESS; + } + + public override Int32 SetSecurity( + Object FileNode0, + Object FileDesc, + AccessControlSections Sections, + Byte[] SecurityDescriptor) + { + FileNode FileNode = (FileNode)FileNode0; + + if (null != FileNode.MainFileNode) + FileNode = FileNode.MainFileNode; + + return ModifySecurityDescriptorEx(FileNode.FileSecurity, Sections, SecurityDescriptor, + ref FileNode.FileSecurity); + } + + public override Boolean ReadDirectoryEntry( + Object FileNode0, + Object FileDesc, + String Pattern, + String Marker, + ref Object Context, + out String FileName, + out FileInfo FileInfo) + { + FileNode FileNode = (FileNode)FileNode0; + IEnumerator Enumerator = (IEnumerator)Context; + + if (null == Enumerator) + { + List ChildrenFileNames = new List(); + if ("\\" != FileNode.FileName) + { + /* if this is not the root directory add the dot entries */ + if (null == Marker) + ChildrenFileNames.Add("."); + if (null == Marker || "." == Marker) + ChildrenFileNames.Add(".."); + } + ChildrenFileNames.AddRange(FileNodeMap.GetChildrenFileNames(FileNode, + "." != Marker && ".." != Marker ? Marker : null)); + Context = Enumerator = ChildrenFileNames.GetEnumerator(); + } + + while (Enumerator.MoveNext()) + { + String FullFileName = Enumerator.Current; + if ("." == FullFileName) + { + FileName = "."; + FileInfo = FileNode.GetFileInfo(); + return true; + } + else if (".." == FullFileName) + { + Int32 Result = STATUS_SUCCESS; + FileNode ParentNode = FileNodeMap.GetParent(FileNode.FileName, ref Result); + if (null != ParentNode) + { + FileName = ".."; + FileInfo = ParentNode.GetFileInfo(); + return true; + } + } + else + { + FileNode ChildFileNode = FileNodeMap.Get(FullFileName); + if (null != ChildFileNode) + { + FileName = Path.GetFileName(FullFileName); + FileInfo = ChildFileNode.GetFileInfo(); + return true; + } + } + } + + FileName = default(String); + FileInfo = default(FileInfo); + return false; + } + +#if MEMFS_SLOWIO + public override int ReadDirectory( + Object FileNode0, + Object FileDesc, + String Pattern, + String Marker, + IntPtr Buffer, + UInt32 Length, + out UInt32 BytesTransferred) + { + if (SlowioReturnPending()) + { + var Hint = Host.GetOperationRequestHint(); + try + { + Interlocked.Increment(ref SlowioTasksRunning); + Task.Run(() => SlowioReadDirectoryTask(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, Hint)); + BytesTransferred = 0; + + return STATUS_PENDING; + } + catch (Exception) + { + Interlocked.Decrement(ref SlowioTasksRunning); + } + } + + return SeekableReadDirectory(FileNode0, FileDesc, Pattern, Marker, Buffer, Length, out BytesTransferred); + } +#endif + + public override int GetDirInfoByName( + Object ParentNode0, + Object FileDesc, + String FileName, + out String NormalizedName, + out FileInfo FileInfo) + { + FileNode ParentNode = (FileNode)ParentNode0; + FileNode FileNode; + + FileName = + ParentNode.FileName + + ("\\" == ParentNode.FileName ? "" : "\\") + + Path.GetFileName(FileName); + + FileNode = FileNodeMap.Get(FileName); + if (null == FileNode) + { + NormalizedName = default(String); + FileInfo = default(FileInfo); + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + NormalizedName = Path.GetFileName(FileNode.FileName); + FileInfo = FileNode.FileInfo; + + return STATUS_SUCCESS; + } + + public override Int32 GetReparsePointByName( + String FileName, + Boolean IsDirectory, + ref Byte[] ReparseData) + { + FileNode FileNode; + + FileNode = FileNodeMap.Get(FileName); + if (null == FileNode) + return STATUS_OBJECT_NAME_NOT_FOUND; + + if (0 == (FileNode.FileInfo.FileAttributes & (UInt32)FileAttributes.ReparsePoint)) + return STATUS_NOT_A_REPARSE_POINT; + + ReparseData = FileNode.ReparseData; + + return STATUS_SUCCESS; + } + + public override Int32 GetReparsePoint( + Object FileNode0, + Object FileDesc, + String FileName, + ref Byte[] ReparseData) + { + FileNode FileNode = (FileNode)FileNode0; + + if (null != FileNode.MainFileNode) + FileNode = FileNode.MainFileNode; + + if (0 == (FileNode.FileInfo.FileAttributes & (UInt32)FileAttributes.ReparsePoint)) + return STATUS_NOT_A_REPARSE_POINT; + + ReparseData = FileNode.ReparseData; + + return STATUS_SUCCESS; + } + + public override Int32 SetReparsePoint( + Object FileNode0, + Object FileDesc, + String FileName, + Byte[] ReparseData) + { + FileNode FileNode = (FileNode)FileNode0; + + if (null != FileNode.MainFileNode) + FileNode = FileNode.MainFileNode; + + if (FileNodeMap.HasChild(FileNode)) + return STATUS_DIRECTORY_NOT_EMPTY; + + if (null != FileNode.ReparseData) + { + Int32 Result = CanReplaceReparsePoint(FileNode.ReparseData, ReparseData); + if (0 > Result) + return Result; + } + + FileNode.FileInfo.FileAttributes |= (UInt32)FileAttributes.ReparsePoint; + FileNode.FileInfo.ReparseTag = GetReparseTag(ReparseData); + FileNode.ReparseData = ReparseData; + + return STATUS_SUCCESS; + } + + public override Int32 DeleteReparsePoint( + Object FileNode0, + Object FileDesc, + String FileName, + Byte[] ReparseData) + { + FileNode FileNode = (FileNode)FileNode0; + + if (null != FileNode.MainFileNode) + FileNode = FileNode.MainFileNode; + + if (null != FileNode.ReparseData) + { + Int32 Result = CanReplaceReparsePoint(FileNode.ReparseData, ReparseData); + if (0 > Result) + return Result; + } + else + return STATUS_NOT_A_REPARSE_POINT; + + FileNode.FileInfo.FileAttributes &= ~(UInt32)FileAttributes.ReparsePoint; + FileNode.FileInfo.ReparseTag = 0; + FileNode.ReparseData = null; + + return STATUS_SUCCESS; + } + + public override Boolean GetStreamEntry( + Object FileNode0, + Object FileDesc, + ref Object Context, + out String StreamName, + out UInt64 StreamSize, + out UInt64 StreamAllocationSize) + { + FileNode FileNode = (FileNode)FileNode0; + IEnumerator Enumerator = (IEnumerator)Context; + + if (null == Enumerator) + { + if (null != FileNode.MainFileNode) + FileNode = FileNode.MainFileNode; + + List StreamFileNames = new List(); + if (0 == (FileNode.FileInfo.FileAttributes & (UInt32)FileAttributes.Directory)) + StreamFileNames.Add(FileNode.FileName); + StreamFileNames.AddRange(FileNodeMap.GetStreamFileNames(FileNode)); + Context = Enumerator = StreamFileNames.GetEnumerator(); + } + + while (Enumerator.MoveNext()) + { + String FullFileName = Enumerator.Current; + FileNode StreamFileNode = FileNodeMap.Get(FullFileName); + if (null != StreamFileNode) + { + int Index = FullFileName.IndexOf(':'); + if (0 > Index) + StreamName = ""; + else + StreamName = FullFileName.Substring(Index + 1); + StreamSize = StreamFileNode.FileInfo.FileSize; + StreamAllocationSize = StreamFileNode.FileInfo.AllocationSize; + return true; + } + } + + StreamName = default(String); + StreamSize = default(UInt64); + StreamAllocationSize = default(UInt64); + return false; + } + public override Boolean GetEaEntry( + Object FileNode0, + Object FileDesc, + ref Object Context, + out String EaName, + out Byte[] EaValue, + out Boolean NeedEa) + { + FileNode FileNode = (FileNode)FileNode0; + IEnumerator> Enumerator = + (IEnumerator>)Context; + + if (null == Enumerator) + { + SortedDictionary EaMap = FileNode.GetEaMap(false); + if (null == EaMap) + { + EaName = default(String); + EaValue = default(Byte[]); + NeedEa = default(Boolean); + return false; + } + + Context = Enumerator = EaMap.GetEnumerator(); + } + + while (Enumerator.MoveNext()) + { + KeyValuePair Pair = Enumerator.Current; + EaName = Pair.Key; + EaValue = Pair.Value.EaValue; + NeedEa = Pair.Value.NeedEa; + return true; + } + + EaName = default(String); + EaValue = default(Byte[]); + NeedEa = default(Boolean); + return false; + } + public override Int32 SetEaEntry( + Object FileNode0, + Object FileDesc, + ref Object Context, + String EaName, + Byte[] EaValue, + Boolean NeedEa) + { + FileNode FileNode = (FileNode)FileNode0; + SortedDictionary EaMap = FileNode.GetEaMap(true); + EaValueData Data; + UInt32 EaSizePlus = 0, EaSizeMinus = 0; + if (EaMap.TryGetValue(EaName, out Data)) + { + EaSizeMinus = GetEaEntrySize(EaName, Data.EaValue, Data.NeedEa); + EaMap.Remove(EaName); + } + if (null != EaValue) + { + Data.EaValue = EaValue; + Data.NeedEa = NeedEa; + EaMap[EaName] = Data; + EaSizePlus = GetEaEntrySize(EaName, EaValue, NeedEa); + } + FileNode.FileInfo.EaSize = FileNode.FileInfo.EaSize + EaSizePlus - EaSizeMinus; + return STATUS_SUCCESS; + } + + private FileNodeMap FileNodeMap; + private UInt32 MaxFileNodes; + private UInt32 MaxFileSize; + private UInt64 SlowioMaxDelay; + private UInt64 SlowioPercentDelay; + private UInt64 SlowioRarefyDelay; + private volatile Int32 SlowioTasksRunning; + private String VolumeLabel; + } + + class MemfsService : Service + { + private class CommandLineUsageException : Exception + { + public CommandLineUsageException(String Message = null) : base(Message) + { + HasMessage = null != Message; + } + + public bool HasMessage; + } + + private const String PROGNAME = "memfs-dotnet"; + + public MemfsService() : base("MemfsService") + { + } + protected override void OnStart(String[] Args) + { + try + { + Boolean CaseInsensitive = false; + String DebugLogFile = null; + UInt32 DebugFlags = 0; + UInt32 FileInfoTimeout = unchecked((UInt32)(-1)); + UInt32 MaxFileNodes = 1024; + UInt32 MaxFileSize = 16 * 1024 * 1024; + UInt32 SlowioMaxDelay = 0; + UInt32 SlowioPercentDelay = 0; + UInt32 SlowioRarefyDelay = 0; + String FileSystemName = null; + String VolumePrefix = null; + String MountPoint = null; + String RootSddl = null; + FileSystemHost Host = null; + Memfs Memfs = null; + int I; + + for (I = 1; Args.Length > I; I++) + { + String Arg = Args[I]; + if ('-' != Arg[0]) + break; + switch (Arg[1]) + { + case '?': + throw new CommandLineUsageException(); + case 'D': + argtos(Args, ref I, ref DebugLogFile); + break; + case 'd': + argtol(Args, ref I, ref DebugFlags); + break; + case 'F': + argtos(Args, ref I, ref FileSystemName); + break; + case 'i': + CaseInsensitive = true; + break; + case 'm': + argtos(Args, ref I, ref MountPoint); + break; + case 'M': + argtol(Args, ref I, ref SlowioMaxDelay); + break; + case 'n': + argtol(Args, ref I, ref MaxFileNodes); + break; + case 'P': + argtol(Args, ref I, ref SlowioPercentDelay); + break; + case 'R': + argtol(Args, ref I, ref SlowioRarefyDelay); + break; + case 'S': + argtos(Args, ref I, ref RootSddl); + break; + case 's': + argtol(Args, ref I, ref MaxFileSize); + break; + case 't': + argtol(Args, ref I, ref FileInfoTimeout); + break; + case 'u': + argtos(Args, ref I, ref VolumePrefix); + break; + default: + throw new CommandLineUsageException(); + } + } + + if (Args.Length > I) + throw new CommandLineUsageException(); + + if ((null == VolumePrefix || 0 == VolumePrefix.Length) && null == MountPoint) + throw new CommandLineUsageException(); + + if (null != DebugLogFile) + if (0 > FileSystemHost.SetDebugLogFile(DebugLogFile)) + throw new CommandLineUsageException("cannot open debug log file"); + + Host = new FileSystemHost(Memfs = new Memfs( + CaseInsensitive, MaxFileNodes, MaxFileSize, RootSddl, + SlowioMaxDelay, SlowioPercentDelay, SlowioRarefyDelay)); + Host.FileInfoTimeout = FileInfoTimeout; + Host.Prefix = VolumePrefix; + Host.FileSystemName = null != FileSystemName ? FileSystemName : "-MEMFS"; + if (0 > Host.Mount(MountPoint, null, false, DebugFlags)) + throw new IOException("cannot mount file system"); + MountPoint = Host.MountPoint(); + _Host = Host; + + Log(EVENTLOG_INFORMATION_TYPE, String.Format("{0} -t {1} -n {2} -s {3}{4}{5}{6}{7}{8}{9}", + PROGNAME, (Int32)FileInfoTimeout, MaxFileNodes, MaxFileSize, + null != RootSddl ? " -S " : "", null != RootSddl ? RootSddl : "", + null != VolumePrefix && 0 < VolumePrefix.Length ? " -u " : "", + null != VolumePrefix && 0 < VolumePrefix.Length ? VolumePrefix : "", + null != MountPoint ? " -m " : "", null != MountPoint ? MountPoint : "")); + } + catch (CommandLineUsageException ex) + { + Log(EVENTLOG_ERROR_TYPE, String.Format( + "{0}" + + "usage: {1} OPTIONS\n" + + "\n" + + "options:\n" + + " -d DebugFlags [-1: enable all debug logs]\n" + + " -D DebugLogFile [file path; use - for stderr]\n" + + " -i [case insensitive file system]\n" + + " -t FileInfoTimeout [millis]\n" + + " -n MaxFileNodes\n" + + " -s MaxFileSize [bytes]\n" + + " -M MaxDelay [maximum slow IO delay in millis]\n" + + " -P PercentDelay [percent of slow IO to make pending]\n" + + " -R RarefyDelay [adjust the rarity of pending slow IO]\n" + + " -F FileSystemName\n" + + " -S RootSddl [file rights: FA, etc; NO generic rights: GA, etc.]\n" + + " -u \\Server\\Share [UNC prefix (single backslash)]\n" + + " -m MountPoint [X:|* (required if no UNC prefix)]\n", + ex.HasMessage ? ex.Message + "\n" : "", + PROGNAME)); + throw; + } + catch (Exception ex) + { + Log(EVENTLOG_ERROR_TYPE, String.Format("{0}", ex.Message)); + throw; + } + } + protected override void OnStop() + { + _Host.Unmount(); + _Host = null; + } + + private static void argtos(String[] Args, ref int I, ref String V) + { + if (Args.Length > ++I) + V = Args[I]; + else + throw new CommandLineUsageException(); + } + private static void argtol(String[] Args, ref int I, ref UInt32 V) + { + Int32 R; + if (Args.Length > ++I) + V = Int32.TryParse(Args[I], out R) ? (UInt32)R : V; + else + throw new CommandLineUsageException(); + } + + private FileSystemHost _Host; + } + + class Program + { + static void Main(string[] args) + { + Environment.ExitCode = new MemfsService().Run(); + } + } +} diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse/Makefile b/3rd_party/winfsp-1.10/samples/memfs-fuse/Makefile new file mode 100644 index 00000000..45f131f3 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse/Makefile @@ -0,0 +1,18 @@ +usage: + @echo "make cygfuse|winfsp-fuse" 1>&2 + @echo "" 1>&2 + @echo " cygfuse Link with CYGFUSE" 1>&2 + @echo " winfsp-fuse Link with WinFsp-FUSE" 1>&2 + @exit 2 + +cygfuse: memfs-cygfuse + +winfsp-fuse: memfs-winfsp-fuse + +memfs-cygfuse: memfs-fuse.cpp + g++ $^ -o $@ -g -Wall -std=gnu++17 `pkg-config fuse --cflags --libs` + +memfs-winfsp-fuse: export PKG_CONFIG_PATH=$(PWD)/winfsp.install/lib +memfs-winfsp-fuse: memfs-fuse.cpp + ln -nsf "`regtool --wow32 get '/HKLM/Software/WinFsp/InstallDir' | cygpath -au -f -`" winfsp.install + g++ $^ -o $@ -g -Wall -std=gnu++17 `pkg-config fuse --cflags --libs` diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse/README.md b/3rd_party/winfsp-1.10/samples/memfs-fuse/README.md new file mode 100644 index 00000000..87994104 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse/README.md @@ -0,0 +1,7 @@ +`Memfs-fuse` is an in-memory FUSE file system. + +It can be built with the following tools: + +- Using Visual Studio (`memfs-fuse.sln`). +- Using Cygwin GCC and linking directly with the WinFsp DLL (`make winfsp-fuse`). +- Using Cygwin GCC and linking to CYGFUSE (`make cygfuse`). diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse/compat.h b/3rd_party/winfsp-1.10/samples/memfs-fuse/compat.h new file mode 100644 index 00000000..30c4b7ee --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse/compat.h @@ -0,0 +1,104 @@ +/** + * @file compat.h + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef COMPAT_H_INCLUDED +#define COMPAT_H_INCLUDED + +#if defined(_WIN32) && defined(FSP_FUSE_SYM) +#include +#undef fuse_main +#define fuse_main(argc, argv, ops, data)\ + (FspLoad(0), fuse_main_real(argc, argv, ops, sizeof *(ops), data)) +#endif + +#if !defined(_WIN32) && !defined(fuse_stat) + +#define fuse_uid_t uid_t +#define fuse_gid_t gid_t +#define fuse_pid_t pid_t + +#define fuse_dev_t dev_t +#define fuse_mode_t mode_t +#define fuse_nlink_t nlink_t +#define fuse_off_t off_t + +#define fuse_fsblkcnt_t fsblkcnt_t +#define fuse_fsfilcnt_t fsfilcnt_t +#define fuse_blksize_t blksize_t +#define fuse_blkcnt_t blkcnt_t + +#define fuse_timespec timespec + +#define fuse_stat stat + +#define fuse_statvfs statvfs + +#define fuse_flock flock + +#define fuse_iovec iovec + +#endif + +#if !defined(S_IFMT) +#define S_IFMT 0170000 +#endif +#if !defined(S_IFDIR) +#define S_IFDIR 0040000 +#endif +#if !defined(S_IFCHR) +#define S_IFCHR 0020000 +#endif +#if !defined(S_IFBLK) +#define S_IFBLK 0060000 +#endif +#if !defined(S_IFREG) +#define S_IFREG 0100000 +#endif +#if !defined(S_IFLNK) +#define S_IFLNK 0120000 +#endif +#if !defined(S_IFSOCK) +#define S_IFSOCK 0140000 +#endif +#if !defined(S_IFIFO) +#define S_IFIFO 0010000 +#endif + +#if defined(__APPLE__) +#define st_atim st_atimespec +#define st_ctim st_ctimespec +#define st_mtim st_mtimespec +#endif + +#if defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__) +#include +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(_WIN32) +#define XATTR_CREATE 1 +#define XATTR_REPLACE 2 +#endif + +#if !defined(ENOATTR) +#define ENOATTR ENODATA +#elif !defined(ENODATA) +#define ENODATA ENOATTR +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse/memfs-fuse.cpp b/3rd_party/winfsp-1.10/samples/memfs-fuse/memfs-fuse.cpp new file mode 100644 index 00000000..0666d072 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse/memfs-fuse.cpp @@ -0,0 +1,693 @@ +/** + * @file memfs-fuse.c + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "compat.h" + +class memfs +{ +public: + memfs() : _ino(1), _root(std::make_shared(_ino, S_IFDIR | 00777, 0, 0)) + { + } + + int main(int argc, char *argv[]) + { + static fuse_operations ops = + { + getattr, + 0, // getdir + readlink, + mknod, + mkdir, + unlink, + rmdir, + symlink, + rename, + link, + chmod, + chown, + truncate, + 0, // utime + open, + read, + write, + statfs, + flush, + release, + 0, // fsync + setxattr, + getxattr, + listxattr, + removexattr, + opendir, + readdir, + releasedir, + 0, // fsyncdir + init, + 0, // destroy + 0, // access + 0, // create + ftruncate, + fgetattr, + 0, // lock + utimens, + 0, // bmap + 0, // flag_nullpath_ok + 0, // flag_nopath + 0, // flag_utime_omit_ok + 0, // flag_reserved + 0, // ioctl + 0, // poll + 0, // write_buf + 0, // read_buf + 0, // flock + 0, // fallocate + 0, // reserved00 + 0, // reserved01 + 0, // reserved02 + 0, // statfs_x + 0, // setvolname + 0, // exchange + 0, // getxtimes + 0, // setbkuptime + 0, // setchgtime + setcrtime, +#if defined(FSP_FUSE_USE_STAT_EX) + chflags, +#else + 0, // chflags +#endif + 0, // setattr_x + 0, // fsetattr_x + }; + return fuse_main(argc, argv, &ops, this); + } + +private: + struct node_t + { + node_t(fuse_ino_t ino, fuse_mode_t mode, fuse_uid_t uid, fuse_gid_t gid, fuse_dev_t dev = 0) + : stat() + { + stat.st_ino = ino; + stat.st_mode = mode; + stat.st_nlink = 1; + stat.st_uid = uid; + stat.st_gid = gid; + stat.st_rdev = dev; + stat.st_atim = stat.st_mtim = stat.st_ctim = stat.st_birthtim = now(); + } + + void resize(size_t size, bool capacity) + { + if (capacity) + { + const size_t unit = 64 * 1024; + size_t newcap = (size + unit - 1) / unit * unit; + size_t oldcap = data.capacity(); + if (newcap > oldcap) + data.reserve(newcap); + else if (newcap < oldcap) + { + data.resize(newcap); + data.shrink_to_fit(); + } + } + data.resize(size); + stat.st_size = size; + } + + struct fuse_stat stat; + std::vector data; + std::unordered_map> childmap; + std::unordered_map> xattrmap; + }; + + static fuse_timespec now() + { + using namespace std::chrono; + auto now = system_clock::now(); + auto sec = floor(now); + auto nsec = floor(now) - floor(sec); + return fuse_timespec + { + static_cast(sec.time_since_epoch().count()), + /* std::chrono epoch is UNIX epoch in C++20 */ + static_cast(nsec.count()), + }; + } + + static memfs *getself() + { + return static_cast(fuse_get_context()->private_data); + } + + static int getattr(const char *path, struct fuse_stat *stbuf) + { + return fgetattr(path, stbuf, nullptr); + } + + static int fgetattr(const char *path, struct fuse_stat *stbuf, struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + *stbuf = node->stat; + return 0; + } + + static int readlink(const char *path, char *buf, size_t size) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + if (S_IFLNK != (node->stat.st_mode & S_IFMT)) + return EINVAL; + size = (std::min)(size - 1, node->data.size()); + std::memcpy(buf, node->data.data(), size); + buf[size] = '\0'; + return 0; + } + + static int mknod(const char *path, fuse_mode_t mode, fuse_dev_t dev) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->make_node(path, mode, dev); + } + + static int mkdir(const char *path, fuse_mode_t mode) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->make_node(path, S_IFDIR | (mode & 07777), 0); + } + + static int unlink(const char *path) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->remove_node(path, false); + } + + static int rmdir(const char *path) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->remove_node(path, true); + } + + static int symlink(const char *dstpath, const char *srcpath) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->make_node(srcpath, S_IFLNK | 00777, 0, dstpath); + } + + static int rename(const char *oldpath, const char *newpath) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto oldlookup = self->lookup_node(oldpath); + auto oldprnt = std::get<0>(oldlookup); + auto oldname = std::get<1>(oldlookup); + auto oldnode = std::get<2>(oldlookup); + if (!oldnode) + return -ENOENT; + auto newlookup = self->lookup_node(newpath); + auto newprnt = std::get<0>(newlookup); + auto newname = std::get<1>(newlookup); + auto newnode = std::get<2>(newlookup); + if (!newprnt) + return -ENOENT; + if (newname.empty()) + // guard against directory loop creation + return -EINVAL; + if (oldprnt == newprnt && oldname == newname) + return 0; + if (newnode) + { + if (int errc = self->remove_node(newpath, S_IFDIR == (oldnode->stat.st_mode & S_IFMT))) + return errc; + } + oldprnt->childmap.erase(oldname); + newprnt->childmap[newname] = oldnode; + return 0; + } + + static int link(const char *oldpath, const char *newpath) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto oldlookup = self->lookup_node(oldpath); + auto oldnode = std::get<2>(oldlookup); + if (!oldnode) + return -ENOENT; + auto newlookup = self->lookup_node(newpath); + auto newprnt = std::get<0>(newlookup); + auto newname = std::get<1>(newlookup); + auto newnode = std::get<2>(newlookup); + if (!newprnt) + return -ENOENT; + if (newnode) + return -EEXIST; + oldnode->stat.st_nlink++; + newprnt->childmap[newname] = oldnode; + oldnode->stat.st_ctim = newprnt->stat.st_ctim = newprnt->stat.st_mtim = now(); + return 0; + } + + static int chmod(const char *path, fuse_mode_t mode) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + node->stat.st_mode = (node->stat.st_mode & S_IFMT) | (mode & 07777); + node->stat.st_ctim = now(); + return 0; + } + + static int chown(const char *path, fuse_uid_t uid, fuse_gid_t gid) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + if (-1 != uid) + node->stat.st_uid = uid; + if (-1 != gid) + node->stat.st_gid = gid; + node->stat.st_ctim = now(); + return 0; + } + + static int truncate(const char *path, fuse_off_t size) + { + return ftruncate(path, size, nullptr); + } + + static int ftruncate(const char *path, fuse_off_t size, + struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + if (SIZE_MAX < size) + return -EFBIG; + node->resize(static_cast(size), true); + node->stat.st_ctim = node->stat.st_mtim = now(); +#if defined(FSP_FUSE_USE_STAT_EX) + node->stat.st_flags |= UF_ARCHIVE; +#endif + return 0; + } + + static int open(const char *path, struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->open_node(path, false, fi); + } + + static int read(const char *path, char *buf, size_t size, fuse_off_t off, + struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + fuse_off_t endoff = (std::min)( + off + static_cast(size), static_cast(node->data.size())); + if (off > endoff) + return 0; + std::memcpy(buf, node->data.data() + off, static_cast(endoff - off)); + node->stat.st_atim = now(); + return static_cast(endoff - off); + } + + static int write(const char *path, const char *buf, size_t size, fuse_off_t off, + struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + fuse_off_t endoff = off + static_cast(size); + if (SIZE_MAX < endoff) + return -EFBIG; + if (node->data.size() < endoff) + node->resize(static_cast(endoff), true); + std::memcpy(node->data.data() + off, buf, static_cast(endoff - off)); + node->stat.st_ctim = node->stat.st_mtim = now(); +#if defined(FSP_FUSE_USE_STAT_EX) + node->stat.st_flags |= UF_ARCHIVE; +#endif + return static_cast(endoff - off); + } + + static int statfs(const char *path, struct fuse_statvfs *stbuf) + { + std::memset(stbuf, 0, sizeof *stbuf); + return 0; + } + + static int flush(const char *path, struct fuse_file_info *fi) + { + return -ENOSYS; + } + + static int release(const char *path, struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->close_node(fi); + } + + static int setxattr(const char *path, const char *name0, const char *value, size_t size, + int flags) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + if (0 == std::strcmp("com.apple.ResourceFork", name0)) + return -ENOTSUP; + std::string name = name0; + if (XATTR_CREATE == flags) + { + if (node->xattrmap.end() != node->xattrmap.find(name)) + return -EEXIST; + } + else if (XATTR_REPLACE == flags) + { + if (node->xattrmap.end() == node->xattrmap.find(name)) + return -ENOATTR; + } + node->xattrmap[name].assign(value, value + size); + return 0; + } + + static int getxattr(const char *path, const char *name0, char *value, size_t size) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + if (0 == std::strcmp("com.apple.ResourceFork", name0)) + return -ENOTSUP; + std::string name = name0; + auto iter = node->xattrmap.find(name); + if (node->xattrmap.end() == iter) + return -ENOATTR; + if (0 != size) + { + if (iter->second.size() > size) + return -ERANGE; + std::memcpy(value, iter->second.data(), iter->second.size()); + } + return static_cast(iter->second.size()); + } + + static int listxattr(const char *path, char *namebuf, size_t size) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + size_t copysize = 0; + for (auto elem : node->xattrmap) + { + size_t namesize = elem.first.size() + 1; + if (0 != size) + { + if (copysize + namesize > size) + return -ERANGE; + std::memcpy(namebuf + copysize, elem.first.c_str(), namesize); + copysize += namesize; + } + } + return static_cast(copysize); + } + + static int removexattr(const char *path, const char *name0) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + if (0 == std::strcmp("com.apple.ResourceFork", name0)) + return -ENOTSUP; + std::string name = name0; + return node->xattrmap.erase(name) ? 0 : -ENOATTR; + } + + static int opendir(const char *path, struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->open_node(path, true, fi); + } + + static int readdir(const char *path, void *buf, fuse_fill_dir_t filler, fuse_off_t off, + struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + filler(buf, ".", &node->stat, 0); + filler(buf, "..", nullptr, 0); + for (auto elem : node->childmap) + if (0 != filler(buf, elem.first.c_str(), &elem.second->stat, 0)) + break; + return 0; + } + + static int releasedir(const char *path, struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->close_node(fi); + } + + static void *init(struct fuse_conn_info *conn) + { +#if defined(FSP_FUSE_CAP_READDIR_PLUS) + conn->want |= (conn->capable & FSP_FUSE_CAP_READDIR_PLUS); +#endif + +#if defined(FSP_FUSE_USE_STAT_EX) && defined(FSP_FUSE_CAP_STAT_EX) + conn->want |= (conn->capable & FSP_FUSE_CAP_STAT_EX); +#endif + + return getself(); + } + + static int utimens(const char *path, const struct fuse_timespec tmsp[2]) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + if (tmsp) + { + node->stat.st_ctim = now(); + node->stat.st_atim = tmsp[0]; + node->stat.st_mtim = tmsp[1]; + } + else + node->stat.st_ctim = node->stat.st_atim = node->stat.st_mtim = now(); + return 0; + } + + static int setcrtime(const char *path, const struct fuse_timespec *tmsp) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + if (tmsp) + { + node->stat.st_ctim = now(); + node->stat.st_birthtim = tmsp[0]; + } + else + node->stat.st_ctim = node->stat.st_birthtim = now(); + return 0; + } + +#if defined(FSP_FUSE_USE_STAT_EX) + static int chflags(const char *path, uint32_t flags) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + node->stat.st_flags = flags; + node->stat.st_ctim = now(); + return 0; + } +#endif + + std::tuple, std::string, std::shared_ptr> + lookup_node(const char *path, node_t *ancestor = nullptr) + { + auto prnt = _root; + std::string name; + auto node = prnt; + for (const char *part = path, *p; *part; part = p + !!(*p)) + { + for (p = part; *p && '/' != *p; p++) + ; + if (part == p) + continue; + prnt = node; + if (!node) + break; + name.assign(part, p); + auto iter = node->childmap.find(name); + node = node->childmap.end() != iter ? iter->second : nullptr; + if (ancestor && node.get() == ancestor) + { + name.assign(""); // special case loop condition + break; + } + } + return std::make_tuple(prnt, name, node); + } + + int make_node(const char *path, fuse_mode_t mode, fuse_dev_t dev, const char *data = nullptr) + { + auto lookup = lookup_node(path); + auto prnt = std::get<0>(lookup); + auto name = std::get<1>(lookup); + auto node = std::get<2>(lookup); + if (!prnt) + return -ENOENT; + if (node) + return -EEXIST; + fuse_context *context = fuse_get_context(); + node = std::make_shared(++_ino, mode, context->uid, context->gid, dev); +#if defined(FSP_FUSE_USE_STAT_EX) + if (S_IFDIR != (mode & S_IFMT)) + node->stat.st_flags |= UF_ARCHIVE; +#endif + if (data) + { + node->resize(std::strlen(data), false); + std::memcpy(node->data.data(), data, node->data.size()); + } + prnt->childmap[name] = node; + prnt->stat.st_ctim = prnt->stat.st_mtim = node->stat.st_ctim; + return 0; + } + + int remove_node(const char *path, bool dir) + { + auto lookup = lookup_node(path); + auto prnt = std::get<0>(lookup); + auto name = std::get<1>(lookup); + auto node = std::get<2>(lookup); + if (!node) + return -ENOENT; + if (!dir && S_IFDIR == (node->stat.st_mode & S_IFMT)) + return -EISDIR; + if (dir && S_IFDIR != (node->stat.st_mode & S_IFMT)) + return -ENOTDIR; + if (0 < node->childmap.size()) + return -ENOTEMPTY; + node->stat.st_nlink--; + prnt->childmap.erase(name); + node->stat.st_ctim = prnt->stat.st_ctim = prnt->stat.st_mtim = now(); + return 0; + } + + int open_node(const char *path, bool dir, struct fuse_file_info *fi) + { + auto node = std::get<2>(lookup_node(path)); + if (!node) + return -ENOENT; + if (!dir && S_IFDIR == (node->stat.st_mode & S_IFMT)) + return -EISDIR; + if (dir && S_IFDIR != (node->stat.st_mode & S_IFMT)) + return -ENOTDIR; + // A file descriptor is a raw pointer to a shared_ptr. + // This has the effect of incrementing the shared_ptr + // refcount, thus keeping an open node around even + // if the node is unlinked. + fi->fh = (uint64_t)(uintptr_t)new std::shared_ptr(node); + return 0; + } + + int close_node(struct fuse_file_info *fi) + { + delete (std::shared_ptr *)(uintptr_t)fi->fh; + return 0; + } + + std::shared_ptr get_node(const char *path, struct fuse_file_info *fi = nullptr) + { + if (!fi) + return std::get<2>(lookup_node(path)); + else + return *(std::shared_ptr *)(uintptr_t)fi->fh; + } + +private: + std::mutex _mutex; + fuse_ino_t _ino; + std::shared_ptr _root; +}; + +int main(int argc, char *argv[]) +{ + return memfs().main(argc, argv); +} diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse/memfs-fuse.sln b/3rd_party/winfsp-1.10/samples/memfs-fuse/memfs-fuse.sln new file mode 100644 index 00000000..7d83d50e --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse/memfs-fuse.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "memfs-fuse", "memfs-fuse.vcxproj", "{CF538F42-C714-4653-B351-E72FD7B0B217}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x64.ActiveCfg = Debug|x64 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x64.Build.0 = Debug|x64 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x86.ActiveCfg = Debug|Win32 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x86.Build.0 = Debug|Win32 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x64.ActiveCfg = Release|x64 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x64.Build.0 = Release|x64 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x86.ActiveCfg = Release|Win32 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse/memfs-fuse.vcxproj b/3rd_party/winfsp-1.10/samples/memfs-fuse/memfs-fuse.vcxproj new file mode 100644 index 00000000..d78f0e8d --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse/memfs-fuse.vcxproj @@ -0,0 +1,189 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {CF538F42-C714-4653-B351-E72FD7B0B217} + Win32Proj + memfsfuse + $(LatestTargetPlatformVersion) + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + + + + Level3 + Disabled + FSP_FUSE_USE_STAT_EX;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreadedDebug + 4018 + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + Level3 + Disabled + FSP_FUSE_USE_STAT_EX;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreadedDebug + 4018 + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + FSP_FUSE_USE_STAT_EX;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + 4018 + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + FSP_FUSE_USE_STAT_EX;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + 4018 + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + + + + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse/memfs-fuse.vcxproj.filters b/3rd_party/winfsp-1.10/samples/memfs-fuse/memfs-fuse.vcxproj.filters new file mode 100644 index 00000000..b9259675 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse/memfs-fuse.vcxproj.filters @@ -0,0 +1,19 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source + + + + + Source + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse3/Makefile b/3rd_party/winfsp-1.10/samples/memfs-fuse3/Makefile new file mode 100644 index 00000000..90e69286 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse3/Makefile @@ -0,0 +1,18 @@ +usage: + @echo "make cygfuse3|winfsp-fuse3" 1>&2 + @echo "" 1>&2 + @echo " cygfuse3 Link with CYGFUSE3" 1>&2 + @echo " winfsp-fuse3 Link with WinFsp-FUSE3" 1>&2 + @exit 2 + +cygfuse3: memfs-cygfuse3 + +winfsp-fuse3: memfs-winfsp-fuse3 + +memfs-cygfuse3: memfs-fuse3.cpp + g++ $^ -o $@ -g -Wall -std=gnu++17 `pkg-config fuse3 --cflags --libs` + +memfs-winfsp-fuse3: export PKG_CONFIG_PATH=$(PWD)/winfsp.install/lib +memfs-winfsp-fuse3: memfs-fuse3.cpp + ln -nsf "`regtool --wow32 get '/HKLM/Software/WinFsp/InstallDir' | cygpath -au -f -`" winfsp.install + g++ $^ -o $@ -g -Wall -std=gnu++17 `pkg-config fuse3 --cflags --libs` diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse3/README.md b/3rd_party/winfsp-1.10/samples/memfs-fuse3/README.md new file mode 100644 index 00000000..ccfc716e --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse3/README.md @@ -0,0 +1,7 @@ +`Memfs-fuse3` is an in-memory FUSE3 file system. + +It can be built with the following tools: + +- Using Visual Studio (`memfs-fuse3.sln`). +- Using Cygwin GCC and linking directly with the WinFsp DLL (`make winfsp-fuse3`). +- Using Cygwin GCC and linking to CYGFUSE3 (`make cygfuse3`). diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse3/compat.h b/3rd_party/winfsp-1.10/samples/memfs-fuse3/compat.h new file mode 100644 index 00000000..30c4b7ee --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse3/compat.h @@ -0,0 +1,104 @@ +/** + * @file compat.h + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef COMPAT_H_INCLUDED +#define COMPAT_H_INCLUDED + +#if defined(_WIN32) && defined(FSP_FUSE_SYM) +#include +#undef fuse_main +#define fuse_main(argc, argv, ops, data)\ + (FspLoad(0), fuse_main_real(argc, argv, ops, sizeof *(ops), data)) +#endif + +#if !defined(_WIN32) && !defined(fuse_stat) + +#define fuse_uid_t uid_t +#define fuse_gid_t gid_t +#define fuse_pid_t pid_t + +#define fuse_dev_t dev_t +#define fuse_mode_t mode_t +#define fuse_nlink_t nlink_t +#define fuse_off_t off_t + +#define fuse_fsblkcnt_t fsblkcnt_t +#define fuse_fsfilcnt_t fsfilcnt_t +#define fuse_blksize_t blksize_t +#define fuse_blkcnt_t blkcnt_t + +#define fuse_timespec timespec + +#define fuse_stat stat + +#define fuse_statvfs statvfs + +#define fuse_flock flock + +#define fuse_iovec iovec + +#endif + +#if !defined(S_IFMT) +#define S_IFMT 0170000 +#endif +#if !defined(S_IFDIR) +#define S_IFDIR 0040000 +#endif +#if !defined(S_IFCHR) +#define S_IFCHR 0020000 +#endif +#if !defined(S_IFBLK) +#define S_IFBLK 0060000 +#endif +#if !defined(S_IFREG) +#define S_IFREG 0100000 +#endif +#if !defined(S_IFLNK) +#define S_IFLNK 0120000 +#endif +#if !defined(S_IFSOCK) +#define S_IFSOCK 0140000 +#endif +#if !defined(S_IFIFO) +#define S_IFIFO 0010000 +#endif + +#if defined(__APPLE__) +#define st_atim st_atimespec +#define st_ctim st_ctimespec +#define st_mtim st_mtimespec +#endif + +#if defined(__APPLE__) || defined(__linux__) || defined(__CYGWIN__) +#include +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(_WIN32) +#define XATTR_CREATE 1 +#define XATTR_REPLACE 2 +#endif + +#if !defined(ENOATTR) +#define ENOATTR ENODATA +#elif !defined(ENODATA) +#define ENODATA ENOATTR +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse3/memfs-fuse3.cpp b/3rd_party/winfsp-1.10/samples/memfs-fuse3/memfs-fuse3.cpp new file mode 100644 index 00000000..2a92a4a0 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse3/memfs-fuse3.cpp @@ -0,0 +1,619 @@ +/** + * @file memfs-fuse3.c + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "compat.h" + +class memfs +{ +public: + memfs() : _ino(1), _root(std::make_shared(_ino, S_IFDIR | 00777, 0, 0)) + { + } + + int main(int argc, char *argv[]) + { + static fuse_operations ops = + { + getattr, + readlink, + mknod, + mkdir, + unlink, + rmdir, + symlink, + rename, + link, + chmod, + chown, + truncate, + open, + read, + write, + statfs, + flush, + release, + 0, // fsync + setxattr, + getxattr, + listxattr, + removexattr, + opendir, + readdir, + releasedir, + 0, // fsyncdir + init, + 0, // destroy + 0, // access + 0, // create + 0, // lock + utimens, + 0, // bmap +#if 0 + ioctl, +#endif + }; + return fuse_main(argc, argv, &ops, this); + } + +private: + struct node_t + { + node_t(fuse_ino_t ino, fuse_mode_t mode, fuse_uid_t uid, fuse_gid_t gid, fuse_dev_t dev = 0) + : stat() + { + stat.st_ino = ino; + stat.st_mode = mode; + stat.st_nlink = 1; + stat.st_uid = uid; + stat.st_gid = gid; + stat.st_rdev = dev; + stat.st_atim = stat.st_mtim = stat.st_ctim = now(); + } + + void resize(size_t size, bool capacity) + { + if (capacity) + { + const size_t unit = 64 * 1024; + size_t newcap = (size + unit - 1) / unit * unit; + size_t oldcap = data.capacity(); + if (newcap > oldcap) + data.reserve(newcap); + else if (newcap < oldcap) + { + data.resize(newcap); + data.shrink_to_fit(); + } + } + data.resize(size); + stat.st_size = size; + } + + struct fuse_stat stat; + std::vector data; + std::unordered_map> childmap; + std::unordered_map> xattrmap; + }; + + static fuse_timespec now() + { + using namespace std::chrono; + auto now = system_clock::now(); + auto sec = floor(now); + auto nsec = floor(now) - floor(sec); + return fuse_timespec + { + static_cast(sec.time_since_epoch().count()), + /* std::chrono epoch is UNIX epoch in C++20 */ + static_cast(nsec.count()), + }; + } + + static memfs *getself() + { + return static_cast(fuse_get_context()->private_data); + } + + static int getattr(const char *path, struct fuse_stat *stbuf, struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + *stbuf = node->stat; + return 0; + } + + static int readlink(const char *path, char *buf, size_t size) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + if (S_IFLNK != (node->stat.st_mode & S_IFMT)) + return EINVAL; + size = (std::min)(size - 1, node->data.size()); + std::memcpy(buf, node->data.data(), size); + buf[size] = '\0'; + return 0; + } + + static int mknod(const char *path, fuse_mode_t mode, fuse_dev_t dev) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->make_node(path, mode, dev); + } + + static int mkdir(const char *path, fuse_mode_t mode) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->make_node(path, S_IFDIR | (mode & 07777), 0); + } + + static int unlink(const char *path) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->remove_node(path, false); + } + + static int rmdir(const char *path) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->remove_node(path, true); + } + + static int symlink(const char *dstpath, const char *srcpath) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->make_node(srcpath, S_IFLNK | 00777, 0, dstpath); + } + + static int rename(const char *oldpath, const char *newpath, unsigned int flags) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto oldlookup = self->lookup_node(oldpath); + auto oldprnt = std::get<0>(oldlookup); + auto oldname = std::get<1>(oldlookup); + auto oldnode = std::get<2>(oldlookup); + if (!oldnode) + return -ENOENT; + auto newlookup = self->lookup_node(newpath); + auto newprnt = std::get<0>(newlookup); + auto newname = std::get<1>(newlookup); + auto newnode = std::get<2>(newlookup); + if (!newprnt) + return -ENOENT; + if (newname.empty()) + // guard against directory loop creation + return -EINVAL; + if (oldprnt == newprnt && oldname == newname) + return 0; + if (newnode) + { + if (int errc = self->remove_node(newpath, S_IFDIR == (oldnode->stat.st_mode & S_IFMT))) + return errc; + } + oldprnt->childmap.erase(oldname); + newprnt->childmap[newname] = oldnode; + return 0; + } + + static int link(const char *oldpath, const char *newpath) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto oldlookup = self->lookup_node(oldpath); + auto oldnode = std::get<2>(oldlookup); + if (!oldnode) + return -ENOENT; + auto newlookup = self->lookup_node(newpath); + auto newprnt = std::get<0>(newlookup); + auto newname = std::get<1>(newlookup); + auto newnode = std::get<2>(newlookup); + if (!newprnt) + return -ENOENT; + if (newnode) + return -EEXIST; + oldnode->stat.st_nlink++; + newprnt->childmap[newname] = oldnode; + oldnode->stat.st_ctim = newprnt->stat.st_ctim = newprnt->stat.st_mtim = now(); + return 0; + } + + static int chmod(const char *path, fuse_mode_t mode, + struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + node->stat.st_mode = (node->stat.st_mode & S_IFMT) | (mode & 07777); + node->stat.st_ctim = now(); + return 0; + } + + static int chown(const char *path, fuse_uid_t uid, fuse_gid_t gid, + struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + if (-1 != uid) + node->stat.st_uid = uid; + if (-1 != gid) + node->stat.st_gid = gid; + node->stat.st_ctim = now(); + return 0; + } + + static int truncate(const char *path, fuse_off_t size, + struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + if (SIZE_MAX < size) + return -EFBIG; + node->resize(static_cast(size), true); + node->stat.st_ctim = node->stat.st_mtim = now(); + return 0; + } + + static int open(const char *path, struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->open_node(path, false, fi); + } + + static int read(const char *path, char *buf, size_t size, fuse_off_t off, + struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + fuse_off_t endoff = (std::min)( + off + static_cast(size), static_cast(node->data.size())); + if (off > endoff) + return 0; + std::memcpy(buf, node->data.data() + off, static_cast(endoff - off)); + node->stat.st_atim = now(); + return static_cast(endoff - off); + } + + static int write(const char *path, const char *buf, size_t size, fuse_off_t off, + struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + fuse_off_t endoff = off + static_cast(size); + if (SIZE_MAX < endoff) + return -EFBIG; + if (node->data.size() < endoff) + node->resize(static_cast(endoff), true); + std::memcpy(node->data.data() + off, buf, static_cast(endoff - off)); + node->stat.st_ctim = node->stat.st_mtim = now(); + return static_cast(endoff - off); + } + + static int statfs(const char *path, struct fuse_statvfs *stbuf) + { + std::memset(stbuf, 0, sizeof *stbuf); + return 0; + } + + static int flush(const char *path, struct fuse_file_info *fi) + { + return -ENOSYS; + } + + static int release(const char *path, struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->close_node(fi); + } + + static int setxattr(const char *path, const char *name0, const char *value, size_t size, + int flags) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + if (0 == std::strcmp("com.apple.ResourceFork", name0)) + return -ENOTSUP; + std::string name = name0; + if (XATTR_CREATE == flags) + { + if (node->xattrmap.end() != node->xattrmap.find(name)) + return -EEXIST; + } + else if (XATTR_REPLACE == flags) + { + if (node->xattrmap.end() == node->xattrmap.find(name)) + return -ENOATTR; + } + node->xattrmap[name].assign(value, value + size); + return 0; + } + + static int getxattr(const char *path, const char *name0, char *value, size_t size) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + if (0 == std::strcmp("com.apple.ResourceFork", name0)) + return -ENOTSUP; + std::string name = name0; + auto iter = node->xattrmap.find(name); + if (node->xattrmap.end() == iter) + return -ENOATTR; + if (0 != size) + { + if (iter->second.size() > size) + return -ERANGE; + std::memcpy(value, iter->second.data(), iter->second.size()); + } + return static_cast(iter->second.size()); + } + + static int listxattr(const char *path, char *namebuf, size_t size) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + size_t copysize = 0; + for (auto elem : node->xattrmap) + { + size_t namesize = elem.first.size() + 1; + if (0 != size) + { + if (copysize + namesize > size) + return -ERANGE; + std::memcpy(namebuf + copysize, elem.first.c_str(), namesize); + copysize += namesize; + } + } + return static_cast(copysize); + } + + static int removexattr(const char *path, const char *name0) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path); + if (!node) + return -ENOENT; + if (0 == std::strcmp("com.apple.ResourceFork", name0)) + return -ENOTSUP; + std::string name = name0; + return node->xattrmap.erase(name) ? 0 : -ENOATTR; + } + + static int opendir(const char *path, struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->open_node(path, true, fi); + } + + static int readdir(const char *path, void *buf, fuse_fill_dir_t filler, fuse_off_t off, + struct fuse_file_info *fi, enum fuse_readdir_flags) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + filler(buf, ".", &node->stat, 0, FUSE_FILL_DIR_PLUS); + filler(buf, "..", nullptr, 0, FUSE_FILL_DIR_PLUS); + for (auto elem : node->childmap) + if (0 != filler(buf, elem.first.c_str(), &elem.second->stat, 0, FUSE_FILL_DIR_PLUS)) + break; + return 0; + } + + static int releasedir(const char *path, struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + return self->close_node(fi); + } + + static void *init(struct fuse_conn_info *conn, + struct fuse_config *conf) + { + conn->want |= (conn->capable & FUSE_CAP_READDIRPLUS); + return getself(); + } + + static int utimens(const char *path, const struct fuse_timespec tmsp[2], + struct fuse_file_info *fi) + { + auto self = getself(); + std::lock_guard lock(self->_mutex); + auto node = self->get_node(path, fi); + if (!node) + return -ENOENT; + if (tmsp) + { + node->stat.st_ctim = now(); + node->stat.st_atim = tmsp[0]; + node->stat.st_mtim = tmsp[1]; + } + else + node->stat.st_ctim = node->stat.st_atim = node->stat.st_mtim = now(); + return 0; + } + +#if 0 + static int ioctl(const char *path, int cmd, void *arg, struct fuse_file_info *fi, + unsigned int flags, void *data) + { + return -ENOSYS; + } +#endif + + std::tuple, std::string, std::shared_ptr> + lookup_node(const char *path, node_t *ancestor = nullptr) + { + auto prnt = _root; + std::string name; + auto node = prnt; + for (const char *part = path, *p; *part; part = p + !!(*p)) + { + for (p = part; *p && '/' != *p; p++) + ; + if (part == p) + continue; + prnt = node; + if (!node) + break; + name.assign(part, p); + auto iter = node->childmap.find(name); + node = node->childmap.end() != iter ? iter->second : nullptr; + if (ancestor && node.get() == ancestor) + { + name.assign(""); // special case loop condition + break; + } + } + return std::make_tuple(prnt, name, node); + } + + int make_node(const char *path, fuse_mode_t mode, fuse_dev_t dev, const char *data = nullptr) + { + auto lookup = lookup_node(path); + auto prnt = std::get<0>(lookup); + auto name = std::get<1>(lookup); + auto node = std::get<2>(lookup); + if (!prnt) + return -ENOENT; + if (node) + return -EEXIST; + fuse_context *context = fuse_get_context(); + node = std::make_shared(++_ino, mode, context->uid, context->gid, dev); + if (data) + { + node->resize(std::strlen(data), false); + std::memcpy(node->data.data(), data, node->data.size()); + } + prnt->childmap[name] = node; + prnt->stat.st_ctim = prnt->stat.st_mtim = node->stat.st_ctim; + return 0; + } + + int remove_node(const char *path, bool dir) + { + auto lookup = lookup_node(path); + auto prnt = std::get<0>(lookup); + auto name = std::get<1>(lookup); + auto node = std::get<2>(lookup); + if (!node) + return -ENOENT; + if (!dir && S_IFDIR == (node->stat.st_mode & S_IFMT)) + return -EISDIR; + if (dir && S_IFDIR != (node->stat.st_mode & S_IFMT)) + return -ENOTDIR; + if (0 < node->childmap.size()) + return -ENOTEMPTY; + node->stat.st_nlink--; + prnt->childmap.erase(name); + node->stat.st_ctim = prnt->stat.st_ctim = prnt->stat.st_mtim = now(); + return 0; + } + + int open_node(const char *path, bool dir, struct fuse_file_info *fi) + { + auto node = std::get<2>(lookup_node(path)); + if (!node) + return -ENOENT; + if (!dir && S_IFDIR == (node->stat.st_mode & S_IFMT)) + return -EISDIR; + if (dir && S_IFDIR != (node->stat.st_mode & S_IFMT)) + return -ENOTDIR; + // A file descriptor is a raw pointer to a shared_ptr. + // This has the effect of incrementing the shared_ptr + // refcount, thus keeping an open node around even + // if the node is unlinked. + fi->fh = (uint64_t)(uintptr_t)new std::shared_ptr(node); + return 0; + } + + int close_node(struct fuse_file_info *fi) + { + delete (std::shared_ptr *)(uintptr_t)fi->fh; + return 0; + } + + std::shared_ptr get_node(const char *path, struct fuse_file_info *fi = nullptr) + { + if (!fi) + return std::get<2>(lookup_node(path)); + else + return *(std::shared_ptr *)(uintptr_t)fi->fh; + } + +private: + std::mutex _mutex; + fuse_ino_t _ino; + std::shared_ptr _root; +}; + +int main(int argc, char *argv[]) +{ + return memfs().main(argc, argv); +} diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse3/memfs-fuse3.sln b/3rd_party/winfsp-1.10/samples/memfs-fuse3/memfs-fuse3.sln new file mode 100644 index 00000000..f7312476 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse3/memfs-fuse3.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "memfs-fuse3", "memfs-fuse3.vcxproj", "{CF538F42-C714-4653-B351-E72FD7B0B217}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x64.ActiveCfg = Debug|x64 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x64.Build.0 = Debug|x64 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x86.ActiveCfg = Debug|Win32 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Debug|x86.Build.0 = Debug|Win32 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x64.ActiveCfg = Release|x64 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x64.Build.0 = Release|x64 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x86.ActiveCfg = Release|Win32 + {CF538F42-C714-4653-B351-E72FD7B0B217}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse3/memfs-fuse3.vcxproj b/3rd_party/winfsp-1.10/samples/memfs-fuse3/memfs-fuse3.vcxproj new file mode 100644 index 00000000..21ad10a3 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse3/memfs-fuse3.vcxproj @@ -0,0 +1,189 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {CF538F42-C714-4653-B351-E72FD7B0B217} + Win32Proj + memfsfuse3 + $(LatestTargetPlatformVersion) + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse3;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreadedDebug + 4018 + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse3;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreadedDebug + 4018 + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse3;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + 4018 + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse3;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + 4018 + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + + + + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/memfs-fuse3/memfs-fuse3.vcxproj.filters b/3rd_party/winfsp-1.10/samples/memfs-fuse3/memfs-fuse3.vcxproj.filters new file mode 100644 index 00000000..dfeda1ce --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs-fuse3/memfs-fuse3.vcxproj.filters @@ -0,0 +1,19 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source + + + + + Source + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/memfs/memfs-main.c b/3rd_party/winfsp-1.10/samples/memfs/memfs-main.c new file mode 100644 index 00000000..b5b6e0d5 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs/memfs-main.c @@ -0,0 +1,241 @@ +/** + * @file memfs-main.c + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#include +#include "memfs.h" + +#define PROGNAME "memfs" + +#define info(format, ...) FspServiceLog(EVENTLOG_INFORMATION_TYPE, format, __VA_ARGS__) +#define warn(format, ...) FspServiceLog(EVENTLOG_WARNING_TYPE, format, __VA_ARGS__) +#define fail(format, ...) FspServiceLog(EVENTLOG_ERROR_TYPE, format, __VA_ARGS__) + +#define argtos(v) if (arge > ++argp) v = *argp; else goto usage +#define argtol(v) if (arge > ++argp) v = wcstol_deflt(*argp, v); else goto usage + +static ULONG wcstol_deflt(wchar_t *w, ULONG deflt) +{ + wchar_t *endp; + ULONG ul = wcstol(w, &endp, 0); + return L'\0' != w[0] && L'\0' == *endp ? ul : deflt; +} + +NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) +{ + wchar_t **argp, **arge; + ULONG DebugFlags = 0; + PWSTR DebugLogFile = 0; + ULONG Flags = MemfsDisk; + ULONG OtherFlags = 0; + ULONG FileInfoTimeout = INFINITE; + ULONG MaxFileNodes = 1024; + ULONG MaxFileSize = 16 * 1024 * 1024; + ULONG SlowioMaxDelay = 0; /* -M: maximum slow IO delay in millis */ + ULONG SlowioPercentDelay = 0; /* -P: percent of slow IO to make pending */ + ULONG SlowioRarefyDelay = 0; /* -R: adjust the rarity of pending slow IO */ + PWSTR FileSystemName = 0; + PWSTR MountPoint = 0; + PWSTR VolumePrefix = 0; + PWSTR RootSddl = 0; + HANDLE DebugLogHandle = INVALID_HANDLE_VALUE; + MEMFS *Memfs = 0; + NTSTATUS Result; + + for (argp = argv + 1, arge = argv + argc; arge > argp; argp++) + { + if (L'-' != argp[0][0]) + break; + switch (argp[0][1]) + { + case L'?': + goto usage; + case L'd': + argtol(DebugFlags); + break; + case L'D': + argtos(DebugLogFile); + break; + case L'f': + OtherFlags = MemfsFlushAndPurgeOnCleanup; + break; + case L'F': + argtos(FileSystemName); + break; + case L'i': + OtherFlags = MemfsCaseInsensitive; + break; + case L'm': + argtos(MountPoint); + break; + case L'M': + argtol(SlowioMaxDelay); + break; + case L'n': + argtol(MaxFileNodes); + break; + case L'P': + argtol(SlowioPercentDelay); + break; + case L'R': + argtol(SlowioRarefyDelay); + break; + case L'S': + argtos(RootSddl); + break; + case L's': + argtol(MaxFileSize); + break; + case L't': + argtol(FileInfoTimeout); + break; + case L'u': + argtos(VolumePrefix); + if (0 != VolumePrefix && L'\0' != VolumePrefix[0]) + Flags = MemfsNet; + break; + default: + goto usage; + } + } + + if (arge > argp) + goto usage; + + if (MemfsDisk == Flags && 0 == MountPoint) + goto usage; + + if (0 != DebugLogFile) + { + if (0 == wcscmp(L"-", DebugLogFile)) + DebugLogHandle = GetStdHandle(STD_ERROR_HANDLE); + else + DebugLogHandle = CreateFileW( + DebugLogFile, + FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0); + if (INVALID_HANDLE_VALUE == DebugLogHandle) + { + fail(L"cannot open debug log file"); + goto usage; + } + + FspDebugLogSetHandle(DebugLogHandle); + } + + Result = MemfsCreateFunnel( + Flags | OtherFlags, + FileInfoTimeout, + MaxFileNodes, + MaxFileSize, + SlowioMaxDelay, + SlowioPercentDelay, + SlowioRarefyDelay, + FileSystemName, + VolumePrefix, + RootSddl, + &Memfs); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot create MEMFS"); + goto exit; + } + + FspFileSystemSetDebugLog(MemfsFileSystem(Memfs), DebugFlags); + + if (0 != MountPoint && L'\0' != MountPoint[0]) + { + Result = FspFileSystemSetMountPoint(MemfsFileSystem(Memfs), + L'*' == MountPoint[0] && L'\0' == MountPoint[1] ? 0 : MountPoint); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot mount MEMFS"); + goto exit; + } + } + + Result = MemfsStart(Memfs); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot start MEMFS"); + goto exit; + } + + MountPoint = FspFileSystemMountPoint(MemfsFileSystem(Memfs)); + + info(L"%s -t %ld -n %ld -s %ld%s%s%s%s%s%s", + L"" PROGNAME, FileInfoTimeout, MaxFileNodes, MaxFileSize, + RootSddl ? L" -S " : L"", RootSddl ? RootSddl : L"", + 0 != VolumePrefix && L'\0' != VolumePrefix[0] ? L" -u " : L"", + 0 != VolumePrefix && L'\0' != VolumePrefix[0] ? VolumePrefix : L"", + MountPoint ? L" -m " : L"", MountPoint ? MountPoint : L""); + + Service->UserContext = Memfs; + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result) && 0 != Memfs) + MemfsDelete(Memfs); + + return Result; + +usage: + static wchar_t usage[] = L"" + "usage: %s OPTIONS\n" + "\n" + "options:\n" + " -d DebugFlags [-1: enable all debug logs]\n" + " -D DebugLogFile [file path; use - for stderr]\n" + " -i [case insensitive file system]\n" + " -f [flush and purge cache on cleanup]\n" + " -t FileInfoTimeout [millis]\n" + " -n MaxFileNodes\n" + " -s MaxFileSize [bytes]\n" + " -M MaxDelay [maximum slow IO delay in millis]\n" + " -P PercentDelay [percent of slow IO to make pending]\n" + " -R RarefyDelay [adjust the rarity of pending slow IO]\n" + " -F FileSystemName\n" + " -S RootSddl [file rights: FA, etc; NO generic rights: GA, etc.]\n" + " -u \\Server\\Share [UNC prefix (single backslash)]\n" + " -m MountPoint [X:|* (required if no UNC prefix)]\n"; + + fail(usage, L"" PROGNAME); + + return STATUS_UNSUCCESSFUL; +} + +NTSTATUS SvcStop(FSP_SERVICE *Service) +{ + MEMFS *Memfs = Service->UserContext; + + MemfsStop(Memfs); + MemfsDelete(Memfs); + + return STATUS_SUCCESS; +} + +int wmain(int argc, wchar_t **argv) +{ + return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0); +} diff --git a/3rd_party/winfsp-1.10/samples/memfs/memfs.cpp b/3rd_party/winfsp-1.10/samples/memfs/memfs.cpp new file mode 100644 index 00000000..f72534b9 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs/memfs.cpp @@ -0,0 +1,2509 @@ +/** + * @file memfs.cpp + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#undef _DEBUG +#include "memfs.h" +#include +#include +#include +#include +#include + +/* SLOWIO */ +#include + +#define MEMFS_MAX_PATH 512 +FSP_FSCTL_STATIC_ASSERT(MEMFS_MAX_PATH > MAX_PATH, + "MEMFS_MAX_PATH must be greater than MAX_PATH."); + +/* + * Define the MEMFS_STANDALONE macro when building MEMFS as a standalone file system. + * This macro should be defined in the Visual Studio project settings, Makefile, etc. + */ +//#define MEMFS_STANDALONE + +/* + * Define the MEMFS_NAME_NORMALIZATION macro to include name normalization support. + */ +#define MEMFS_NAME_NORMALIZATION + +/* + * Define the MEMFS_REPARSE_POINTS macro to include reparse points support. + */ +#define MEMFS_REPARSE_POINTS + +/* + * Define the MEMFS_NAMED_STREAMS macro to include named streams support. + */ +#define MEMFS_NAMED_STREAMS + +/* + * Define the MEMFS_DIRINFO_BY_NAME macro to include GetDirInfoByName. + */ +#define MEMFS_DIRINFO_BY_NAME + +/* + * Define the MEMFS_SLOWIO macro to include delayed I/O response support. + */ +#define MEMFS_SLOWIO + +/* + * Define the MEMFS_CONTROL macro to include DeviceControl support. + */ +#define MEMFS_CONTROL + +/* + * Define the MEMFS_EA macro to include extended attributes support. + */ +#define MEMFS_EA + +/* + * Define the MEMFS_WSL macro to include WSLinux support. + */ +#define MEMFS_WSL + +/* + * Define the MEMFS_REJECT_EARLY_IRP macro to reject IRP's sent + * to the file system prior to the dispatcher being started. + */ +#if defined(MEMFS_STANDALONE) +#define MEMFS_REJECT_EARLY_IRP +#endif + +/* + * Define the DEBUG_BUFFER_CHECK macro on Windows 8 or above. This includes + * a check for the Write buffer to ensure that it is read-only. + * + * Since ProcessBuffer support in the FSD, this is no longer a guarantee. + */ +#if !defined(NDEBUG) +//#define DEBUG_BUFFER_CHECK +#endif + +#define MEMFS_SECTOR_SIZE 512 +#define MEMFS_SECTORS_PER_ALLOCATION_UNIT 1 + +/* + * Large Heap Support + */ + +typedef struct +{ + DWORD Options; + SIZE_T InitialSize; + SIZE_T MaximumSize; + SIZE_T Alignment; +} LARGE_HEAP_INITIALIZE_PARAMS; +static INIT_ONCE LargeHeapInitOnce = INIT_ONCE_STATIC_INIT; +static HANDLE LargeHeap; +static SIZE_T LargeHeapAlignment; +static BOOL WINAPI LargeHeapInitOnceF( + PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) +{ + LARGE_HEAP_INITIALIZE_PARAMS *Params = (LARGE_HEAP_INITIALIZE_PARAMS *)Parameter; + LargeHeap = HeapCreate(Params->Options, Params->InitialSize, Params->MaximumSize); + LargeHeapAlignment = 0 != Params->Alignment ? + FSP_FSCTL_ALIGN_UP(Params->Alignment, 4096) : + 16 * 4096; + return TRUE; +} +static inline +BOOLEAN LargeHeapInitialize( + DWORD Options, + SIZE_T InitialSize, + SIZE_T MaximumSize, + SIZE_T Alignment) +{ + LARGE_HEAP_INITIALIZE_PARAMS Params; + Params.Options = Options; + Params.InitialSize = InitialSize; + Params.MaximumSize = MaximumSize; + Params.Alignment = Alignment; + InitOnceExecuteOnce(&LargeHeapInitOnce, LargeHeapInitOnceF, &Params, 0); + return 0 != LargeHeap; +} +static inline +PVOID LargeHeapAlloc(SIZE_T Size) +{ + return HeapAlloc(LargeHeap, 0, FSP_FSCTL_ALIGN_UP(Size, LargeHeapAlignment)); +} +static inline +PVOID LargeHeapRealloc(PVOID Pointer, SIZE_T Size) +{ + if (0 != Pointer) + { + if (0 != Size) + return HeapReAlloc(LargeHeap, 0, Pointer, FSP_FSCTL_ALIGN_UP(Size, LargeHeapAlignment)); + else + return HeapFree(LargeHeap, 0, Pointer), 0; + } + else + { + if (0 != Size) + return HeapAlloc(LargeHeap, 0, FSP_FSCTL_ALIGN_UP(Size, LargeHeapAlignment)); + else + return 0; + } +} +static inline +VOID LargeHeapFree(PVOID Pointer) +{ + if (0 != Pointer) + HeapFree(LargeHeap, 0, Pointer); +} + +/* + * MEMFS + */ + +static inline +UINT64 MemfsGetSystemTime(VOID) +{ + FILETIME FileTime; + GetSystemTimeAsFileTime(&FileTime); + return ((PLARGE_INTEGER)&FileTime)->QuadPart; +} + +static inline +int MemfsFileNameCompare(PWSTR a, int alen, PWSTR b, int blen, BOOLEAN CaseInsensitive) +{ + PWSTR p, endp, partp, q, endq, partq; + WCHAR c, d; + int plen, qlen, len, res; + + if (-1 == alen) + alen = lstrlenW(a); + if (-1 == blen) + blen = lstrlenW(b); + + for (p = a, endp = p + alen, q = b, endq = q + blen; endp > p && endq > q;) + { + c = d = 0; + for (; endp > p && (L':' == *p || L'\\' == *p); p++) + c = *p; + for (; endq > q && (L':' == *q || L'\\' == *q); q++) + d = *q; + + if (L':' == c) + c = 1; + else if (L'\\' == c) + c = 2; + if (L':' == d) + d = 1; + else if (L'\\' == d) + d = 2; + + res = c - d; + if (0 != res) + return res; + + for (partp = p; endp > p && L':' != *p && L'\\' != *p; p++) + ; + for (partq = q; endq > q && L':' != *q && L'\\' != *q; q++) + ; + + plen = (int)(p - partp); + qlen = (int)(q - partq); + + len = plen < qlen ? plen : qlen; + + if (CaseInsensitive) + { + res = CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, partp, plen, partq, qlen); + if (0 != res) + res -= 2; + else + res = _wcsnicmp(partp, partq, len); + } + else + res = wcsncmp(partp, partq, len); + + if (0 == res) + res = plen - qlen; + + if (0 != res) + return res; + } + + return -(endp <= p) + (endq <= q); +} + +static inline +BOOLEAN MemfsFileNameHasPrefix(PWSTR a, PWSTR b, BOOLEAN CaseInsensitive) +{ + int alen = (int)wcslen(a); + int blen = (int)wcslen(b); + + return alen >= blen && 0 == MemfsFileNameCompare(a, blen, b, blen, CaseInsensitive) && + (alen == blen || (1 == blen && L'\\' == b[0]) || +#if defined(MEMFS_NAMED_STREAMS) + (L'\\' == a[blen] || L':' == a[blen])); +#else + (L'\\' == a[blen])); +#endif +} + +#if defined(MEMFS_EA) +static inline +int MemfsEaNameCompare(PSTR a, PSTR b) +{ + /* EA names are always case-insensitive in MEMFS (to be inline with NTFS) */ + + int res; + + res = CompareStringA(LOCALE_INVARIANT, NORM_IGNORECASE, a, -1, b, -1); + if (0 != res) + res -= 2; + else + res = _stricmp(a, b); + + return res; +} + +struct MEMFS_FILE_NODE_EA_LESS +{ + MEMFS_FILE_NODE_EA_LESS() + { + } + bool operator()(PSTR a, PSTR b) const + { + return 0 > MemfsEaNameCompare(a, b); + } +}; +typedef std::map MEMFS_FILE_NODE_EA_MAP; +#endif + +typedef struct _MEMFS_FILE_NODE +{ + WCHAR FileName[MEMFS_MAX_PATH]; + FSP_FSCTL_FILE_INFO FileInfo; + SIZE_T FileSecuritySize; + PVOID FileSecurity; + PVOID FileData; +#if defined(MEMFS_REPARSE_POINTS) + SIZE_T ReparseDataSize; + PVOID ReparseData; +#endif +#if defined(MEMFS_EA) + MEMFS_FILE_NODE_EA_MAP *EaMap; +#endif + volatile LONG RefCount; +#if defined(MEMFS_NAMED_STREAMS) + struct _MEMFS_FILE_NODE *MainFileNode; +#endif +} MEMFS_FILE_NODE; + +struct MEMFS_FILE_NODE_LESS +{ + MEMFS_FILE_NODE_LESS(BOOLEAN CaseInsensitive) : CaseInsensitive(CaseInsensitive) + { + } + bool operator()(PWSTR a, PWSTR b) const + { + return 0 > MemfsFileNameCompare(a, -1, b, -1, CaseInsensitive); + } + BOOLEAN CaseInsensitive; +}; +typedef std::map MEMFS_FILE_NODE_MAP; + +typedef struct _MEMFS +{ + FSP_FILE_SYSTEM *FileSystem; + MEMFS_FILE_NODE_MAP *FileNodeMap; + ULONG MaxFileNodes; + ULONG MaxFileSize; +#ifdef MEMFS_SLOWIO + ULONG SlowioMaxDelay; + ULONG SlowioPercentDelay; + ULONG SlowioRarefyDelay; + volatile LONG SlowioThreadsRunning; +#endif + UINT16 VolumeLabelLength; + WCHAR VolumeLabel[32]; +} MEMFS; + +static inline +NTSTATUS MemfsFileNodeCreate(PWSTR FileName, MEMFS_FILE_NODE **PFileNode) +{ + static UINT64 IndexNumber = 1; + MEMFS_FILE_NODE *FileNode; + + *PFileNode = 0; + + FileNode = (MEMFS_FILE_NODE *)malloc(sizeof *FileNode); + if (0 == FileNode) + return STATUS_INSUFFICIENT_RESOURCES; + + memset(FileNode, 0, sizeof *FileNode); + wcscpy_s(FileNode->FileName, sizeof FileNode->FileName / sizeof(WCHAR), FileName); + FileNode->FileInfo.CreationTime = + FileNode->FileInfo.LastAccessTime = + FileNode->FileInfo.LastWriteTime = + FileNode->FileInfo.ChangeTime = MemfsGetSystemTime(); + FileNode->FileInfo.IndexNumber = IndexNumber++; + + *PFileNode = FileNode; + + return STATUS_SUCCESS; +} + +#if defined(MEMFS_EA) +static inline +VOID MemfsFileNodeDeleteEaMap(MEMFS_FILE_NODE *FileNode) +{ + if (0 != FileNode->EaMap) + { + for (MEMFS_FILE_NODE_EA_MAP::iterator p = FileNode->EaMap->begin(), q = FileNode->EaMap->end(); + p != q; ++p) + free(p->second); + delete FileNode->EaMap; + FileNode->EaMap = 0; + FileNode->FileInfo.EaSize = 0; + } +} +#endif + +static inline +VOID MemfsFileNodeDelete(MEMFS_FILE_NODE *FileNode) +{ +#if defined(MEMFS_EA) + MemfsFileNodeDeleteEaMap(FileNode); +#endif +#if defined(MEMFS_REPARSE_POINTS) + free(FileNode->ReparseData); +#endif + LargeHeapFree(FileNode->FileData); + free(FileNode->FileSecurity); + free(FileNode); +} + +static inline +VOID MemfsFileNodeReference(MEMFS_FILE_NODE *FileNode) +{ + InterlockedIncrement(&FileNode->RefCount); +} + +static inline +VOID MemfsFileNodeDereference(MEMFS_FILE_NODE *FileNode) +{ + if (0 == InterlockedDecrement(&FileNode->RefCount)) + MemfsFileNodeDelete(FileNode); +} + +static inline +VOID MemfsFileNodeGetFileInfo(MEMFS_FILE_NODE *FileNode, FSP_FSCTL_FILE_INFO *FileInfo) +{ +#if defined(MEMFS_NAMED_STREAMS) + if (0 == FileNode->MainFileNode) + *FileInfo = FileNode->FileInfo; + else + { + *FileInfo = FileNode->MainFileNode->FileInfo; + FileInfo->FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY; + /* named streams cannot be directories */ + FileInfo->AllocationSize = FileNode->FileInfo.AllocationSize; + FileInfo->FileSize = FileNode->FileInfo.FileSize; + } +#else + *FileInfo = FileNode->FileInfo; +#endif +} + +#if defined(MEMFS_EA) +static inline +NTSTATUS MemfsFileNodeGetEaMap(MEMFS_FILE_NODE *FileNode, MEMFS_FILE_NODE_EA_MAP **PEaMap) +{ +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; +#endif + + *PEaMap = FileNode->EaMap; + if (0 != *PEaMap) + return STATUS_SUCCESS; + + try + { + *PEaMap = FileNode->EaMap = new MEMFS_FILE_NODE_EA_MAP(MEMFS_FILE_NODE_EA_LESS()); + return STATUS_SUCCESS; + } + catch (...) + { + *PEaMap = 0; + return STATUS_INSUFFICIENT_RESOURCES; + } +} + +static inline +NTSTATUS MemfsFileNodeSetEa( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PFILE_FULL_EA_INFORMATION Ea) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)Context; + MEMFS_FILE_NODE_EA_MAP *EaMap; + FILE_FULL_EA_INFORMATION *FileNodeEa = 0; + MEMFS_FILE_NODE_EA_MAP::iterator p; + ULONG EaSizePlus = 0, EaSizeMinus = 0; + NTSTATUS Result; + + Result = MemfsFileNodeGetEaMap(FileNode, &EaMap); + if (!NT_SUCCESS(Result)) + return Result; + + if (0 != Ea->EaValueLength) + { + EaSizePlus = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + + Ea->EaNameLength + 1 + Ea->EaValueLength; + FileNodeEa = (FILE_FULL_EA_INFORMATION *)malloc(EaSizePlus); + if (0 == FileNodeEa) + return STATUS_INSUFFICIENT_RESOURCES; + memcpy(FileNodeEa, Ea, EaSizePlus); + FileNodeEa->NextEntryOffset = 0; + + EaSizePlus = FspFileSystemGetEaPackedSize(Ea); + } + + p = EaMap->find(Ea->EaName); + if (p != EaMap->end()) + { + EaSizeMinus = FspFileSystemGetEaPackedSize(Ea); + + free(p->second); + EaMap->erase(p); + } + + if (0 != Ea->EaValueLength) + { + try + { + EaMap->insert(MEMFS_FILE_NODE_EA_MAP::value_type(FileNodeEa->EaName, FileNodeEa)); + } + catch (...) + { + free(FileNodeEa); + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + FileNode->FileInfo.EaSize = FileNode->FileInfo.EaSize + EaSizePlus - EaSizeMinus; + + return STATUS_SUCCESS; +} + +static inline +BOOLEAN MemfsFileNodeNeedEa(MEMFS_FILE_NODE *FileNode) +{ +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; +#endif + + if (0 != FileNode->EaMap) + { + for (MEMFS_FILE_NODE_EA_MAP::iterator p = FileNode->EaMap->begin(), q = FileNode->EaMap->end(); + p != q; ++p) + if (0 != (p->second->Flags & FILE_NEED_EA)) + return TRUE; + } + + return FALSE; +} + +static inline +BOOLEAN MemfsFileNodeEnumerateEa(MEMFS_FILE_NODE *FileNode, + BOOLEAN (*EnumFn)(PFILE_FULL_EA_INFORMATION Ea, PVOID), PVOID Context) +{ +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; +#endif + + if (0 != FileNode->EaMap) + { + for (MEMFS_FILE_NODE_EA_MAP::iterator p = FileNode->EaMap->begin(), q = FileNode->EaMap->end(); + p != q; ++p) + if (!EnumFn(p->second, Context)) + return FALSE; + } + + return TRUE; +} +#endif + +static inline +VOID MemfsFileNodeMapDump(MEMFS_FILE_NODE_MAP *FileNodeMap) +{ + for (MEMFS_FILE_NODE_MAP::iterator p = FileNodeMap->begin(), q = FileNodeMap->end(); p != q; ++p) + FspDebugLog("%c %04lx %6lu %S\n", + FILE_ATTRIBUTE_DIRECTORY & p->second->FileInfo.FileAttributes ? 'd' : 'f', + (ULONG)p->second->FileInfo.FileAttributes, + (ULONG)p->second->FileInfo.FileSize, + p->second->FileName); +} + +static inline +BOOLEAN MemfsFileNodeMapIsCaseInsensitive(MEMFS_FILE_NODE_MAP *FileNodeMap) +{ + return FileNodeMap->key_comp().CaseInsensitive; +} + +static inline +NTSTATUS MemfsFileNodeMapCreate(BOOLEAN CaseInsensitive, MEMFS_FILE_NODE_MAP **PFileNodeMap) +{ + *PFileNodeMap = 0; + try + { + *PFileNodeMap = new MEMFS_FILE_NODE_MAP(MEMFS_FILE_NODE_LESS(CaseInsensitive)); + return STATUS_SUCCESS; + } + catch (...) + { + return STATUS_INSUFFICIENT_RESOURCES; + } +} + +static inline +VOID MemfsFileNodeMapDelete(MEMFS_FILE_NODE_MAP *FileNodeMap) +{ + for (MEMFS_FILE_NODE_MAP::iterator p = FileNodeMap->begin(), q = FileNodeMap->end(); p != q; ++p) + MemfsFileNodeDelete(p->second); + + delete FileNodeMap; +} + +static inline +SIZE_T MemfsFileNodeMapCount(MEMFS_FILE_NODE_MAP *FileNodeMap) +{ + return FileNodeMap->size(); +} + +static inline +MEMFS_FILE_NODE *MemfsFileNodeMapGet(MEMFS_FILE_NODE_MAP *FileNodeMap, PWSTR FileName) +{ + MEMFS_FILE_NODE_MAP::iterator iter = FileNodeMap->find(FileName); + if (iter == FileNodeMap->end()) + return 0; + return iter->second; +} + +#if defined(MEMFS_NAMED_STREAMS) +static inline +MEMFS_FILE_NODE *MemfsFileNodeMapGetMain(MEMFS_FILE_NODE_MAP *FileNodeMap, PWSTR FileName0) +{ + WCHAR FileName[MEMFS_MAX_PATH]; + wcscpy_s(FileName, sizeof FileName / sizeof(WCHAR), FileName0); + PWSTR StreamName = wcschr(FileName, L':'); + if (0 == StreamName) + return 0; + StreamName[0] = L'\0'; + MEMFS_FILE_NODE_MAP::iterator iter = FileNodeMap->find(FileName); + if (iter == FileNodeMap->end()) + return 0; + return iter->second; +} +#endif + +static inline +MEMFS_FILE_NODE *MemfsFileNodeMapGetParent(MEMFS_FILE_NODE_MAP *FileNodeMap, PWSTR FileName0, + PNTSTATUS PResult) +{ + WCHAR Root[2] = L"\\"; + PWSTR Remain, Suffix; + WCHAR FileName[MEMFS_MAX_PATH]; + wcscpy_s(FileName, sizeof FileName / sizeof(WCHAR), FileName0); + FspPathSuffix(FileName, &Remain, &Suffix, Root); + MEMFS_FILE_NODE_MAP::iterator iter = FileNodeMap->find(Remain); + FspPathCombine(FileName, Suffix); + if (iter == FileNodeMap->end()) + { + *PResult = STATUS_OBJECT_PATH_NOT_FOUND; + return 0; + } + if (0 == (iter->second->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + *PResult = STATUS_NOT_A_DIRECTORY; + return 0; + } + return iter->second; +} + +static inline +VOID MemfsFileNodeMapTouchParent(MEMFS_FILE_NODE_MAP *FileNodeMap, MEMFS_FILE_NODE *FileNode) +{ + NTSTATUS Result; + MEMFS_FILE_NODE *Parent; + if (L'\\' == FileNode->FileName[0] && L'\0' == FileNode->FileName[1]) + return; + Parent = MemfsFileNodeMapGetParent(FileNodeMap, FileNode->FileName, &Result); + if (0 == Parent) + return; + Parent->FileInfo.LastAccessTime = + Parent->FileInfo.LastWriteTime = + Parent->FileInfo.ChangeTime = MemfsGetSystemTime(); +} + +static inline +NTSTATUS MemfsFileNodeMapInsert(MEMFS_FILE_NODE_MAP *FileNodeMap, MEMFS_FILE_NODE *FileNode, + PBOOLEAN PInserted) +{ + *PInserted = 0; + try + { + *PInserted = FileNodeMap->insert(MEMFS_FILE_NODE_MAP::value_type(FileNode->FileName, FileNode)).second; + if (*PInserted) + { + MemfsFileNodeReference(FileNode); + MemfsFileNodeMapTouchParent(FileNodeMap, FileNode); + } + return STATUS_SUCCESS; + } + catch (...) + { + return STATUS_INSUFFICIENT_RESOURCES; + } +} + +static inline +VOID MemfsFileNodeMapRemove(MEMFS_FILE_NODE_MAP *FileNodeMap, MEMFS_FILE_NODE *FileNode) +{ + if (FileNodeMap->erase(FileNode->FileName)) + { + MemfsFileNodeMapTouchParent(FileNodeMap, FileNode); + MemfsFileNodeDereference(FileNode); + } +} + +static inline +BOOLEAN MemfsFileNodeMapHasChild(MEMFS_FILE_NODE_MAP *FileNodeMap, MEMFS_FILE_NODE *FileNode) +{ + BOOLEAN Result = FALSE; + WCHAR Root[2] = L"\\"; + PWSTR Remain, Suffix; + MEMFS_FILE_NODE_MAP::iterator iter = FileNodeMap->upper_bound(FileNode->FileName); + for (; FileNodeMap->end() != iter; ++iter) + { +#if defined(MEMFS_NAMED_STREAMS) + if (0 != wcschr(iter->second->FileName, L':')) + continue; +#endif + FspPathSuffix(iter->second->FileName, &Remain, &Suffix, Root); + Result = 0 == MemfsFileNameCompare(Remain, -1, FileNode->FileName, -1, + MemfsFileNodeMapIsCaseInsensitive(FileNodeMap)); + FspPathCombine(iter->second->FileName, Suffix); + break; + } + return Result; +} + +static inline +BOOLEAN MemfsFileNodeMapEnumerateChildren(MEMFS_FILE_NODE_MAP *FileNodeMap, MEMFS_FILE_NODE *FileNode, + PWSTR PrevFileName0, BOOLEAN (*EnumFn)(MEMFS_FILE_NODE *, PVOID), PVOID Context) +{ + WCHAR Root[2] = L"\\"; + PWSTR Remain, Suffix; + MEMFS_FILE_NODE_MAP::iterator iter; + BOOLEAN IsDirectoryChild; + if (0 != PrevFileName0) + { + WCHAR PrevFileName[MEMFS_MAX_PATH + 256]; + size_t Length0 = wcslen(FileNode->FileName); + size_t Length1 = 1 != Length0 || L'\\' != FileNode->FileName[0]; + size_t Length2 = wcslen(PrevFileName0); + assert(MEMFS_MAX_PATH + 256 > Length0 + Length1 + Length2); + memcpy(PrevFileName, FileNode->FileName, Length0 * sizeof(WCHAR)); + memcpy(PrevFileName + Length0, L"\\", Length1 * sizeof(WCHAR)); + memcpy(PrevFileName + Length0 + Length1, PrevFileName0, Length2 * sizeof(WCHAR)); + PrevFileName[Length0 + Length1 + Length2] = L'\0'; + iter = FileNodeMap->upper_bound(PrevFileName); + } + else + iter = FileNodeMap->upper_bound(FileNode->FileName); + for (; FileNodeMap->end() != iter; ++iter) + { + if (!MemfsFileNameHasPrefix(iter->second->FileName, FileNode->FileName, + MemfsFileNodeMapIsCaseInsensitive(FileNodeMap))) + break; + FspPathSuffix(iter->second->FileName, &Remain, &Suffix, Root); + IsDirectoryChild = 0 == MemfsFileNameCompare(Remain, -1, FileNode->FileName, -1, + MemfsFileNodeMapIsCaseInsensitive(FileNodeMap)); +#if defined(MEMFS_NAMED_STREAMS) + IsDirectoryChild = IsDirectoryChild && 0 == wcschr(Suffix, L':'); +#endif + FspPathCombine(iter->second->FileName, Suffix); + if (IsDirectoryChild) + { + if (!EnumFn(iter->second, Context)) + return FALSE; + } + } + return TRUE; +} + +#if defined(MEMFS_NAMED_STREAMS) +static inline +BOOLEAN MemfsFileNodeMapEnumerateNamedStreams(MEMFS_FILE_NODE_MAP *FileNodeMap, MEMFS_FILE_NODE *FileNode, + BOOLEAN (*EnumFn)(MEMFS_FILE_NODE *, PVOID), PVOID Context) +{ + MEMFS_FILE_NODE_MAP::iterator iter = FileNodeMap->upper_bound(FileNode->FileName); + for (; FileNodeMap->end() != iter; ++iter) + { + if (!MemfsFileNameHasPrefix(iter->second->FileName, FileNode->FileName, + MemfsFileNodeMapIsCaseInsensitive(FileNodeMap))) + break; + if (L':' != iter->second->FileName[wcslen(FileNode->FileName)]) + break; + if (!EnumFn(iter->second, Context)) + return FALSE; + } + return TRUE; +} +#endif + +static inline +BOOLEAN MemfsFileNodeMapEnumerateDescendants(MEMFS_FILE_NODE_MAP *FileNodeMap, MEMFS_FILE_NODE *FileNode, + BOOLEAN (*EnumFn)(MEMFS_FILE_NODE *, PVOID), PVOID Context) +{ + WCHAR Root[2] = L"\\"; + MEMFS_FILE_NODE_MAP::iterator iter = FileNodeMap->lower_bound(FileNode->FileName); + for (; FileNodeMap->end() != iter; ++iter) + { + if (!MemfsFileNameHasPrefix(iter->second->FileName, FileNode->FileName, + MemfsFileNodeMapIsCaseInsensitive(FileNodeMap))) + break; + if (!EnumFn(iter->second, Context)) + return FALSE; + } + return TRUE; +} + +typedef struct _MEMFS_FILE_NODE_MAP_ENUM_CONTEXT +{ + BOOLEAN Reference; + MEMFS_FILE_NODE **FileNodes; + ULONG Capacity, Count; +} MEMFS_FILE_NODE_MAP_ENUM_CONTEXT; + +static inline +BOOLEAN MemfsFileNodeMapEnumerateFn(MEMFS_FILE_NODE *FileNode, PVOID Context0) +{ + MEMFS_FILE_NODE_MAP_ENUM_CONTEXT *Context = (MEMFS_FILE_NODE_MAP_ENUM_CONTEXT *)Context0; + + if (Context->Capacity <= Context->Count) + { + ULONG Capacity = 0 != Context->Capacity ? Context->Capacity * 2 : 16; + PVOID P = realloc(Context->FileNodes, Capacity * sizeof Context->FileNodes[0]); + if (0 == P) + { + FspDebugLog(__FUNCTION__ ": cannot allocate memory; aborting\n"); + abort(); + } + + Context->FileNodes = (MEMFS_FILE_NODE **)P; + Context->Capacity = Capacity; + } + + Context->FileNodes[Context->Count++] = FileNode; + if (Context->Reference) + MemfsFileNodeReference(FileNode); + + return TRUE; +} + +static inline +VOID MemfsFileNodeMapEnumerateFree(MEMFS_FILE_NODE_MAP_ENUM_CONTEXT *Context) +{ + if (Context->Reference) + { + for (ULONG Index = 0; Context->Count > Index; Index++) + { + MEMFS_FILE_NODE *FileNode = Context->FileNodes[Index]; + MemfsFileNodeDereference(FileNode); + } + } + free(Context->FileNodes); +} + +#ifdef MEMFS_SLOWIO +/* + * SLOWIO + * + * This is included for two uses: + * + * 1) For testing winfsp, by allowing memfs to act more like a non-ram file system, + * with some IO taking many milliseconds, and some IO completion delayed. + * + * 2) As sample code for how to use winfsp's STATUS_PENDING capabilities. + * + */ + +static inline UINT64 Hash(UINT64 x) +{ + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ull; + x = (x ^ (x >> 27)) * 0x94d049bb133111ebull; + x = x ^ (x >> 31); + return x; +} + +static inline ULONG PseudoRandom(ULONG to) +{ + /* John Oberschelp's PRNG */ + static UINT64 spin = 0; + InterlockedIncrement(&spin); + return Hash(spin) % to; +} + +static inline BOOLEAN SlowioReturnPending(FSP_FILE_SYSTEM *FileSystem) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + if (0 == Memfs->SlowioMaxDelay) + return FALSE; + return PseudoRandom(100) < Memfs->SlowioPercentDelay; +} + +static inline VOID SlowioSnooze(FSP_FILE_SYSTEM *FileSystem) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + if (0 == Memfs->SlowioMaxDelay) + return; + ULONG millis = PseudoRandom(Memfs->SlowioMaxDelay + 1) >> PseudoRandom(Memfs->SlowioRarefyDelay + 1); + Sleep(millis); +} + +void SlowioReadThread( + FSP_FILE_SYSTEM *FileSystem, + MEMFS_FILE_NODE *FileNode, + PVOID Buffer, + UINT64 Offset, + UINT64 EndOffset, + UINT64 RequestHint) +{ + SlowioSnooze(FileSystem); + + memcpy(Buffer, (PUINT8)FileNode->FileData + Offset, (size_t)(EndOffset - Offset)); + UINT32 BytesTransferred = (ULONG)(EndOffset - Offset); + + FSP_FSCTL_TRANSACT_RSP ResponseBuf; + memset(&ResponseBuf, 0, sizeof ResponseBuf); + ResponseBuf.Size = sizeof ResponseBuf; + ResponseBuf.Kind = FspFsctlTransactReadKind; + ResponseBuf.Hint = RequestHint; // IRP that is being completed + ResponseBuf.IoStatus.Status = STATUS_SUCCESS; + ResponseBuf.IoStatus.Information = BytesTransferred; // bytes read + FspFileSystemSendResponse(FileSystem, &ResponseBuf); + + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + InterlockedDecrement(&Memfs->SlowioThreadsRunning); +} + +void SlowioWriteThread( + FSP_FILE_SYSTEM *FileSystem, + MEMFS_FILE_NODE *FileNode, + PVOID Buffer, + UINT64 Offset, + UINT64 EndOffset, + UINT64 RequestHint) +{ + SlowioSnooze(FileSystem); + + memcpy((PUINT8)FileNode->FileData + Offset, Buffer, (size_t)(EndOffset - Offset)); + UINT32 BytesTransferred = (ULONG)(EndOffset - Offset); + + FSP_FSCTL_TRANSACT_RSP ResponseBuf; + memset(&ResponseBuf, 0, sizeof ResponseBuf); + ResponseBuf.Size = sizeof ResponseBuf; + ResponseBuf.Kind = FspFsctlTransactWriteKind; + ResponseBuf.Hint = RequestHint; // IRP that is being completed + ResponseBuf.IoStatus.Status = STATUS_SUCCESS; + ResponseBuf.IoStatus.Information = BytesTransferred; // bytes written + MemfsFileNodeGetFileInfo(FileNode, &ResponseBuf.Rsp.Write.FileInfo); + FspFileSystemSendResponse(FileSystem, &ResponseBuf); + + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + InterlockedDecrement(&Memfs->SlowioThreadsRunning); +} + +void SlowioReadDirectoryThread( + FSP_FILE_SYSTEM *FileSystem, + ULONG BytesTransferred, + UINT64 RequestHint) +{ + SlowioSnooze(FileSystem); + + FSP_FSCTL_TRANSACT_RSP ResponseBuf; + memset(&ResponseBuf, 0, sizeof ResponseBuf); + ResponseBuf.Size = sizeof ResponseBuf; + ResponseBuf.Kind = FspFsctlTransactQueryDirectoryKind; + ResponseBuf.Hint = RequestHint; // IRP that is being completed + ResponseBuf.IoStatus.Status = STATUS_SUCCESS; + ResponseBuf.IoStatus.Information = BytesTransferred; // bytes of directory info read + FspFileSystemSendResponse(FileSystem, &ResponseBuf); + + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + InterlockedDecrement(&Memfs->SlowioThreadsRunning); +} +#endif + +/* + * FSP_FILE_SYSTEM_INTERFACE + */ + +#if defined(MEMFS_REPARSE_POINTS) +static NTSTATUS GetReparsePointByName( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize); +#endif + +static NTSTATUS SetFileSizeInternal(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, UINT64 NewSize, BOOLEAN SetAllocationSize); + +static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + + VolumeInfo->TotalSize = Memfs->MaxFileNodes * (UINT64)Memfs->MaxFileSize; + VolumeInfo->FreeSize = (Memfs->MaxFileNodes - MemfsFileNodeMapCount(Memfs->FileNodeMap)) * + (UINT64)Memfs->MaxFileSize; + VolumeInfo->VolumeLabelLength = Memfs->VolumeLabelLength; + memcpy(VolumeInfo->VolumeLabel, Memfs->VolumeLabel, Memfs->VolumeLabelLength); + + return STATUS_SUCCESS; +} + +static NTSTATUS SetVolumeLabel(FSP_FILE_SYSTEM *FileSystem, + PWSTR VolumeLabel, + FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + + Memfs->VolumeLabelLength = (UINT16)(wcslen(VolumeLabel) * sizeof(WCHAR)); + if (Memfs->VolumeLabelLength > sizeof Memfs->VolumeLabel) + Memfs->VolumeLabelLength = sizeof Memfs->VolumeLabel; + memcpy(Memfs->VolumeLabel, VolumeLabel, Memfs->VolumeLabelLength); + + VolumeInfo->TotalSize = Memfs->MaxFileNodes * Memfs->MaxFileSize; + VolumeInfo->FreeSize = + (Memfs->MaxFileNodes - MemfsFileNodeMapCount(Memfs->FileNodeMap)) * Memfs->MaxFileSize; + VolumeInfo->VolumeLabelLength = Memfs->VolumeLabelLength; + memcpy(VolumeInfo->VolumeLabel, Memfs->VolumeLabel, Memfs->VolumeLabelLength); + + return STATUS_SUCCESS; +} + +static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, PUINT32 PFileAttributes, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode; + NTSTATUS Result; + + FileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, FileName); + if (0 == FileNode) + { + Result = STATUS_OBJECT_NAME_NOT_FOUND; + +#if defined(MEMFS_REPARSE_POINTS) + if (FspFileSystemFindReparsePoint(FileSystem, GetReparsePointByName, 0, + FileName, PFileAttributes)) + Result = STATUS_REPARSE; + else +#endif + MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileName, &Result); + + return Result; + } + +#if defined(MEMFS_NAMED_STREAMS) + UINT32 FileAttributesMask = ~(UINT32)0; + if (0 != FileNode->MainFileNode) + { + FileAttributesMask = ~(UINT32)FILE_ATTRIBUTE_DIRECTORY; + FileNode = FileNode->MainFileNode; + } + + if (0 != PFileAttributes) + *PFileAttributes = FileNode->FileInfo.FileAttributes & FileAttributesMask; +#else + if (0 != PFileAttributes) + *PFileAttributes = FileNode->FileInfo.FileAttributes; +#endif + + if (0 != PSecurityDescriptorSize) + { + if (FileNode->FileSecuritySize > *PSecurityDescriptorSize) + { + *PSecurityDescriptorSize = FileNode->FileSecuritySize; + return STATUS_BUFFER_OVERFLOW; + } + + *PSecurityDescriptorSize = FileNode->FileSecuritySize; + if (0 != SecurityDescriptor) + memcpy(SecurityDescriptor, FileNode->FileSecurity, FileNode->FileSecuritySize); + } + + return STATUS_SUCCESS; +} + +static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, +#if defined(MEMFS_EA) || defined(MEMFS_WSL) + PVOID ExtraBuffer, ULONG ExtraLength, BOOLEAN ExtraBufferIsReparsePoint, +#endif + PVOID *PFileNode, FSP_FSCTL_FILE_INFO *FileInfo) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; +#if defined(MEMFS_NAME_NORMALIZATION) + WCHAR FileNameBuf[MEMFS_MAX_PATH]; +#endif + MEMFS_FILE_NODE *FileNode; + MEMFS_FILE_NODE *ParentNode; + NTSTATUS Result; + BOOLEAN Inserted; + + if (MEMFS_MAX_PATH <= wcslen(FileName)) + return STATUS_OBJECT_NAME_INVALID; + + if (CreateOptions & FILE_DIRECTORY_FILE) + AllocationSize = 0; + + FileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, FileName); + if (0 != FileNode) + return STATUS_OBJECT_NAME_COLLISION; + + ParentNode = MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileName, &Result); + if (0 == ParentNode) + return Result; + + if (MemfsFileNodeMapCount(Memfs->FileNodeMap) >= Memfs->MaxFileNodes) + return STATUS_CANNOT_MAKE; + + if (AllocationSize > Memfs->MaxFileSize) + return STATUS_DISK_FULL; + +#if defined(MEMFS_NAME_NORMALIZATION) + if (MemfsFileNodeMapIsCaseInsensitive(Memfs->FileNodeMap)) + { + WCHAR Root[2] = L"\\"; + PWSTR Remain, Suffix; + size_t RemainLength, BSlashLength, SuffixLength; + + FspPathSuffix(FileName, &Remain, &Suffix, Root); + assert(0 == MemfsFileNameCompare(Remain, -1, ParentNode->FileName, -1, TRUE)); + FspPathCombine(FileName, Suffix); + + RemainLength = wcslen(ParentNode->FileName); + BSlashLength = 1 < RemainLength; + SuffixLength = wcslen(Suffix); + if (MEMFS_MAX_PATH <= RemainLength + BSlashLength + SuffixLength) + return STATUS_OBJECT_NAME_INVALID; + + memcpy(FileNameBuf, ParentNode->FileName, RemainLength * sizeof(WCHAR)); + memcpy(FileNameBuf + RemainLength, L"\\", BSlashLength * sizeof(WCHAR)); + memcpy(FileNameBuf + RemainLength + BSlashLength, Suffix, (SuffixLength + 1) * sizeof(WCHAR)); + + FileName = FileNameBuf; + } +#endif + + Result = MemfsFileNodeCreate(FileName, &FileNode); + if (!NT_SUCCESS(Result)) + return Result; + +#if defined(MEMFS_NAMED_STREAMS) + FileNode->MainFileNode = MemfsFileNodeMapGetMain(Memfs->FileNodeMap, FileName); +#endif + + FileNode->FileInfo.FileAttributes = (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? + FileAttributes : FileAttributes | FILE_ATTRIBUTE_ARCHIVE; + + if (0 != SecurityDescriptor) + { + FileNode->FileSecuritySize = GetSecurityDescriptorLength(SecurityDescriptor); + FileNode->FileSecurity = (PSECURITY_DESCRIPTOR)malloc(FileNode->FileSecuritySize); + if (0 == FileNode->FileSecurity) + { + MemfsFileNodeDelete(FileNode); + return STATUS_INSUFFICIENT_RESOURCES; + } + memcpy(FileNode->FileSecurity, SecurityDescriptor, FileNode->FileSecuritySize); + } + +#if defined(MEMFS_EA) || defined(MEMFS_WSL) + if (0 != ExtraBuffer) + { +#if defined(MEMFS_EA) + if (!ExtraBufferIsReparsePoint) + { + Result = FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, + (PFILE_FULL_EA_INFORMATION)ExtraBuffer, ExtraLength); + if (!NT_SUCCESS(Result)) + { + MemfsFileNodeDelete(FileNode); + return Result; + } + } +#endif +#if defined(MEMFS_WSL) + if (ExtraBufferIsReparsePoint) + { +#if defined(MEMFS_REPARSE_POINTS) + FileNode->ReparseDataSize = ExtraLength; + FileNode->ReparseData = malloc(ExtraLength); + if (0 == FileNode->ReparseData && 0 != ExtraLength) + { + MemfsFileNodeDelete(FileNode); + return STATUS_INSUFFICIENT_RESOURCES; + } + + FileNode->FileInfo.FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; + FileNode->FileInfo.ReparseTag = *(PULONG)ExtraBuffer; + /* the first field in a reparse buffer is the reparse tag */ + memcpy(FileNode->ReparseData, ExtraBuffer, ExtraLength); +#else + MemfsFileNodeDelete(FileNode); + return STATUS_INVALID_PARAMETER; +#endif + } +#endif + } +#endif + + FileNode->FileInfo.AllocationSize = AllocationSize; + if (0 != FileNode->FileInfo.AllocationSize) + { + FileNode->FileData = LargeHeapAlloc((size_t)FileNode->FileInfo.AllocationSize); + if (0 == FileNode->FileData) + { + MemfsFileNodeDelete(FileNode); + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + Result = MemfsFileNodeMapInsert(Memfs->FileNodeMap, FileNode, &Inserted); + if (!NT_SUCCESS(Result) || !Inserted) + { + MemfsFileNodeDelete(FileNode); + if (NT_SUCCESS(Result)) + Result = STATUS_OBJECT_NAME_COLLISION; /* should not happen! */ + return Result; + } + + MemfsFileNodeReference(FileNode); + *PFileNode = FileNode; + MemfsFileNodeGetFileInfo(FileNode, FileInfo); + +#if defined(MEMFS_NAME_NORMALIZATION) + if (MemfsFileNodeMapIsCaseInsensitive(Memfs->FileNodeMap)) + { + FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = FspFileSystemGetOpenFileInfo(FileInfo); + + wcscpy_s(OpenFileInfo->NormalizedName, OpenFileInfo->NormalizedNameSize / sizeof(WCHAR), + FileNode->FileName); + OpenFileInfo->NormalizedNameSize = (UINT16)(wcslen(FileNode->FileName) * sizeof(WCHAR)); + } +#endif + + return STATUS_SUCCESS; +} + +static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + PVOID *PFileNode, FSP_FSCTL_FILE_INFO *FileInfo) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode; + NTSTATUS Result; + + if (MEMFS_MAX_PATH <= wcslen(FileName)) + return STATUS_OBJECT_NAME_INVALID; + + FileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, FileName); + if (0 == FileNode) + { + Result = STATUS_OBJECT_NAME_NOT_FOUND; + MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileName, &Result); + return Result; + } + +#if defined(MEMFS_EA) + /* if the OP specified no EA's check the need EA count, but only if accessing main stream */ + if (0 != (CreateOptions & FILE_NO_EA_KNOWLEDGE) +#if defined(MEMFS_NAMED_STREAMS) + && (0 == FileNode->MainFileNode) +#endif + ) + { + if (MemfsFileNodeNeedEa(FileNode)) + { + Result = STATUS_ACCESS_DENIED; + return Result; + } + } +#endif + + MemfsFileNodeReference(FileNode); + *PFileNode = FileNode; + MemfsFileNodeGetFileInfo(FileNode, FileInfo); + +#if defined(MEMFS_NAME_NORMALIZATION) + if (MemfsFileNodeMapIsCaseInsensitive(Memfs->FileNodeMap)) + { + FSP_FSCTL_OPEN_FILE_INFO *OpenFileInfo = FspFileSystemGetOpenFileInfo(FileInfo); + + wcscpy_s(OpenFileInfo->NormalizedName, OpenFileInfo->NormalizedNameSize / sizeof(WCHAR), + FileNode->FileName); + OpenFileInfo->NormalizedNameSize = (UINT16)(wcslen(FileNode->FileName) * sizeof(WCHAR)); + } +#endif + + return STATUS_SUCCESS; +} + +static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, +#if defined(MEMFS_EA) + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, +#endif + FSP_FSCTL_FILE_INFO *FileInfo) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + NTSTATUS Result; + +#if defined(MEMFS_NAMED_STREAMS) + MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { TRUE }; + ULONG Index; + + MemfsFileNodeMapEnumerateNamedStreams(Memfs->FileNodeMap, FileNode, + MemfsFileNodeMapEnumerateFn, &Context); + for (Index = 0; Context.Count > Index; Index++) + { + LONG RefCount = Context.FileNodes[Index]->RefCount; + MemoryBarrier(); + if (2 >= RefCount) + MemfsFileNodeMapRemove(Memfs->FileNodeMap, Context.FileNodes[Index]); + } + MemfsFileNodeMapEnumerateFree(&Context); +#endif + +#if defined(MEMFS_EA) + MemfsFileNodeDeleteEaMap(FileNode); + if (0 != Ea) + { + Result = FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, Ea, EaLength); + if (!NT_SUCCESS(Result)) + return Result; + } +#endif + + Result = SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE); + if (!NT_SUCCESS(Result)) + return Result; + + if (ReplaceFileAttributes) + FileNode->FileInfo.FileAttributes = FileAttributes | FILE_ATTRIBUTE_ARCHIVE; + else + FileNode->FileInfo.FileAttributes |= FileAttributes | FILE_ATTRIBUTE_ARCHIVE; + + FileNode->FileInfo.FileSize = 0; + FileNode->FileInfo.LastAccessTime = + FileNode->FileInfo.LastWriteTime = + FileNode->FileInfo.ChangeTime = MemfsGetSystemTime(); + + MemfsFileNodeGetFileInfo(FileNode, FileInfo); + + return STATUS_SUCCESS; +} + +static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, PWSTR FileName, ULONG Flags) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; +#if defined(MEMFS_NAMED_STREAMS) + MEMFS_FILE_NODE *MainFileNode = 0 != FileNode->MainFileNode ? + FileNode->MainFileNode : FileNode; +#else + MEMFS_FILE_NODE *MainFileNode = FileNode; +#endif + + assert(0 != Flags); /* FSP_FSCTL_VOLUME_PARAMS::PostCleanupWhenModifiedOnly ensures this */ + + if (Flags & FspCleanupSetArchiveBit) + { + if (0 == (MainFileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + MainFileNode->FileInfo.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; + } + + if (Flags & (FspCleanupSetLastAccessTime | FspCleanupSetLastWriteTime | FspCleanupSetChangeTime)) + { + UINT64 SystemTime = MemfsGetSystemTime(); + + if (Flags & FspCleanupSetLastAccessTime) + MainFileNode->FileInfo.LastAccessTime = SystemTime; + if (Flags & FspCleanupSetLastWriteTime) + MainFileNode->FileInfo.LastWriteTime = SystemTime; + if (Flags & FspCleanupSetChangeTime) + MainFileNode->FileInfo.ChangeTime = SystemTime; + } + + if (Flags & FspCleanupSetAllocationSize) + { + UINT64 AllocationUnit = MEMFS_SECTOR_SIZE * MEMFS_SECTORS_PER_ALLOCATION_UNIT; + UINT64 AllocationSize = (FileNode->FileInfo.FileSize + AllocationUnit - 1) / + AllocationUnit * AllocationUnit; + + SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE); + } + + if ((Flags & FspCleanupDelete) && !MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode)) + { +#if defined(MEMFS_NAMED_STREAMS) + MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { FALSE }; + ULONG Index; + + MemfsFileNodeMapEnumerateNamedStreams(Memfs->FileNodeMap, FileNode, + MemfsFileNodeMapEnumerateFn, &Context); + for (Index = 0; Context.Count > Index; Index++) + MemfsFileNodeMapRemove(Memfs->FileNodeMap, Context.FileNodes[Index]); + MemfsFileNodeMapEnumerateFree(&Context); +#endif + + MemfsFileNodeMapRemove(Memfs->FileNodeMap, FileNode); + } +} + +static VOID Close(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + + MemfsFileNodeDereference(FileNode); +} + +static NTSTATUS Read(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, PVOID Buffer, UINT64 Offset, ULONG Length, + PULONG PBytesTransferred) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + UINT64 EndOffset; + + if (Offset >= FileNode->FileInfo.FileSize) + return STATUS_END_OF_FILE; + + EndOffset = Offset + Length; + if (EndOffset > FileNode->FileInfo.FileSize) + EndOffset = FileNode->FileInfo.FileSize; + +#ifdef MEMFS_SLOWIO + if (SlowioReturnPending(FileSystem)) + { + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + try + { + InterlockedIncrement(&Memfs->SlowioThreadsRunning); + std::thread(SlowioReadThread, + FileSystem, FileNode, Buffer, Offset, EndOffset, + FspFileSystemGetOperationContext()->Request->Hint). + detach(); + return STATUS_PENDING; + } + catch (...) + { + InterlockedDecrement(&Memfs->SlowioThreadsRunning); + } + } + SlowioSnooze(FileSystem); +#endif + + memcpy(Buffer, (PUINT8)FileNode->FileData + Offset, (size_t)(EndOffset - Offset)); + + *PBytesTransferred = (ULONG)(EndOffset - Offset); + + return STATUS_SUCCESS; +} + +static NTSTATUS Write(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, PVOID Buffer, UINT64 Offset, ULONG Length, + BOOLEAN WriteToEndOfFile, BOOLEAN ConstrainedIo, + PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO *FileInfo) +{ +#if defined(DEBUG_BUFFER_CHECK) + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + for (PUINT8 P = (PUINT8)Buffer, EndP = P + Length; EndP > P; P += SystemInfo.dwPageSize) + __try + { + *P = *P | 0; + assert(!IsWindows8OrGreater()); + /* only on Windows 8 we can make the buffer read-only! */ + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + /* ignore! */ + } +#endif + + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + UINT64 EndOffset; + NTSTATUS Result; + + if (ConstrainedIo) + { + if (Offset >= FileNode->FileInfo.FileSize) + return STATUS_SUCCESS; + EndOffset = Offset + Length; + if (EndOffset > FileNode->FileInfo.FileSize) + EndOffset = FileNode->FileInfo.FileSize; + } + else + { + if (WriteToEndOfFile) + Offset = FileNode->FileInfo.FileSize; + EndOffset = Offset + Length; + if (EndOffset > FileNode->FileInfo.FileSize) + { + Result = SetFileSizeInternal(FileSystem, FileNode, EndOffset, FALSE); + if (!NT_SUCCESS(Result)) + return Result; + } + } + +#ifdef MEMFS_SLOWIO + if (SlowioReturnPending(FileSystem)) + { + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + try + { + InterlockedIncrement(&Memfs->SlowioThreadsRunning); + std::thread(SlowioWriteThread, + FileSystem, FileNode, Buffer, Offset, EndOffset, + FspFileSystemGetOperationContext()->Request->Hint). + detach(); + return STATUS_PENDING; + } + catch (...) + { + InterlockedDecrement(&Memfs->SlowioThreadsRunning); + } + } + SlowioSnooze(FileSystem); +#endif + + memcpy((PUINT8)FileNode->FileData + Offset, Buffer, (size_t)(EndOffset - Offset)); + + *PBytesTransferred = (ULONG)(EndOffset - Offset); + MemfsFileNodeGetFileInfo(FileNode, FileInfo); + + return STATUS_SUCCESS; +} + +NTSTATUS Flush(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + + /* nothing to flush, since we do not cache anything */ + + if (0 != FileNode) + { +#if 0 +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode->MainFileNode->FileInfo.LastAccessTime = + FileNode->MainFileNode->FileInfo.LastWriteTime = + FileNode->MainFileNode->FileInfo.ChangeTime = MemfsGetSystemTime(); + else +#endif + FileNode->FileInfo.LastAccessTime = + FileNode->FileInfo.LastWriteTime = + FileNode->FileInfo.ChangeTime = MemfsGetSystemTime(); +#endif + + MemfsFileNodeGetFileInfo(FileNode, FileInfo); + } + + return STATUS_SUCCESS; +} + +static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + + MemfsFileNodeGetFileInfo(FileNode, FileInfo); + + return STATUS_SUCCESS; +} + +static NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, UINT32 FileAttributes, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; +#endif + + if (INVALID_FILE_ATTRIBUTES != FileAttributes) + FileNode->FileInfo.FileAttributes = FileAttributes; + if (0 != CreationTime) + FileNode->FileInfo.CreationTime = CreationTime; + if (0 != LastAccessTime) + FileNode->FileInfo.LastAccessTime = LastAccessTime; + if (0 != LastWriteTime) + FileNode->FileInfo.LastWriteTime = LastWriteTime; + if (0 != ChangeTime) + FileNode->FileInfo.ChangeTime = ChangeTime; + + MemfsFileNodeGetFileInfo(FileNode, FileInfo); + + return STATUS_SUCCESS; +} + +static NTSTATUS SetFileSizeInternal(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, UINT64 NewSize, BOOLEAN SetAllocationSize) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + + if (SetAllocationSize) + { + if (FileNode->FileInfo.AllocationSize != NewSize) + { + if (NewSize > Memfs->MaxFileSize) + return STATUS_DISK_FULL; + + PVOID FileData = LargeHeapRealloc(FileNode->FileData, (size_t)NewSize); + if (0 == FileData && 0 != NewSize) + return STATUS_INSUFFICIENT_RESOURCES; + + FileNode->FileData = FileData; + + FileNode->FileInfo.AllocationSize = NewSize; + if (FileNode->FileInfo.FileSize > NewSize) + FileNode->FileInfo.FileSize = NewSize; + } + } + else + { + if (FileNode->FileInfo.FileSize != NewSize) + { + if (FileNode->FileInfo.AllocationSize < NewSize) + { + UINT64 AllocationUnit = MEMFS_SECTOR_SIZE * MEMFS_SECTORS_PER_ALLOCATION_UNIT; + UINT64 AllocationSize = (NewSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit; + + NTSTATUS Result = SetFileSizeInternal(FileSystem, FileNode, AllocationSize, TRUE); + if (!NT_SUCCESS(Result)) + return Result; + } + + if (FileNode->FileInfo.FileSize < NewSize) + memset((PUINT8)FileNode->FileData + FileNode->FileInfo.FileSize, 0, + (size_t)(NewSize - FileNode->FileInfo.FileSize)); + FileNode->FileInfo.FileSize = NewSize; + } + } + + return STATUS_SUCCESS; +} + +static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, UINT64 NewSize, BOOLEAN SetAllocationSize, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + NTSTATUS Result; + + Result = SetFileSizeInternal(FileSystem, FileNode0, NewSize, SetAllocationSize); + if (!NT_SUCCESS(Result)) + return Result; + + MemfsFileNodeGetFileInfo(FileNode, FileInfo); + + return STATUS_SUCCESS; +} + +static NTSTATUS CanDelete(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, PWSTR FileName) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + + if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode)) + return STATUS_DIRECTORY_NOT_EMPTY; + + return STATUS_SUCCESS; +} + +static NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, + PWSTR FileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + MEMFS_FILE_NODE *NewFileNode, *DescendantFileNode; + MEMFS_FILE_NODE_MAP_ENUM_CONTEXT Context = { TRUE }; + ULONG Index, FileNameLen, NewFileNameLen; + BOOLEAN Inserted; + NTSTATUS Result; + + NewFileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, NewFileName); + if (0 != NewFileNode && FileNode != NewFileNode) + { + if (!ReplaceIfExists) + { + Result = STATUS_OBJECT_NAME_COLLISION; + goto exit; + } + + if (NewFileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + Result = STATUS_ACCESS_DENIED; + goto exit; + } + } + + MemfsFileNodeMapEnumerateDescendants(Memfs->FileNodeMap, FileNode, + MemfsFileNodeMapEnumerateFn, &Context); + + FileNameLen = (ULONG)wcslen(FileNode->FileName); + NewFileNameLen = (ULONG)wcslen(NewFileName); + for (Index = 0; Context.Count > Index; Index++) + { + DescendantFileNode = Context.FileNodes[Index]; + if (MEMFS_MAX_PATH <= wcslen(DescendantFileNode->FileName) - FileNameLen + NewFileNameLen) + { + Result = STATUS_OBJECT_NAME_INVALID; + goto exit; + } + } + + if (0 != NewFileNode) + { + MemfsFileNodeReference(NewFileNode); + MemfsFileNodeMapRemove(Memfs->FileNodeMap, NewFileNode); + MemfsFileNodeDereference(NewFileNode); + } + + for (Index = 0; Context.Count > Index; Index++) + { + DescendantFileNode = Context.FileNodes[Index]; + MemfsFileNodeMapRemove(Memfs->FileNodeMap, DescendantFileNode); + memmove(DescendantFileNode->FileName + NewFileNameLen, + DescendantFileNode->FileName + FileNameLen, + (wcslen(DescendantFileNode->FileName) + 1 - FileNameLen) * sizeof(WCHAR)); + memcpy(DescendantFileNode->FileName, NewFileName, NewFileNameLen * sizeof(WCHAR)); + Result = MemfsFileNodeMapInsert(Memfs->FileNodeMap, DescendantFileNode, &Inserted); + if (!NT_SUCCESS(Result)) + { + FspDebugLog(__FUNCTION__ ": cannot insert into FileNodeMap; aborting\n"); + abort(); + } + assert(Inserted); + } + + Result = STATUS_SUCCESS; + +exit: + MemfsFileNodeMapEnumerateFree(&Context); + + return Result; +} + +static NTSTATUS GetSecurity(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; +#endif + + if (FileNode->FileSecuritySize > *PSecurityDescriptorSize) + { + *PSecurityDescriptorSize = FileNode->FileSecuritySize; + return STATUS_BUFFER_OVERFLOW; + } + + *PSecurityDescriptorSize = FileNode->FileSecuritySize; + if (0 != SecurityDescriptor) + memcpy(SecurityDescriptor, FileNode->FileSecurity, FileNode->FileSecuritySize); + + return STATUS_SUCCESS; +} + +static NTSTATUS SetSecurity(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, + SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ModificationDescriptor) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + PSECURITY_DESCRIPTOR NewSecurityDescriptor, FileSecurity; + SIZE_T FileSecuritySize; + NTSTATUS Result; + +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; +#endif + + Result = FspSetSecurityDescriptor( + FileNode->FileSecurity, + SecurityInformation, + ModificationDescriptor, + &NewSecurityDescriptor); + if (!NT_SUCCESS(Result)) + return Result; + + FileSecuritySize = GetSecurityDescriptorLength(NewSecurityDescriptor); + FileSecurity = (PSECURITY_DESCRIPTOR)malloc(FileSecuritySize); + if (0 == FileSecurity) + { + FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor); + return STATUS_INSUFFICIENT_RESOURCES; + } + memcpy(FileSecurity, NewSecurityDescriptor, FileSecuritySize); + FspDeleteSecurityDescriptor(NewSecurityDescriptor, (NTSTATUS (*)())FspSetSecurityDescriptor); + + free(FileNode->FileSecurity); + FileNode->FileSecuritySize = FileSecuritySize; + FileNode->FileSecurity = FileSecurity; + + return STATUS_SUCCESS; +} + +typedef struct _MEMFS_READ_DIRECTORY_CONTEXT +{ + PVOID Buffer; + ULONG Length; + PULONG PBytesTransferred; +} MEMFS_READ_DIRECTORY_CONTEXT; + +static BOOLEAN AddDirInfo(MEMFS_FILE_NODE *FileNode, PWSTR FileName, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + UINT8 DirInfoBuf[sizeof(FSP_FSCTL_DIR_INFO) + sizeof FileNode->FileName]; + FSP_FSCTL_DIR_INFO *DirInfo = (FSP_FSCTL_DIR_INFO *)DirInfoBuf; + WCHAR Root[2] = L"\\"; + PWSTR Remain, Suffix; + + if (0 == FileName) + { + FspPathSuffix(FileNode->FileName, &Remain, &Suffix, Root); + FileName = Suffix; + FspPathCombine(FileNode->FileName, Suffix); + } + + memset(DirInfo->Padding, 0, sizeof DirInfo->Padding); + DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + wcslen(FileName) * sizeof(WCHAR)); + DirInfo->FileInfo = FileNode->FileInfo; + memcpy(DirInfo->FileNameBuf, FileName, DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO)); + + return FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred); +} + +static BOOLEAN ReadDirectoryEnumFn(MEMFS_FILE_NODE *FileNode, PVOID Context0) +{ + MEMFS_READ_DIRECTORY_CONTEXT *Context = (MEMFS_READ_DIRECTORY_CONTEXT *)Context0; + + return AddDirInfo(FileNode, 0, + Context->Buffer, Context->Length, Context->PBytesTransferred); +} + +static NTSTATUS ReadDirectory(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, PWSTR Pattern, PWSTR Marker, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + assert(0 == Pattern); + + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + MEMFS_FILE_NODE *ParentNode; + MEMFS_READ_DIRECTORY_CONTEXT Context; + NTSTATUS Result; + + Context.Buffer = Buffer; + Context.Length = Length; + Context.PBytesTransferred = PBytesTransferred; + + if (L'\0' != FileNode->FileName[1]) + { + /* if this is not the root directory add the dot entries */ + + ParentNode = MemfsFileNodeMapGetParent(Memfs->FileNodeMap, FileNode->FileName, &Result); + if (0 == ParentNode) + return Result; + + if (0 == Marker) + { + if (!AddDirInfo(FileNode, L".", Buffer, Length, PBytesTransferred)) + return STATUS_SUCCESS; + } + if (0 == Marker || (L'.' == Marker[0] && L'\0' == Marker[1])) + { + if (!AddDirInfo(ParentNode, L"..", Buffer, Length, PBytesTransferred)) + return STATUS_SUCCESS; + Marker = 0; + } + } + + if (MemfsFileNodeMapEnumerateChildren(Memfs->FileNodeMap, FileNode, Marker, + ReadDirectoryEnumFn, &Context)) + FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred); + +#ifdef MEMFS_SLOWIO + if (SlowioReturnPending(FileSystem)) + { + try + { + InterlockedIncrement(&Memfs->SlowioThreadsRunning); + std::thread(SlowioReadDirectoryThread, + FileSystem, *PBytesTransferred, + FspFileSystemGetOperationContext()->Request->Hint). + detach(); + return STATUS_PENDING; + } + catch (...) + { + InterlockedDecrement(&Memfs->SlowioThreadsRunning); + } + } + SlowioSnooze(FileSystem); +#endif + + return STATUS_SUCCESS; +} + +#if defined(MEMFS_DIRINFO_BY_NAME) +static NTSTATUS GetDirInfoByName(FSP_FILE_SYSTEM *FileSystem, + PVOID ParentNode0, PWSTR FileName, + FSP_FSCTL_DIR_INFO *DirInfo) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *ParentNode = (MEMFS_FILE_NODE *)ParentNode0; + MEMFS_FILE_NODE *FileNode; + WCHAR FileNameBuf[MEMFS_MAX_PATH]; + size_t ParentLength, BSlashLength, FileNameLength; + WCHAR Root[2] = L"\\"; + PWSTR Remain, Suffix; + + ParentLength = wcslen(ParentNode->FileName); + BSlashLength = 1 < ParentLength; + FileNameLength = wcslen(FileName); + if (MEMFS_MAX_PATH <= ParentLength + BSlashLength + FileNameLength) + return STATUS_OBJECT_NAME_NOT_FOUND; //STATUS_OBJECT_NAME_INVALID? + + memcpy(FileNameBuf, ParentNode->FileName, ParentLength * sizeof(WCHAR)); + memcpy(FileNameBuf + ParentLength, L"\\", BSlashLength * sizeof(WCHAR)); + memcpy(FileNameBuf + ParentLength + BSlashLength, FileName, (FileNameLength + 1) * sizeof(WCHAR)); + + FileName = FileNameBuf; + + FileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, FileName); + if (0 == FileNode) + return STATUS_OBJECT_NAME_NOT_FOUND; + + FspPathSuffix(FileNode->FileName, &Remain, &Suffix, Root); + FileName = Suffix; + FspPathCombine(FileNode->FileName, Suffix); + + //memset(DirInfo->Padding, 0, sizeof DirInfo->Padding); + DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + wcslen(FileName) * sizeof(WCHAR)); + DirInfo->FileInfo = FileNode->FileInfo; + memcpy(DirInfo->FileNameBuf, FileName, DirInfo->Size - sizeof(FSP_FSCTL_DIR_INFO)); + + return STATUS_SUCCESS; +} +#endif + +#if defined(MEMFS_REPARSE_POINTS) +static NTSTATUS ResolveReparsePoints(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 ReparsePointIndex, BOOLEAN ResolveLastPathComponent, + PIO_STATUS_BLOCK PIoStatus, PVOID Buffer, PSIZE_T PSize) +{ + return FspFileSystemResolveReparsePoints(FileSystem, GetReparsePointByName, 0, + FileName, ReparsePointIndex, ResolveLastPathComponent, + PIoStatus, Buffer, PSize); +} + +static NTSTATUS GetReparsePointByName( + FSP_FILE_SYSTEM *FileSystem, PVOID Context, + PWSTR FileName, BOOLEAN IsDirectory, PVOID Buffer, PSIZE_T PSize) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode; + +#if defined(MEMFS_NAMED_STREAMS) + /* GetReparsePointByName will never receive a named stream */ + assert(0 == wcschr(FileName, L':')); +#endif + + FileNode = MemfsFileNodeMapGet(Memfs->FileNodeMap, FileName); + if (0 == FileNode) + return STATUS_OBJECT_NAME_NOT_FOUND; + + if (0 == (FileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + return STATUS_NOT_A_REPARSE_POINT; + + if (0 != Buffer) + { + if (FileNode->ReparseDataSize > *PSize) + return STATUS_BUFFER_TOO_SMALL; + + *PSize = FileNode->ReparseDataSize; + memcpy(Buffer, FileNode->ReparseData, FileNode->ReparseDataSize); + } + + return STATUS_SUCCESS; +} + +static NTSTATUS GetReparsePoint(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, + PWSTR FileName, PVOID Buffer, PSIZE_T PSize) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; +#endif + + if (0 == (FileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) + return STATUS_NOT_A_REPARSE_POINT; + + if (FileNode->ReparseDataSize > *PSize) + return STATUS_BUFFER_TOO_SMALL; + + *PSize = FileNode->ReparseDataSize; + memcpy(Buffer, FileNode->ReparseData, FileNode->ReparseDataSize); + + return STATUS_SUCCESS; +} + +static NTSTATUS SetReparsePoint(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, + PWSTR FileName, PVOID Buffer, SIZE_T Size) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + PVOID ReparseData; + NTSTATUS Result; + +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; +#endif + + if (MemfsFileNodeMapHasChild(Memfs->FileNodeMap, FileNode)) + return STATUS_DIRECTORY_NOT_EMPTY; + + if (0 != FileNode->ReparseData) + { + Result = FspFileSystemCanReplaceReparsePoint( + FileNode->ReparseData, FileNode->ReparseDataSize, + Buffer, Size); + if (!NT_SUCCESS(Result)) + return Result; + } + + ReparseData = realloc(FileNode->ReparseData, Size); + if (0 == ReparseData && 0 != Size) + return STATUS_INSUFFICIENT_RESOURCES; + + FileNode->FileInfo.FileAttributes |= FILE_ATTRIBUTE_REPARSE_POINT; + FileNode->FileInfo.ReparseTag = *(PULONG)Buffer; + /* the first field in a reparse buffer is the reparse tag */ + FileNode->ReparseDataSize = Size; + FileNode->ReparseData = ReparseData; + memcpy(FileNode->ReparseData, Buffer, Size); + + return STATUS_SUCCESS; +} + +static NTSTATUS DeleteReparsePoint(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, + PWSTR FileName, PVOID Buffer, SIZE_T Size) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + NTSTATUS Result; + +#if defined(MEMFS_NAMED_STREAMS) + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; +#endif + + if (0 != FileNode->ReparseData) + { + Result = FspFileSystemCanReplaceReparsePoint( + FileNode->ReparseData, FileNode->ReparseDataSize, + Buffer, Size); + if (!NT_SUCCESS(Result)) + return Result; + } + else + return STATUS_NOT_A_REPARSE_POINT; + + free(FileNode->ReparseData); + + FileNode->FileInfo.FileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT; + FileNode->FileInfo.ReparseTag = 0; + FileNode->ReparseDataSize = 0; + FileNode->ReparseData = 0; + + return STATUS_SUCCESS; +} +#endif + +#if defined(MEMFS_NAMED_STREAMS) +typedef struct _MEMFS_GET_STREAM_INFO_CONTEXT +{ + PVOID Buffer; + ULONG Length; + PULONG PBytesTransferred; +} MEMFS_GET_STREAM_INFO_CONTEXT; + +static BOOLEAN AddStreamInfo(MEMFS_FILE_NODE *FileNode, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + UINT8 StreamInfoBuf[sizeof(FSP_FSCTL_STREAM_INFO) + sizeof FileNode->FileName]; + FSP_FSCTL_STREAM_INFO *StreamInfo = (FSP_FSCTL_STREAM_INFO *)StreamInfoBuf; + PWSTR StreamName; + + StreamName = wcschr(FileNode->FileName, L':'); + if (0 != StreamName) + StreamName++; + else + StreamName = L""; + + StreamInfo->Size = (UINT16)(sizeof(FSP_FSCTL_STREAM_INFO) + wcslen(StreamName) * sizeof(WCHAR)); + StreamInfo->StreamSize = FileNode->FileInfo.FileSize; + StreamInfo->StreamAllocationSize = FileNode->FileInfo.AllocationSize; + memcpy(StreamInfo->StreamNameBuf, StreamName, StreamInfo->Size - sizeof(FSP_FSCTL_STREAM_INFO)); + + return FspFileSystemAddStreamInfo(StreamInfo, Buffer, Length, PBytesTransferred); +} + +static BOOLEAN GetStreamInfoEnumFn(MEMFS_FILE_NODE *FileNode, PVOID Context0) +{ + MEMFS_GET_STREAM_INFO_CONTEXT *Context = (MEMFS_GET_STREAM_INFO_CONTEXT *)Context0; + + return AddStreamInfo(FileNode, + Context->Buffer, Context->Length, Context->PBytesTransferred); +} + +static NTSTATUS GetStreamInfo(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, PVOID Buffer, ULONG Length, + PULONG PBytesTransferred) +{ + MEMFS *Memfs = (MEMFS *)FileSystem->UserContext; + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + MEMFS_GET_STREAM_INFO_CONTEXT Context; + + if (0 != FileNode->MainFileNode) + FileNode = FileNode->MainFileNode; + + Context.Buffer = Buffer; + Context.Length = Length; + Context.PBytesTransferred = PBytesTransferred; + + if (0 == (FileNode->FileInfo.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + !AddStreamInfo(FileNode, Buffer, Length, PBytesTransferred)) + return STATUS_SUCCESS; + + if (MemfsFileNodeMapEnumerateNamedStreams(Memfs->FileNodeMap, FileNode, GetStreamInfoEnumFn, &Context)) + FspFileSystemAddStreamInfo(0, Buffer, Length, PBytesTransferred); + + /* ???: how to handle out of response buffer condition? */ + + return STATUS_SUCCESS; +} +#endif + +#if defined(MEMFS_CONTROL) +static NTSTATUS Control(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode, UINT32 ControlCode, + PVOID InputBuffer, ULONG InputBufferLength, + PVOID OutputBuffer, ULONG OutputBufferLength, PULONG PBytesTransferred) +{ + /* MEMFS also supports encryption! See below :) */ + if (CTL_CODE(0x8000 + 'M', 'R', METHOD_BUFFERED, FILE_ANY_ACCESS) == ControlCode) + { + if (OutputBufferLength != InputBufferLength) + return STATUS_INVALID_PARAMETER; + + for (PUINT8 P = (PUINT8)InputBuffer, Q = (PUINT8)OutputBuffer, EndP = P + InputBufferLength; + EndP > P; P++, Q++) + { + if (('A' <= *P && *P <= 'M') || ('a' <= *P && *P <= 'm')) + *Q = *P + 13; + else + if (('N' <= *P && *P <= 'Z') || ('n' <= *P && *P <= 'z')) + *Q = *P - 13; + else + *Q = *P; + } + + *PBytesTransferred = InputBufferLength; + return STATUS_SUCCESS; + } + + return STATUS_INVALID_DEVICE_REQUEST; +} +#endif + +#if defined(MEMFS_EA) +typedef struct _MEMFS_GET_EA_CONTEXT +{ + PFILE_FULL_EA_INFORMATION Ea; + ULONG EaLength; + PULONG PBytesTransferred; +} MEMFS_GET_EA_CONTEXT; + +static BOOLEAN GetEaEnumFn(PFILE_FULL_EA_INFORMATION Ea, PVOID Context0) +{ + MEMFS_GET_EA_CONTEXT *Context = (MEMFS_GET_EA_CONTEXT *)Context0; + + return FspFileSystemAddEa(Ea, + Context->Ea, Context->EaLength, Context->PBytesTransferred); +} + +static NTSTATUS GetEa(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, PULONG PBytesTransferred) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + MEMFS_GET_EA_CONTEXT Context; + + Context.Ea = Ea; + Context.EaLength = EaLength; + Context.PBytesTransferred = PBytesTransferred; + + if (MemfsFileNodeEnumerateEa(FileNode, GetEaEnumFn, &Context)) + FspFileSystemAddEa(0, Ea, EaLength, PBytesTransferred); + + return STATUS_SUCCESS; +} + +static NTSTATUS SetEa(FSP_FILE_SYSTEM *FileSystem, + PVOID FileNode0, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + MEMFS_FILE_NODE *FileNode = (MEMFS_FILE_NODE *)FileNode0; + NTSTATUS Result; + + Result = FspFileSystemEnumerateEa(FileSystem, MemfsFileNodeSetEa, FileNode, Ea, EaLength); + if (!NT_SUCCESS(Result)) + return Result; + + MemfsFileNodeGetFileInfo(FileNode, FileInfo); + + return STATUS_SUCCESS; +} +#endif + +static FSP_FILE_SYSTEM_INTERFACE MemfsInterface = +{ + GetVolumeInfo, + SetVolumeLabel, + GetSecurityByName, +#if defined(MEMFS_EA) || defined(MEMFS_WSL) + 0, +#else + Create, +#endif + Open, +#if defined(MEMFS_EA) + 0, +#else + Overwrite, +#endif + Cleanup, + Close, + Read, + Write, + Flush, + GetFileInfo, + SetBasicInfo, + SetFileSize, + CanDelete, + Rename, + GetSecurity, + SetSecurity, + ReadDirectory, +#if defined(MEMFS_REPARSE_POINTS) + ResolveReparsePoints, + GetReparsePoint, + SetReparsePoint, + DeleteReparsePoint, +#else + 0, + 0, + 0, + 0, +#endif +#if defined(MEMFS_NAMED_STREAMS) + GetStreamInfo, +#else + 0, +#endif +#if defined(MEMFS_DIRINFO_BY_NAME) + GetDirInfoByName, +#else + 0, +#endif +#if defined(MEMFS_CONTROL) + Control, +#else + 0, +#endif + 0, +#if defined(MEMFS_EA) || defined(MEMFS_WSL) + Create, +#endif +#if defined(MEMFS_EA) + Overwrite, + GetEa, + SetEa, +#else + 0, + 0, + 0, +#endif +}; + +/* + * Public API + */ + +NTSTATUS MemfsCreateFunnel( + ULONG Flags, + ULONG FileInfoTimeout, + ULONG MaxFileNodes, + ULONG MaxFileSize, + ULONG SlowioMaxDelay, + ULONG SlowioPercentDelay, + ULONG SlowioRarefyDelay, + PWSTR FileSystemName, + PWSTR VolumePrefix, + PWSTR RootSddl, + MEMFS **PMemfs) +{ + NTSTATUS Result; + FSP_FSCTL_VOLUME_PARAMS VolumeParams; + BOOLEAN CaseInsensitive = !!(Flags & MemfsCaseInsensitive); + BOOLEAN FlushAndPurgeOnCleanup = !!(Flags & MemfsFlushAndPurgeOnCleanup); + BOOLEAN SupportsPosixUnlinkRename = !(Flags & MemfsLegacyUnlinkRename); + PWSTR DevicePath = MemfsNet == (Flags & MemfsDeviceMask) ? + L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME; + UINT64 AllocationUnit; + MEMFS *Memfs; + MEMFS_FILE_NODE *RootNode; + PSECURITY_DESCRIPTOR RootSecurity; + ULONG RootSecuritySize; + BOOLEAN Inserted; + + *PMemfs = 0; + + Result = MemfsHeapConfigure(0, 0, 0); + if (!NT_SUCCESS(Result)) + return Result; + + if (0 == RootSddl) + RootSddl = L"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)"; + if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(RootSddl, SDDL_REVISION_1, + &RootSecurity, &RootSecuritySize)) + return FspNtStatusFromWin32(GetLastError()); + + Memfs = (MEMFS *)malloc(sizeof *Memfs); + if (0 == Memfs) + { + LocalFree(RootSecurity); + return STATUS_INSUFFICIENT_RESOURCES; + } + + memset(Memfs, 0, sizeof *Memfs); + Memfs->MaxFileNodes = MaxFileNodes; + AllocationUnit = MEMFS_SECTOR_SIZE * MEMFS_SECTORS_PER_ALLOCATION_UNIT; + Memfs->MaxFileSize = (ULONG)((MaxFileSize + AllocationUnit - 1) / AllocationUnit * AllocationUnit); + +#ifdef MEMFS_SLOWIO + Memfs->SlowioMaxDelay = SlowioMaxDelay; + Memfs->SlowioPercentDelay = SlowioPercentDelay; + Memfs->SlowioRarefyDelay = SlowioRarefyDelay; +#endif + + Result = MemfsFileNodeMapCreate(CaseInsensitive, &Memfs->FileNodeMap); + if (!NT_SUCCESS(Result)) + { + free(Memfs); + LocalFree(RootSecurity); + return Result; + } + + memset(&VolumeParams, 0, sizeof VolumeParams); + VolumeParams.Version = sizeof FSP_FSCTL_VOLUME_PARAMS; + VolumeParams.SectorSize = MEMFS_SECTOR_SIZE; + VolumeParams.SectorsPerAllocationUnit = MEMFS_SECTORS_PER_ALLOCATION_UNIT; + VolumeParams.VolumeCreationTime = MemfsGetSystemTime(); + VolumeParams.VolumeSerialNumber = (UINT32)(MemfsGetSystemTime() / (10000 * 1000)); + VolumeParams.FileInfoTimeout = FileInfoTimeout; + VolumeParams.CaseSensitiveSearch = !CaseInsensitive; + VolumeParams.CasePreservedNames = 1; + VolumeParams.UnicodeOnDisk = 1; + VolumeParams.PersistentAcls = 1; + VolumeParams.ReparsePoints = 1; + VolumeParams.ReparsePointsAccessCheck = 0; +#if defined(MEMFS_NAMED_STREAMS) + VolumeParams.NamedStreams = 1; +#endif + VolumeParams.PostCleanupWhenModifiedOnly = 1; +#if defined(MEMFS_DIRINFO_BY_NAME) + VolumeParams.PassQueryDirectoryFileName = 1; +#endif + VolumeParams.FlushAndPurgeOnCleanup = FlushAndPurgeOnCleanup; +#if defined(MEMFS_CONTROL) + VolumeParams.DeviceControl = 1; +#endif +#if defined(MEMFS_EA) + VolumeParams.ExtendedAttributes = 1; +#endif +#if defined(MEMFS_WSL) + VolumeParams.WslFeatures = 1; +#endif + VolumeParams.AllowOpenInKernelMode = 1; +#if defined(MEMFS_REJECT_EARLY_IRP) + VolumeParams.RejectIrpPriorToTransact0 = 1; +#endif + VolumeParams.SupportsPosixUnlinkRename = SupportsPosixUnlinkRename; + if (0 != VolumePrefix) + wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix); + wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR), + 0 != FileSystemName ? FileSystemName : L"-MEMFS"); + + Result = FspFileSystemCreate(DevicePath, &VolumeParams, &MemfsInterface, &Memfs->FileSystem); + if (!NT_SUCCESS(Result)) + { + MemfsFileNodeMapDelete(Memfs->FileNodeMap); + free(Memfs); + LocalFree(RootSecurity); + return Result; + } + + Memfs->FileSystem->UserContext = Memfs; + Memfs->VolumeLabelLength = sizeof L"MEMFS" - sizeof(WCHAR); + memcpy(Memfs->VolumeLabel, L"MEMFS", Memfs->VolumeLabelLength); + +#if 0 + FspFileSystemSetOperationGuardStrategy(Memfs->FileSystem, + FSP_FILE_SYSTEM_OPERATION_GUARD_STRATEGY_COARSE); +#endif + + /* + * Create root directory. + */ + + Result = MemfsFileNodeCreate(L"\\", &RootNode); + if (!NT_SUCCESS(Result)) + { + MemfsDelete(Memfs); + LocalFree(RootSecurity); + return Result; + } + + RootNode->FileInfo.FileAttributes = FILE_ATTRIBUTE_DIRECTORY; + + RootNode->FileSecurity = malloc(RootSecuritySize); + if (0 == RootNode->FileSecurity) + { + MemfsFileNodeDelete(RootNode); + MemfsDelete(Memfs); + LocalFree(RootSecurity); + return STATUS_INSUFFICIENT_RESOURCES; + } + RootNode->FileSecuritySize = RootSecuritySize; + memcpy(RootNode->FileSecurity, RootSecurity, RootSecuritySize); + + Result = MemfsFileNodeMapInsert(Memfs->FileNodeMap, RootNode, &Inserted); + if (!NT_SUCCESS(Result)) + { + MemfsFileNodeDelete(RootNode); + MemfsDelete(Memfs); + LocalFree(RootSecurity); + return Result; + } + + LocalFree(RootSecurity); + + *PMemfs = Memfs; + + return STATUS_SUCCESS; +} + +VOID MemfsDelete(MEMFS *Memfs) +{ + FspFileSystemDelete(Memfs->FileSystem); + + MemfsFileNodeMapDelete(Memfs->FileNodeMap); + + free(Memfs); +} + +NTSTATUS MemfsStart(MEMFS *Memfs) +{ +#ifdef MEMFS_SLOWIO + Memfs->SlowioThreadsRunning = 0; +#endif + + return FspFileSystemStartDispatcher(Memfs->FileSystem, 0); +} + +VOID MemfsStop(MEMFS *Memfs) +{ + FspFileSystemStopDispatcher(Memfs->FileSystem); + +#ifdef MEMFS_SLOWIO + while (Memfs->SlowioThreadsRunning) + Sleep(1); +#endif +} + +FSP_FILE_SYSTEM *MemfsFileSystem(MEMFS *Memfs) +{ + return Memfs->FileSystem; +} + +NTSTATUS MemfsHeapConfigure(SIZE_T InitialSize, SIZE_T MaximumSize, SIZE_T Alignment) +{ + return LargeHeapInitialize(0, InitialSize, MaximumSize, LargeHeapAlignment) ? + STATUS_SUCCESS : STATUS_INSUFFICIENT_RESOURCES; +} diff --git a/3rd_party/winfsp-1.10/samples/memfs/memfs.h b/3rd_party/winfsp-1.10/samples/memfs/memfs.h new file mode 100644 index 00000000..d3e66334 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/memfs/memfs.h @@ -0,0 +1,79 @@ +/** + * @file memfs.h + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef MEMFS_H_INCLUDED +#define MEMFS_H_INCLUDED + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _MEMFS MEMFS; + +enum +{ + MemfsDisk = 0x00000000, + MemfsNet = 0x00000001, + MemfsDeviceMask = 0x0000000f, + MemfsCaseInsensitive = 0x80000000, + MemfsFlushAndPurgeOnCleanup = 0x40000000, + MemfsLegacyUnlinkRename = 0x20000000, +}; + +#define MemfsCreate(Flags, FileInfoTimeout, MaxFileNodes, MaxFileSize, VolumePrefix, RootSddl, PMemfs)\ + MemfsCreateFunnel(\ + Flags,\ + FileInfoTimeout,\ + MaxFileNodes,\ + MaxFileSize,\ + 0/*SlowioMaxDelay*/,\ + 0/*SlowioPercentDelay*/,\ + 0/*SlowioRarefyDelay*/,\ + 0/*FileSystemName*/,\ + VolumePrefix,\ + RootSddl,\ + PMemfs) +NTSTATUS MemfsCreateFunnel( + ULONG Flags, + ULONG FileInfoTimeout, + ULONG MaxFileNodes, + ULONG MaxFileSize, + ULONG SlowioMaxDelay, + ULONG SlowioPercentDelay, + ULONG SlowioRarefyDelay, + PWSTR FileSystemName, + PWSTR VolumePrefix, + PWSTR RootSddl, + MEMFS **PMemfs); +VOID MemfsDelete(MEMFS *Memfs); +NTSTATUS MemfsStart(MEMFS *Memfs); +VOID MemfsStop(MEMFS *Memfs); +FSP_FILE_SYSTEM *MemfsFileSystem(MEMFS *Memfs); + +NTSTATUS MemfsHeapConfigure(SIZE_T InitialSize, SIZE_T MaximumSize, SIZE_T Alignment); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3rd_party/winfsp-1.10/samples/notifyfs-dotnet/Program.cs b/3rd_party/winfsp-1.10/samples/notifyfs-dotnet/Program.cs new file mode 100644 index 00000000..ec0211cc --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/notifyfs-dotnet/Program.cs @@ -0,0 +1,394 @@ +/** + * @file Program.cs + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.AccessControl; +using System.Text; +using System.Threading; + +using Fsp; +using VolumeInfo = Fsp.Interop.VolumeInfo; +using FileInfo = Fsp.Interop.FileInfo; +using NotifyInfo = Fsp.Interop.NotifyInfo; +using NotifyAction = Fsp.Interop.NotifyAction; +using NotifyFilter = Fsp.Interop.NotifyFilter; + +namespace notifyfs +{ + class Notifyfs : FileSystemBase + { + public override Int32 Init(Object Host) + { + _Host = (FileSystemHost)Host; + _Host.SectorSize = ALLOCATION_UNIT; + _Host.SectorsPerAllocationUnit = 1; + _Host.FileInfoTimeout = 1000; + _Host.CaseSensitiveSearch = false; + _Host.CasePreservedNames = true; + _Host.UnicodeOnDisk = true; + _Host.PersistentAcls = false; + _Host.PostCleanupWhenModifiedOnly = true; + _Host.VolumeCreationTime = 0; + _Host.VolumeSerialNumber = 0; + + return STATUS_SUCCESS; + } + public override Int32 Mounted(Object Host) + { + _Timer = new Timer(this.Tick, null, 0, 1000); + + return STATUS_SUCCESS; + } + public override void Unmounted(Object Host) + { + WaitHandle Event = new ManualResetEvent(false); + _Timer.Dispose(Event); + Event.WaitOne(); + } + public override Int32 GetVolumeInfo( + out VolumeInfo VolumeInfo) + { + VolumeInfo = default(VolumeInfo); + + return STATUS_SUCCESS; + } + public override Int32 GetSecurityByName( + String FileName, + out UInt32 FileAttributes/* or ReparsePointIndex */, + ref Byte[] SecurityDescriptor) + { + int Index = FileLookup(FileName); + if (-1 == Index) + { + FileAttributes = default(UInt32); + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + FileAttributes = 0 == Index ? (UInt32)System.IO.FileAttributes.Directory : 0; + if (null != SecurityDescriptor) + SecurityDescriptor = DefaultSecurity; + + return STATUS_SUCCESS; + } + public override Int32 Open( + String FileName, + UInt32 CreateOptions, + UInt32 GrantedAccess, + out Object FileNode, + out Object FileDesc, + out FileInfo FileInfo, + out String NormalizedName) + { + FileNode = default(Object); + FileDesc = default(Object); + FileInfo = default(FileInfo); + NormalizedName = default(String); + + int Index = FileLookup(FileName); + if (-1 == Index) + return STATUS_OBJECT_NAME_NOT_FOUND; + + FileNode = Index; + FillFileInfo(Index, out FileInfo); + + return STATUS_SUCCESS; + } + public override Int32 Read( + Object FileNode, + Object FileDesc, + IntPtr Buffer, + UInt64 Offset, + UInt32 Length, + out UInt32 BytesTransferred) + { + int Index = (int)FileNode; + UInt64 EndOffset; + Byte[] Contents = FileContents(Index); + + if (Offset >= (UInt64)Contents.Length) + { + BytesTransferred = 0; + return STATUS_END_OF_FILE; + } + + EndOffset = Offset + Length; + if (EndOffset > (UInt64)Contents.Length) + EndOffset = (UInt64)Contents.Length; + + BytesTransferred = (UInt32)(EndOffset - Offset); + Marshal.Copy(Contents, (int)Offset, Buffer, (int)BytesTransferred); + + return STATUS_SUCCESS; + } + public override Int32 GetFileInfo( + Object FileNode, + Object FileDesc, + out FileInfo FileInfo) + { + int Index = (int)FileNode; + + FillFileInfo(Index, out FileInfo); + + return STATUS_SUCCESS; + } + public override Boolean ReadDirectoryEntry( + Object FileNode, + Object FileDesc, + String Pattern, + String Marker, + ref Object Context, + out String FileName, + out FileInfo FileInfo) + { + IEnumerator Enumerator = (IEnumerator)Context; + + if (null == Enumerator) + { + List ChildrenFileNames = new List(); + for (int Index = 1, Count = FileCount(); Count >= Index; Index++) + ChildrenFileNames.Add(String.Format("{0}", Index)); + Context = Enumerator = ChildrenFileNames.GetEnumerator(); + } + + while (Enumerator.MoveNext()) + { + FileName = Enumerator.Current; + FillFileInfo(int.Parse(FileName), out FileInfo); + return true; + } + + FileName = default(String); + FileInfo = default(FileInfo); + return false; + } + + private static int CountFromTicks(int Ticks) + { + /* + * The formula below produces the periodic sequence: + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + * ... + */ + int div10 = (Ticks % 20) / 10; + int mod10 = Ticks % 10; + int mdv10 = 1 - div10; + int mmd10 = 10 - mod10; + return mdv10 * mod10 + div10 * mmd10; + } + private int FileCount() + { + int Ticks = Thread.VolatileRead(ref _Ticks); + return CountFromTicks(Ticks); + } + private int FileLookup(String FileName) + { + FileName = FileName.Substring(1); + if ("" == FileName) + return 0; /* root */ + int Count = FileCount(); + Boolean Valid = int.TryParse(FileName, out int Index); + if (!Valid || 0 >= Index || Index > Count) + return -1; /* not found */ + return Index; /* regular file named 1, 2, ..., Count */ + } + private static Byte[] FileContents(int Index) + { + if (0 == Index) + return EmptyByteArray; + return Encoding.UTF8.GetBytes(String.Format("{0}\n", Index)); + } + private static void FillFileInfo(int Index, out FileInfo FileInfo) + { + FileInfo = default(FileInfo); + FileInfo.FileAttributes = 0 == Index ? (UInt32)System.IO.FileAttributes.Directory : 0; + FileInfo.FileSize = (UInt64)FileContents(Index).Length; + FileInfo.AllocationSize = (FileInfo.FileSize + ALLOCATION_UNIT - 1) + / ALLOCATION_UNIT * ALLOCATION_UNIT; + FileInfo.CreationTime = + FileInfo.LastAccessTime = + FileInfo.LastWriteTime = + FileInfo.ChangeTime = (UInt64)DateTime.Now.ToFileTimeUtc(); + } + private void Tick(Object Context) + { + int Ticks = Interlocked.Increment(ref _Ticks); + int OldCount = CountFromTicks(Ticks - 1); + int NewCount = CountFromTicks(Ticks); + NotifyInfo[] NotifyInfo = new NotifyInfo[1]; + + if (OldCount < NewCount) + { + NotifyInfo[0].FileName = String.Format("\\{0}", NewCount); + NotifyInfo[0].Action = NotifyAction.Added; + NotifyInfo[0].Filter = NotifyFilter.ChangeFileName; + Console.Error.WriteLine("CREATE \\{0}", NewCount); + } + else if (OldCount > NewCount) + { + NotifyInfo[0].FileName = String.Format("\\{0}", OldCount); + NotifyInfo[0].Action = NotifyAction.Removed; + NotifyInfo[0].Filter = NotifyFilter.ChangeFileName; + Console.Error.WriteLine("REMOVE \\{0}", OldCount); + } + + if (OldCount != NewCount) + { + if (STATUS_SUCCESS == _Host.NotifyBegin(500)) + { + _Host.Notify(NotifyInfo); + _Host.NotifyEnd(); + } + } + } + + static Notifyfs() + { + RawSecurityDescriptor RootSecurityDescriptor = new RawSecurityDescriptor( + "O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)"); + DefaultSecurity = new Byte[RootSecurityDescriptor.BinaryLength]; + RootSecurityDescriptor.GetBinaryForm(DefaultSecurity, 0); + } + + private const int ALLOCATION_UNIT = 4096; + private static readonly Byte[] EmptyByteArray = new Byte[0]; + private static readonly Byte[] DefaultSecurity; + private FileSystemHost _Host; + private Timer _Timer; + private int _Ticks; + } + + class NotifyfsService : Service + { + private class CommandLineUsageException : Exception + { + public CommandLineUsageException(String Message = null) : base(Message) + { + HasMessage = null != Message; + } + + public bool HasMessage; + } + + private const String PROGNAME = "notifyfs-dotnet"; + + public NotifyfsService() : base("NotifyfsService") + { + } + + protected override void OnStart(String[] Args) + { + try + { + String VolumePrefix = null; + String MountPoint = null; + FileSystemHost Host = null; + Notifyfs Notifyfs = null; + int I; + + for (I = 1; Args.Length > I; I++) + { + String Arg = Args[I]; + if ('-' != Arg[0]) + break; + switch (Arg[1]) + { + case '?': + throw new CommandLineUsageException(); + case 'm': + argtos(Args, ref I, ref MountPoint); + break; + case 'u': + argtos(Args, ref I, ref VolumePrefix); + break; + default: + throw new CommandLineUsageException(); + } + } + + if (Args.Length > I) + throw new CommandLineUsageException(); + + if (null == MountPoint) + throw new CommandLineUsageException(); + + FileSystemHost.SetDebugLogFile("-"); + + Host = new FileSystemHost(Notifyfs = new Notifyfs()); + Host.Prefix = VolumePrefix; + if (0 > Host.Mount(MountPoint)) + throw new IOException("cannot mount file system"); + MountPoint = Host.MountPoint(); + _Host = Host; + + Log(EVENTLOG_INFORMATION_TYPE, String.Format("{0}{1}{2} -m {3}", + PROGNAME, + null != VolumePrefix && 0 < VolumePrefix.Length ? " -u " : "", + null != VolumePrefix && 0 < VolumePrefix.Length ? VolumePrefix : "", + MountPoint)); + } + catch (CommandLineUsageException ex) + { + Log(EVENTLOG_ERROR_TYPE, String.Format( + "{0}" + + "usage: {1} OPTIONS\n" + + "\n" + + "options:\n" + + " -u \\Server\\Share [UNC prefix (single backslash)]\n" + + " -m MountPoint [X:|*|directory]\n", + ex.HasMessage ? ex.Message + "\n" : "", + PROGNAME)); + throw; + } + catch (Exception ex) + { + Log(EVENTLOG_ERROR_TYPE, String.Format("{0}", ex.Message)); + throw; + } + } + protected override void OnStop() + { + _Host.Unmount(); + _Host = null; + } + + private static void argtos(String[] Args, ref int I, ref String V) + { + if (Args.Length > ++I) + V = Args[I]; + else + throw new CommandLineUsageException(); + } + + private FileSystemHost _Host; + } + + class Program + { + static void Main(string[] args) + { + Environment.ExitCode = new NotifyfsService().Run(); + } + } +} diff --git a/3rd_party/winfsp-1.10/samples/notifyfs-dotnet/notifyfs-dotnet.csproj b/3rd_party/winfsp-1.10/samples/notifyfs-dotnet/notifyfs-dotnet.csproj new file mode 100644 index 00000000..2b71840a --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/notifyfs-dotnet/notifyfs-dotnet.csproj @@ -0,0 +1,77 @@ + + + + + Debug + AnyCPU + {DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA} + Exe + notifyfs + notifyfs-dotnet + v4.5.2 + 512 + true + true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + AnyCPU + true + full + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\ + $(BaseIntermediateOutputPath)$(Configuration)\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\ + $(BaseIntermediateOutputPath)$(Configuration)\ + TRACE + prompt + 4 + + + + + + + + $(MSBuildProgramFiles32)\WinFsp\bin\winfsp-msil.dll + + + + + False + Microsoft .NET Framework 4.5.2 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/notifyfs-dotnet/notifyfs-dotnet.sln b/3rd_party/winfsp-1.10/samples/notifyfs-dotnet/notifyfs-dotnet.sln new file mode 100644 index 00000000..d131b1de --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/notifyfs-dotnet/notifyfs-dotnet.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30717.126 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "notifyfs-dotnet", "notifyfs-dotnet.csproj", "{DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA7C383A-D10F-4FB0-BDCB-E7A7C5D068AA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {18D87005-09CA-40CF-9B51-139B486AB8D0} + EndGlobalSection +EndGlobal diff --git a/3rd_party/winfsp-1.10/samples/notifyfs/notifyfs.c b/3rd_party/winfsp-1.10/samples/notifyfs/notifyfs.c new file mode 100644 index 00000000..12e58024 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/notifyfs/notifyfs.c @@ -0,0 +1,508 @@ +/** + * @file notifyfs.c + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#include +#include + +#define DEBUGFLAGS 0 +//#define DEBUGFLAGS -1 + +#define PROGNAME "notifyfs" +#define ALLOCATION_UNIT 4096 + +#define info(format, ...) FspServiceLog(EVENTLOG_INFORMATION_TYPE, format, __VA_ARGS__) +#define warn(format, ...) FspServiceLog(EVENTLOG_WARNING_TYPE, format, __VA_ARGS__) +#define fail(format, ...) FspServiceLog(EVENTLOG_ERROR_TYPE, format, __VA_ARGS__) + +typedef struct +{ + FSP_FILE_SYSTEM *FileSystem; + PTP_TIMER Timer; + UINT32 Ticks; +} NOTIFYFS; + +static PSECURITY_DESCRIPTOR DefaultSecurity; +static ULONG DefaultSecuritySize; + +static UINT32 CountFromTicks(UINT32 Ticks) +{ + /* + * The formula below produces the periodic sequence: + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, + * ... + */ + UINT32 div10 = (Ticks % 20) / 10; + UINT32 mod10 = Ticks % 10; + UINT32 mdv10 = 1 - div10; + UINT32 mmd10 = 10 - mod10; + return mdv10 * mod10 + div10 * mmd10; +} + +static UINT32 FileCount(NOTIFYFS *Notifyfs) +{ + UINT32 Ticks = InterlockedOr(&Notifyfs->Ticks, 0); + return CountFromTicks(Ticks); +} + +static UINT32 FileLookup(NOTIFYFS *Notifyfs, PWSTR FileName) +{ + FileName++; + PWSTR Endp; + UINT32 Count = FileCount(Notifyfs); + UINT32 Index = wcstoul(FileName, &Endp, 10); + if ('\0' != *Endp || (FileName != Endp && (0 == Index || Index > Count))) + return -1; /* not found */ + if (FileName == Endp) + return 0; /* root */ + return Index; /* regular file named 1, 2, ..., Count */ +} + +static UINT32 FileContents(UINT32 Index, WCHAR P[32]) +{ + WCHAR Buffer[32]; + if (0 == P) + P = Buffer; + if (0 == Index) + P[0] = '\0'; + else + wsprintfW(P, L"%u\n", (unsigned)Index); + return lstrlenW(P); +} + +static VOID FillFileInfo(UINT32 Index, FSP_FSCTL_FILE_INFO *FileInfo) +{ + FILETIME SystemTime; + + GetSystemTimeAsFileTime(&SystemTime); + + memset(FileInfo, 0, sizeof FileInfo); + FileInfo->FileAttributes = 0 == Index ? FILE_ATTRIBUTE_DIRECTORY : 0; + FileInfo->FileSize = FileContents(Index, 0); + FileInfo->AllocationSize = (FileInfo->FileSize + ALLOCATION_UNIT - 1) + / ALLOCATION_UNIT * ALLOCATION_UNIT; + FileInfo->CreationTime = + FileInfo->LastAccessTime = + FileInfo->LastWriteTime = + FileInfo->ChangeTime = *(PUINT64)&SystemTime; +} + +static BOOLEAN AddDirInfo(PWSTR FileName, UINT32 Index, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + union + { + UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) + MAX_PATH * sizeof(WCHAR)]; + FSP_FSCTL_DIR_INFO D; + } DirInfoBuf; + FSP_FSCTL_DIR_INFO *DirInfo = &DirInfoBuf.D; + + memset(DirInfo->Padding, 0, sizeof DirInfo->Padding); + if (0 != FileName) + lstrcpyW(DirInfo->FileNameBuf, FileName); + else + wsprintfW(DirInfo->FileNameBuf, L"%u", (unsigned)Index); + DirInfo->Size = (UINT16)(sizeof(FSP_FSCTL_DIR_INFO) + wcslen(DirInfo->FileNameBuf) * sizeof(WCHAR)); + FillFileInfo(Index, &DirInfo->FileInfo); + + return FspFileSystemAddDirInfo(DirInfo, Buffer, Length, PBytesTransferred); +} + +static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + memset(VolumeInfo, 0, sizeof *VolumeInfo); + + return STATUS_SUCCESS; +} + +static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, PUINT32 PFileAttributes, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext; + UINT32 Index; + + Index = FileLookup(Notifyfs, FileName); + if (-1 == Index) + return STATUS_OBJECT_NAME_NOT_FOUND; + + if (0 != PFileAttributes) + *PFileAttributes = 0 == Index ? FILE_ATTRIBUTE_DIRECTORY : 0; + + if (0 != PSecurityDescriptorSize) + { + if (DefaultSecuritySize > *PSecurityDescriptorSize) + { + *PSecurityDescriptorSize = DefaultSecuritySize; + return STATUS_BUFFER_OVERFLOW; + } + + *PSecurityDescriptorSize = DefaultSecuritySize; + if (0 != SecurityDescriptor) + memcpy(SecurityDescriptor, DefaultSecurity, DefaultSecuritySize); + } + + return STATUS_SUCCESS; +} + +static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, + PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo) +{ + return STATUS_INVALID_DEVICE_REQUEST; +} + +static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo) +{ + NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext; + FSP_FSCTL_TRANSACT_FULL_CONTEXT *FullContext = (PVOID)PFileContext; + UINT32 Index; + + Index = FileLookup(Notifyfs, FileName); + if (-1 == Index) + return STATUS_OBJECT_NAME_NOT_FOUND; + + FullContext->UserContext = Index; + FullContext->UserContext2 = 0; + + FillFileInfo(Index, FileInfo); + + return STATUS_SUCCESS; +} + +static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + return STATUS_INVALID_DEVICE_REQUEST; +} + +static NTSTATUS Read(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PVOID Buffer, UINT64 Offset, ULONG Length, + PULONG PBytesTransferred) +{ + NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext; + FSP_FSCTL_TRANSACT_FULL_CONTEXT *FullContext = FileContext; + UINT32 Index = (UINT32)FullContext->UserContext; + UINT64 EndOffset; + WCHAR ContentBuf[32]; + UINT32 ContentLen; + + ContentLen = FileContents(Index, ContentBuf); + + if (Offset >= ContentLen) + return STATUS_END_OF_FILE; + + EndOffset = Offset + Length; + if (EndOffset > ContentLen) + EndOffset = ContentLen; + + memcpy(Buffer, (PUINT8)ContentBuf + Offset, (size_t)(EndOffset - Offset)); + + *PBytesTransferred = (ULONG)(EndOffset - Offset); + + return STATUS_SUCCESS; +} + +static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext; + FSP_FSCTL_TRANSACT_FULL_CONTEXT *FullContext = FileContext; + UINT32 Index = (UINT32)FullContext->UserContext; + + FillFileInfo(Index, FileInfo); + + return STATUS_SUCCESS; +} + +static NTSTATUS ReadDirectory(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PWSTR Pattern, PWSTR Marker, + PVOID Buffer, ULONG Length, PULONG PBytesTransferred) +{ + NOTIFYFS *Notifyfs = (NOTIFYFS *)FileSystem->UserContext; + UINT32 Count = FileCount(Notifyfs); + UINT32 Index; + + Index = 0 == Marker ? 1 : wcstoul(Marker, 0, 10) + 1; + for (; Count >= Index; Index++) + if (!AddDirInfo(0, Index, Buffer, Length, PBytesTransferred)) + break; + FspFileSystemAddDirInfo(0, Buffer, Length, PBytesTransferred); + + return STATUS_SUCCESS; +} + +static FSP_FILE_SYSTEM_INTERFACE NotifyfsInterface = +{ + .GetVolumeInfo = GetVolumeInfo, + .GetSecurityByName = GetSecurityByName, + .Create = Create, + .Open = Open, + .Overwrite = Overwrite, + .Read = Read, + .GetFileInfo = GetFileInfo, + .ReadDirectory = ReadDirectory, +}; + +static VOID CALLBACK NotifyfsTick(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_TIMER Timer) +{ + NOTIFYFS *Notifyfs = Context; + UINT32 Ticks = InterlockedIncrement(&Notifyfs->Ticks); + UINT32 OldCount = CountFromTicks(Ticks - 1); + UINT32 NewCount = CountFromTicks(Ticks); + union + { + UINT8 B[FIELD_OFFSET(FSP_FSCTL_NOTIFY_INFO, FileNameBuf) + MAX_PATH * sizeof(WCHAR)]; + FSP_FSCTL_NOTIFY_INFO V; + } NotifyInfoBuf; + FSP_FSCTL_NOTIFY_INFO *NotifyInfo = &NotifyInfoBuf.V; + + memset(NotifyInfo, 0, sizeof NotifyInfo); + if (OldCount < NewCount) + { + wsprintfW(NotifyInfo->FileNameBuf, L"\\%u", (unsigned)NewCount); + NotifyInfo->Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + + wcslen(NotifyInfo->FileNameBuf) * sizeof(WCHAR)); + NotifyInfo->Action = FILE_ACTION_ADDED; + NotifyInfo->Filter = FILE_NOTIFY_CHANGE_FILE_NAME; + FspDebugLog("CREATE \\%u\n", (unsigned)NewCount); + } + else if (OldCount > NewCount) + { + wsprintfW(NotifyInfo->FileNameBuf, L"\\%u", (unsigned)OldCount); + NotifyInfo->Size = (UINT16)(sizeof(FSP_FSCTL_NOTIFY_INFO) + + wcslen(NotifyInfo->FileNameBuf) * sizeof(WCHAR)); + NotifyInfo->Action = FILE_ACTION_REMOVED; + NotifyInfo->Filter = FILE_NOTIFY_CHANGE_FILE_NAME; + FspDebugLog("REMOVE \\%u\n", (unsigned)OldCount); + } + + if (OldCount != NewCount) + { + if (STATUS_SUCCESS == FspFileSystemNotifyBegin(Notifyfs->FileSystem, 500)) + { + FspFileSystemNotify(Notifyfs->FileSystem, NotifyInfo, NotifyInfo->Size); + FspFileSystemNotifyEnd(Notifyfs->FileSystem); + } + } +} + +static VOID NotifyfsDelete(NOTIFYFS *Notifyfs); + +static NTSTATUS NotifyfsCreate(PWSTR VolumePrefix, PWSTR MountPoint, NOTIFYFS **PNotifyfs) +{ + FSP_FSCTL_VOLUME_PARAMS VolumeParams; + NOTIFYFS *Notifyfs = 0; + INT64 TimerDue; + NTSTATUS Result; + + *PNotifyfs = 0; + + if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( + L"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)", SDDL_REVISION_1, + &DefaultSecurity, &DefaultSecuritySize)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + Notifyfs = malloc(sizeof *Notifyfs); + if (0 == Notifyfs) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + memset(Notifyfs, 0, sizeof *Notifyfs); + + memset(&VolumeParams, 0, sizeof VolumeParams); + VolumeParams.SectorSize = ALLOCATION_UNIT; + VolumeParams.SectorsPerAllocationUnit = 1; + VolumeParams.VolumeCreationTime = 0; + VolumeParams.VolumeSerialNumber = 0; + VolumeParams.FileInfoTimeout = 1000; + VolumeParams.CaseSensitiveSearch = 0; + VolumeParams.CasePreservedNames = 1; + VolumeParams.UnicodeOnDisk = 1; + VolumeParams.PersistentAcls = 0; + VolumeParams.PostCleanupWhenModifiedOnly = 1; + VolumeParams.UmFileContextIsFullContext = 1; + if (0 != VolumePrefix) + wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix); + wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR), + L"" PROGNAME); + + Result = FspFileSystemCreate( + VolumeParams.Prefix[0] ? L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME, + &VolumeParams, + &NotifyfsInterface, + &Notifyfs->FileSystem); + if (!NT_SUCCESS(Result)) + goto exit; + Notifyfs->FileSystem->UserContext = Notifyfs; + + FspFileSystemSetDebugLog(Notifyfs->FileSystem, DEBUGFLAGS); + + Result = FspFileSystemSetMountPoint(Notifyfs->FileSystem, MountPoint); + if (!NT_SUCCESS(Result)) + goto exit; + + Notifyfs->Timer = CreateThreadpoolTimer(NotifyfsTick, Notifyfs, 0); + if (0 == Notifyfs->Timer) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + TimerDue = -1000; + SetThreadpoolTimer(Notifyfs->Timer, (PVOID)&TimerDue, 1000, 0); + + Result = STATUS_SUCCESS; + +exit: + if (NT_SUCCESS(Result)) + *PNotifyfs = Notifyfs; + else if (0 != Notifyfs) + NotifyfsDelete(Notifyfs); + + return Result; +} + +static VOID NotifyfsDelete(NOTIFYFS *Notifyfs) +{ + if (0 != Notifyfs->Timer) + { + SetThreadpoolTimer(Notifyfs->Timer, 0, 0, 0); + WaitForThreadpoolTimerCallbacks(Notifyfs->Timer, TRUE); + CloseThreadpoolTimer(Notifyfs->Timer); + } + + if (0 != Notifyfs->FileSystem) + FspFileSystemDelete(Notifyfs->FileSystem); + + free(Notifyfs); +} + +static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) +{ +#define argtos(v) if (arge > ++argp) v = *argp; else goto usage + + wchar_t **argp, **arge; + PWSTR VolumePrefix = 0; + PWSTR MountPoint = 0; + NOTIFYFS *Notifyfs = 0; + NTSTATUS Result; + + for (argp = argv + 1, arge = argv + argc; arge > argp; argp++) + { + if (L'-' != argp[0][0]) + break; + switch (argp[0][1]) + { + case L'?': + goto usage; + case L'm': + argtos(MountPoint); + break; + case L'u': + argtos(VolumePrefix); + break; + default: + goto usage; + } + } + + if (arge > argp) + goto usage; + + if (0 == MountPoint) + goto usage; + + FspDebugLogSetHandle(GetStdHandle(STD_ERROR_HANDLE)); + + Result = NotifyfsCreate(VolumePrefix, MountPoint, &Notifyfs); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot create file system"); + goto exit; + } + + Result = FspFileSystemStartDispatcher(Notifyfs->FileSystem, 0); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot start file system"); + goto exit; + } + + MountPoint = FspFileSystemMountPoint(Notifyfs->FileSystem); + + info(L"%s%s%s -m %s", + L"" PROGNAME, + 0 != VolumePrefix && L'\0' != VolumePrefix[0] ? L" -u " : L"", + 0 != VolumePrefix && L'\0' != VolumePrefix[0] ? VolumePrefix : L"", + MountPoint); + + Service->UserContext = Notifyfs; + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result) && 0 != Notifyfs) + NotifyfsDelete(Notifyfs); + + return Result; + +usage:; + static wchar_t usage[] = L"" + "usage: %s OPTIONS\n" + "\n" + "options:\n" + " -u \\Server\\Share [UNC prefix (single backslash)]\n" + " -m MountPoint [X:|*|directory]\n"; + + fail(usage, L"" PROGNAME); + + return STATUS_UNSUCCESSFUL; + +#undef argtos +} + +static NTSTATUS SvcStop(FSP_SERVICE *Service) +{ + NOTIFYFS *Notifyfs = Service->UserContext; + + FspFileSystemStopDispatcher(Notifyfs->FileSystem); + NotifyfsDelete(Notifyfs); + + return STATUS_SUCCESS; +} + +int wmain(int argc, wchar_t **argv) +{ + if (!NT_SUCCESS(FspLoad(0))) + return ERROR_DELAY_LOAD_FAILED; + + return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0); +} diff --git a/3rd_party/winfsp-1.10/samples/notifyfs/notifyfs.sln b/3rd_party/winfsp-1.10/samples/notifyfs/notifyfs.sln new file mode 100644 index 00000000..93b88953 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/notifyfs/notifyfs.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30717.126 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "notifyfs", "notifyfs.vcxproj", "{4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Debug|x64.ActiveCfg = Debug|x64 + {4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Debug|x64.Build.0 = Debug|x64 + {4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Debug|x86.ActiveCfg = Debug|Win32 + {4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Debug|x86.Build.0 = Debug|Win32 + {4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Release|x64.ActiveCfg = Release|x64 + {4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Release|x64.Build.0 = Release|x64 + {4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Release|x86.ActiveCfg = Release|Win32 + {4BA1DED0-4268-408A-A4E2-8E1A6D55A99C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3CD0763B-2BFD-4D73-9539-13273788C41E} + EndGlobalSection +EndGlobal diff --git a/3rd_party/winfsp-1.10/samples/notifyfs/notifyfs.vcxproj b/3rd_party/winfsp-1.10/samples/notifyfs/notifyfs.vcxproj new file mode 100644 index 00000000..dbc6ce6a --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/notifyfs/notifyfs.vcxproj @@ -0,0 +1,175 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {4ba1ded0-4268-408a-a4e2-8e1a6d55a99c} + notifyfs + $(LatestTargetPlatformVersion) + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/notifyfs/notifyfs.vcxproj.filters b/3rd_party/winfsp-1.10/samples/notifyfs/notifyfs.vcxproj.filters new file mode 100644 index 00000000..fb8d0ba8 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/notifyfs/notifyfs.vcxproj.filters @@ -0,0 +1,14 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/passthrough-dotnet/Program.cs b/3rd_party/winfsp-1.10/samples/passthrough-dotnet/Program.cs new file mode 100644 index 00000000..53f69c19 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-dotnet/Program.cs @@ -0,0 +1,859 @@ +/** + * @file Program.cs + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +using System; +using System.Collections; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.AccessControl; + +using Fsp; +using VolumeInfo = Fsp.Interop.VolumeInfo; +using FileInfo = Fsp.Interop.FileInfo; + +namespace passthrough +{ + class Ptfs : FileSystemBase + { + protected const int ALLOCATION_UNIT = 4096; + + protected static void ThrowIoExceptionWithHResult(Int32 HResult) + { + throw new IOException(null, HResult); + } + protected static void ThrowIoExceptionWithWin32(Int32 Error) + { + ThrowIoExceptionWithHResult(unchecked((Int32)(0x80070000 | Error))); + } + protected static void ThrowIoExceptionWithNtStatus(Int32 Status) + { + ThrowIoExceptionWithWin32((Int32)Win32FromNtStatus(Status)); + } + + protected class FileDesc + { + public FileStream Stream; + public DirectoryInfo DirInfo; + public DictionaryEntry[] FileSystemInfos; + + public FileDesc(FileStream Stream) + { + this.Stream = Stream; + } + public FileDesc(DirectoryInfo DirInfo) + { + this.DirInfo = DirInfo; + } + public static void GetFileInfoFromFileSystemInfo( + FileSystemInfo Info, + out FileInfo FileInfo) + { + FileInfo.FileAttributes = (UInt32)Info.Attributes; + FileInfo.ReparseTag = 0; + FileInfo.FileSize = Info is System.IO.FileInfo ? + (UInt64)((System.IO.FileInfo)Info).Length : 0; + FileInfo.AllocationSize = (FileInfo.FileSize + ALLOCATION_UNIT - 1) + / ALLOCATION_UNIT * ALLOCATION_UNIT; + FileInfo.CreationTime = (UInt64)Info.CreationTimeUtc.ToFileTimeUtc(); + FileInfo.LastAccessTime = (UInt64)Info.LastAccessTimeUtc.ToFileTimeUtc(); + FileInfo.LastWriteTime = (UInt64)Info.LastWriteTimeUtc.ToFileTimeUtc(); + FileInfo.ChangeTime = FileInfo.LastWriteTime; + FileInfo.IndexNumber = 0; + FileInfo.HardLinks = 0; + } + public Int32 GetFileInfo(out FileInfo FileInfo) + { + if (null != Stream) + { + BY_HANDLE_FILE_INFORMATION Info; + if (!GetFileInformationByHandle(Stream.SafeFileHandle.DangerousGetHandle(), + out Info)) + ThrowIoExceptionWithWin32(Marshal.GetLastWin32Error()); + FileInfo.FileAttributes = Info.dwFileAttributes; + FileInfo.ReparseTag = 0; + FileInfo.FileSize = (UInt64)Stream.Length; + FileInfo.AllocationSize = (FileInfo.FileSize + ALLOCATION_UNIT - 1) + / ALLOCATION_UNIT * ALLOCATION_UNIT; + FileInfo.CreationTime = Info.ftCreationTime; + FileInfo.LastAccessTime = Info.ftLastAccessTime; + FileInfo.LastWriteTime = Info.ftLastWriteTime; + FileInfo.ChangeTime = FileInfo.LastWriteTime; + FileInfo.IndexNumber = 0; + FileInfo.HardLinks = 0; + } + else + GetFileInfoFromFileSystemInfo(DirInfo, out FileInfo); + return STATUS_SUCCESS; + } + public void SetBasicInfo( + UInt32 FileAttributes, + UInt64 CreationTime, + UInt64 LastAccessTime, + UInt64 LastWriteTime) + { + if (0 == FileAttributes) + FileAttributes = (UInt32)System.IO.FileAttributes.Normal; + if (null != Stream) + { + FILE_BASIC_INFO Info = default(FILE_BASIC_INFO); + if (unchecked((UInt32)(-1)) != FileAttributes) + Info.FileAttributes = FileAttributes; + if (0 != CreationTime) + Info.CreationTime = CreationTime; + if (0 != LastAccessTime) + Info.LastAccessTime = LastAccessTime; + if (0 != LastWriteTime) + Info.LastWriteTime = LastWriteTime; + if (!SetFileInformationByHandle(Stream.SafeFileHandle.DangerousGetHandle(), + 0/*FileBasicInfo*/, ref Info, (UInt32)Marshal.SizeOf(Info))) + ThrowIoExceptionWithWin32(Marshal.GetLastWin32Error()); + } + else + { + if (unchecked((UInt32)(-1)) != FileAttributes) + DirInfo.Attributes = (System.IO.FileAttributes)FileAttributes; + if (0 != CreationTime) + DirInfo.CreationTimeUtc = DateTime.FromFileTimeUtc((Int64)CreationTime); + if (0 != LastAccessTime) + DirInfo.LastAccessTimeUtc = DateTime.FromFileTimeUtc((Int64)LastAccessTime); + if (0 != LastWriteTime) + DirInfo.LastWriteTimeUtc = DateTime.FromFileTimeUtc((Int64)LastWriteTime); + } + } + public UInt32 GetFileAttributes() + { + FileInfo FileInfo; + GetFileInfo(out FileInfo); + return FileInfo.FileAttributes; + } + public void SetFileAttributes(UInt32 FileAttributes) + { + SetBasicInfo(FileAttributes, 0, 0, 0); + } + public Byte[] GetSecurityDescriptor() + { + if (null != Stream) + return Stream.GetAccessControl().GetSecurityDescriptorBinaryForm(); + else + return DirInfo.GetAccessControl().GetSecurityDescriptorBinaryForm(); + } + public void SetSecurityDescriptor(AccessControlSections Sections, Byte[] SecurityDescriptor) + { + Int32 SecurityInformation = 0; + if (0 != (Sections & AccessControlSections.Owner)) + SecurityInformation |= 1/*OWNER_SECURITY_INFORMATION*/; + if (0 != (Sections & AccessControlSections.Group)) + SecurityInformation |= 2/*GROUP_SECURITY_INFORMATION*/; + if (0 != (Sections & AccessControlSections.Access)) + SecurityInformation |= 4/*DACL_SECURITY_INFORMATION*/; + if (0 != (Sections & AccessControlSections.Audit)) + SecurityInformation |= 8/*SACL_SECURITY_INFORMATION*/; + if (null != Stream) + { + if (!SetKernelObjectSecurity(Stream.SafeFileHandle.DangerousGetHandle(), + SecurityInformation, SecurityDescriptor)) + ThrowIoExceptionWithWin32(Marshal.GetLastWin32Error()); + } + else + { + if (!SetFileSecurityW(DirInfo.FullName, + SecurityInformation, SecurityDescriptor)) + ThrowIoExceptionWithWin32(Marshal.GetLastWin32Error()); + } + } + public void SetDisposition(Boolean Safe) + { + if (null != Stream) + { + FILE_DISPOSITION_INFO Info; + Info.DeleteFile = true; + if (!SetFileInformationByHandle(Stream.SafeFileHandle.DangerousGetHandle(), + 4/*FileDispositionInfo*/, ref Info, (UInt32)Marshal.SizeOf(Info))) + if (!Safe) + ThrowIoExceptionWithWin32(Marshal.GetLastWin32Error()); + } + else + try + { + DirInfo.Delete(); + } + catch (Exception ex) + { + if (!Safe) + ThrowIoExceptionWithHResult(ex.HResult); + } + } + public static void Rename(String FileName, String NewFileName, Boolean ReplaceIfExists) + { + if (!MoveFileExW(FileName, NewFileName, ReplaceIfExists ? 1U/*MOVEFILE_REPLACE_EXISTING*/ : 0)) + ThrowIoExceptionWithWin32(Marshal.GetLastWin32Error()); + } + + /* interop */ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct BY_HANDLE_FILE_INFORMATION + { + public UInt32 dwFileAttributes; + public UInt64 ftCreationTime; + public UInt64 ftLastAccessTime; + public UInt64 ftLastWriteTime; + public UInt32 dwVolumeSerialNumber; + public UInt32 nFileSizeHigh; + public UInt32 nFileSizeLow; + public UInt32 nNumberOfLinks; + public UInt32 nFileIndexHigh; + public UInt32 nFileIndexLow; + } + [StructLayout(LayoutKind.Sequential)] + private struct FILE_BASIC_INFO + { + public UInt64 CreationTime; + public UInt64 LastAccessTime; + public UInt64 LastWriteTime; + public UInt64 ChangeTime; + public UInt32 FileAttributes; + } + [StructLayout(LayoutKind.Sequential)] + private struct FILE_DISPOSITION_INFO + { + public Boolean DeleteFile; + } + [DllImport("kernel32.dll", SetLastError = true)] + private static extern Boolean GetFileInformationByHandle( + IntPtr hFile, + out BY_HANDLE_FILE_INFORMATION lpFileInformation); + [DllImport("kernel32.dll", SetLastError = true)] + private static extern Boolean SetFileInformationByHandle( + IntPtr hFile, + Int32 FileInformationClass, + ref FILE_BASIC_INFO lpFileInformation, + UInt32 dwBufferSize); + [DllImport("kernel32.dll", SetLastError = true)] + private static extern Boolean SetFileInformationByHandle( + IntPtr hFile, + Int32 FileInformationClass, + ref FILE_DISPOSITION_INFO lpFileInformation, + UInt32 dwBufferSize); + [DllImport("kernel32.dll", SetLastError = true)] + private static extern Boolean MoveFileExW( + [MarshalAs(UnmanagedType.LPWStr)] String lpExistingFileName, + [MarshalAs(UnmanagedType.LPWStr)] String lpNewFileName, + UInt32 dwFlags); + [DllImport("advapi32.dll", SetLastError = true)] + private static extern Boolean SetFileSecurityW( + [MarshalAs(UnmanagedType.LPWStr)] String FileName, + Int32 SecurityInformation, + Byte[] SecurityDescriptor); + [DllImport("advapi32.dll", SetLastError = true)] + private static extern Boolean SetKernelObjectSecurity( + IntPtr Handle, + Int32 SecurityInformation, + Byte[] SecurityDescriptor); + } + + private class DirectoryEntryComparer : IComparer + { + public int Compare(object x, object y) + { + return String.Compare( + (String)((DictionaryEntry)x).Key, + (String)((DictionaryEntry)y).Key); + } + } + private static DirectoryEntryComparer _DirectoryEntryComparer = + new DirectoryEntryComparer(); + + public Ptfs(String Path0) + { + _Path = Path.GetFullPath(Path0); + if (_Path.EndsWith("\\")) + _Path = _Path.Substring(0, _Path.Length - 1); + } + public String ConcatPath(String FileName) + { + return _Path + FileName; + } + public override Int32 ExceptionHandler(Exception ex) + { + Int32 HResult = ex.HResult; /* needs Framework 4.5 */ + if (0x80070000 == (HResult & 0xFFFF0000)) + return NtStatusFromWin32((UInt32)HResult & 0xFFFF); + return STATUS_UNEXPECTED_IO_ERROR; + } + public override Int32 Init(Object Host0) + { + FileSystemHost Host = (FileSystemHost)Host0; + Host.SectorSize = ALLOCATION_UNIT; + Host.SectorsPerAllocationUnit = 1; + Host.MaxComponentLength = 255; + Host.FileInfoTimeout = 1000; + Host.CaseSensitiveSearch = false; + Host.CasePreservedNames = true; + Host.UnicodeOnDisk = true; + Host.PersistentAcls = true; + Host.PostCleanupWhenModifiedOnly = true; + Host.PassQueryDirectoryPattern = true; + Host.FlushAndPurgeOnCleanup = true; + Host.VolumeCreationTime = (UInt64)File.GetCreationTimeUtc(_Path).ToFileTimeUtc(); + Host.VolumeSerialNumber = 0; + return STATUS_SUCCESS; + } + public override Int32 GetVolumeInfo( + out VolumeInfo VolumeInfo) + { + VolumeInfo = default(VolumeInfo); + try + { + DriveInfo Info = new DriveInfo(_Path); + VolumeInfo.TotalSize = (UInt64)Info.TotalSize; + VolumeInfo.FreeSize = (UInt64)Info.TotalFreeSpace; + } + catch (ArgumentException) + { + /* + * DriveInfo only supports drives and does not support UNC paths. + * It would be better to use GetDiskFreeSpaceEx here. + */ + } + return STATUS_SUCCESS; + } + public override Int32 GetSecurityByName( + String FileName, + out UInt32 FileAttributes/* or ReparsePointIndex */, + ref Byte[] SecurityDescriptor) + { + FileName = ConcatPath(FileName); + System.IO.FileInfo Info = new System.IO.FileInfo(FileName); + FileAttributes = (UInt32)Info.Attributes; + if (null != SecurityDescriptor) + SecurityDescriptor = Info.GetAccessControl().GetSecurityDescriptorBinaryForm(); + return STATUS_SUCCESS; + } + public override Int32 Create( + String FileName, + UInt32 CreateOptions, + UInt32 GrantedAccess, + UInt32 FileAttributes, + Byte[] SecurityDescriptor, + UInt64 AllocationSize, + out Object FileNode, + out Object FileDesc0, + out FileInfo FileInfo, + out String NormalizedName) + { + FileDesc FileDesc = null; + try + { + FileName = ConcatPath(FileName); + if (0 == (CreateOptions & FILE_DIRECTORY_FILE)) + { + FileSecurity Security = null; + if (null != SecurityDescriptor) + { + Security = new FileSecurity(); + Security.SetSecurityDescriptorBinaryForm(SecurityDescriptor); + } + FileDesc = new FileDesc( + new FileStream( + FileName, + FileMode.CreateNew, + (FileSystemRights)GrantedAccess | FileSystemRights.WriteAttributes, + FileShare.Read | FileShare.Write | FileShare.Delete, + 4096, + 0, + Security)); + FileDesc.SetFileAttributes(FileAttributes | (UInt32)System.IO.FileAttributes.Archive); + } + else + { + if (Directory.Exists(FileName)) + ThrowIoExceptionWithNtStatus(STATUS_OBJECT_NAME_COLLISION); + DirectorySecurity Security = null; + if (null != SecurityDescriptor) + { + Security = new DirectorySecurity(); + Security.SetSecurityDescriptorBinaryForm(SecurityDescriptor); + } + FileDesc = new FileDesc( + Directory.CreateDirectory(FileName, Security)); + FileDesc.SetFileAttributes(FileAttributes); + } + FileNode = default(Object); + FileDesc0 = FileDesc; + NormalizedName = default(String); + return FileDesc.GetFileInfo(out FileInfo); + } + catch + { + if (null != FileDesc && null != FileDesc.Stream) + FileDesc.Stream.Dispose(); + throw; + } + } + public override Int32 Open( + String FileName, + UInt32 CreateOptions, + UInt32 GrantedAccess, + out Object FileNode, + out Object FileDesc0, + out FileInfo FileInfo, + out String NormalizedName) + { + FileDesc FileDesc = null; + try + { + FileName = ConcatPath(FileName); + if (!Directory.Exists(FileName)) + { + FileDesc = new FileDesc( + new FileStream( + FileName, + FileMode.Open, + (FileSystemRights)GrantedAccess, + FileShare.Read | FileShare.Write | FileShare.Delete, + 4096, + 0)); + } + else + { + FileDesc = new FileDesc( + new DirectoryInfo(FileName)); + } + FileNode = default(Object); + FileDesc0 = FileDesc; + NormalizedName = default(String); + return FileDesc.GetFileInfo(out FileInfo); + } + catch + { + if (null != FileDesc && null != FileDesc.Stream) + FileDesc.Stream.Dispose(); + throw; + } + } + public override Int32 Overwrite( + Object FileNode, + Object FileDesc0, + UInt32 FileAttributes, + Boolean ReplaceFileAttributes, + UInt64 AllocationSize, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (ReplaceFileAttributes) + FileDesc.SetFileAttributes(FileAttributes | + (UInt32)System.IO.FileAttributes.Archive); + else if (0 != FileAttributes) + FileDesc.SetFileAttributes(FileDesc.GetFileAttributes() | FileAttributes | + (UInt32)System.IO.FileAttributes.Archive); + FileDesc.Stream.SetLength(0); + return FileDesc.GetFileInfo(out FileInfo); + } + public override void Cleanup( + Object FileNode, + Object FileDesc0, + String FileName, + UInt32 Flags) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (0 != (Flags & CleanupDelete)) + { + FileDesc.SetDisposition(true); + if (null != FileDesc.Stream) + FileDesc.Stream.Dispose(); + } + } + public override void Close( + Object FileNode, + Object FileDesc0) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (null != FileDesc.Stream) + FileDesc.Stream.Dispose(); + } + public override Int32 Read( + Object FileNode, + Object FileDesc0, + IntPtr Buffer, + UInt64 Offset, + UInt32 Length, + out UInt32 PBytesTransferred) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (Offset >= (UInt64)FileDesc.Stream.Length) + ThrowIoExceptionWithNtStatus(STATUS_END_OF_FILE); + Byte[] Bytes = new byte[Length]; + FileDesc.Stream.Seek((Int64)Offset, SeekOrigin.Begin); + PBytesTransferred = (UInt32)FileDesc.Stream.Read(Bytes, 0, Bytes.Length); + Marshal.Copy(Bytes, 0, Buffer, Bytes.Length); + return STATUS_SUCCESS; + } + public override Int32 Write( + Object FileNode, + Object FileDesc0, + IntPtr Buffer, + UInt64 Offset, + UInt32 Length, + Boolean WriteToEndOfFile, + Boolean ConstrainedIo, + out UInt32 PBytesTransferred, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (ConstrainedIo) + { + if (Offset >= (UInt64)FileDesc.Stream.Length) + { + PBytesTransferred = default(UInt32); + FileInfo = default(FileInfo); + return STATUS_SUCCESS; + } + if (Offset + Length > (UInt64)FileDesc.Stream.Length) + Length = (UInt32)((UInt64)FileDesc.Stream.Length - Offset); + } + Byte[] Bytes = new byte[Length]; + Marshal.Copy(Buffer, Bytes, 0, Bytes.Length); + if (!WriteToEndOfFile) + FileDesc.Stream.Seek((Int64)Offset, SeekOrigin.Begin); + FileDesc.Stream.Write(Bytes, 0, Bytes.Length); + PBytesTransferred = (UInt32)Bytes.Length; + return FileDesc.GetFileInfo(out FileInfo); + } + public override Int32 Flush( + Object FileNode, + Object FileDesc0, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (null == FileDesc) + { + /* we do not flush the whole volume, so just return SUCCESS */ + FileInfo = default(FileInfo); + return STATUS_SUCCESS; + } + FileDesc.Stream.Flush(true); + return FileDesc.GetFileInfo(out FileInfo); + } + public override Int32 GetFileInfo( + Object FileNode, + Object FileDesc0, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + return FileDesc.GetFileInfo(out FileInfo); + } + public override Int32 SetBasicInfo( + Object FileNode, + Object FileDesc0, + UInt32 FileAttributes, + UInt64 CreationTime, + UInt64 LastAccessTime, + UInt64 LastWriteTime, + UInt64 ChangeTime, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + FileDesc.SetBasicInfo(FileAttributes, CreationTime, LastAccessTime, LastWriteTime); + return FileDesc.GetFileInfo(out FileInfo); + } + public override Int32 SetFileSize( + Object FileNode, + Object FileDesc0, + UInt64 NewSize, + Boolean SetAllocationSize, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (!SetAllocationSize || (UInt64)FileDesc.Stream.Length > NewSize) + { + /* + * "FileInfo.FileSize > NewSize" explanation: + * Ptfs does not support allocation size. However if the new AllocationSize + * is less than the current FileSize we must truncate the file. + */ + FileDesc.Stream.SetLength((Int64)NewSize); + } + return FileDesc.GetFileInfo(out FileInfo); + } + public override Int32 CanDelete( + Object FileNode, + Object FileDesc0, + String FileName) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + FileDesc.SetDisposition(false); + return STATUS_SUCCESS; + } + public override Int32 Rename( + Object FileNode, + Object FileDesc0, + String FileName, + String NewFileName, + Boolean ReplaceIfExists) + { + FileName = ConcatPath(FileName); + NewFileName = ConcatPath(NewFileName); + FileDesc.Rename(FileName, NewFileName, ReplaceIfExists); + return STATUS_SUCCESS; + } + public override Int32 GetSecurity( + Object FileNode, + Object FileDesc0, + ref Byte[] SecurityDescriptor) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + SecurityDescriptor = FileDesc.GetSecurityDescriptor(); + return STATUS_SUCCESS; + } + public override Int32 SetSecurity( + Object FileNode, + Object FileDesc0, + AccessControlSections Sections, + Byte[] SecurityDescriptor) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + FileDesc.SetSecurityDescriptor(Sections, SecurityDescriptor); + return STATUS_SUCCESS; + } + public override Boolean ReadDirectoryEntry( + Object FileNode, + Object FileDesc0, + String Pattern, + String Marker, + ref Object Context, + out String FileName, + out FileInfo FileInfo) + { + FileDesc FileDesc = (FileDesc)FileDesc0; + if (null == FileDesc.FileSystemInfos) + { + if (null != Pattern) + Pattern = Pattern.Replace('<', '*').Replace('>', '?').Replace('"', '.'); + else + Pattern = "*"; + IEnumerable Enum = FileDesc.DirInfo.EnumerateFileSystemInfos(Pattern); + SortedList List = new SortedList(); + if (null != FileDesc.DirInfo && null != FileDesc.DirInfo.Parent) + { + List.Add(".", FileDesc.DirInfo); + List.Add("..", FileDesc.DirInfo.Parent); + } + foreach (FileSystemInfo Info in Enum) + List.Add(Info.Name, Info); + FileDesc.FileSystemInfos = new DictionaryEntry[List.Count]; + List.CopyTo(FileDesc.FileSystemInfos, 0); + } + int Index; + if (null == Context) + { + Index = 0; + if (null != Marker) + { + Index = Array.BinarySearch(FileDesc.FileSystemInfos, + new DictionaryEntry(Marker, null), + _DirectoryEntryComparer); + if (0 <= Index) + Index++; + else + Index = ~Index; + } + } + else + Index = (int)Context; + if (FileDesc.FileSystemInfos.Length > Index) + { + Context = Index + 1; + FileName = (String)FileDesc.FileSystemInfos[Index].Key; + FileDesc.GetFileInfoFromFileSystemInfo( + (FileSystemInfo)FileDesc.FileSystemInfos[Index].Value, + out FileInfo); + return true; + } + else + { + FileName = default(String); + FileInfo = default(FileInfo); + return false; + } + } + + private String _Path; + } + + class PtfsService : Service + { + private class CommandLineUsageException : Exception + { + public CommandLineUsageException(String Message = null) : base(Message) + { + HasMessage = null != Message; + } + + public bool HasMessage; + } + + private const String PROGNAME = "passthrough-dotnet"; + + public PtfsService() : base("PtfsService") + { + } + + protected override void OnStart(String[] Args) + { + try + { + String DebugLogFile = null; + UInt32 DebugFlags = 0; + String VolumePrefix = null; + String PassThrough = null; + String MountPoint = null; + IntPtr DebugLogHandle = (IntPtr)(-1); + FileSystemHost Host = null; + Ptfs Ptfs = null; + int I; + + for (I = 1; Args.Length > I; I++) + { + String Arg = Args[I]; + if ('-' != Arg[0]) + break; + switch (Arg[1]) + { + case '?': + throw new CommandLineUsageException(); + case 'd': + argtol(Args, ref I, ref DebugFlags); + break; + case 'D': + argtos(Args, ref I, ref DebugLogFile); + break; + case 'm': + argtos(Args, ref I, ref MountPoint); + break; + case 'p': + argtos(Args, ref I, ref PassThrough); + break; + case 'u': + argtos(Args, ref I, ref VolumePrefix); + break; + default: + throw new CommandLineUsageException(); + } + } + + if (Args.Length > I) + throw new CommandLineUsageException(); + + if (null == PassThrough && null != VolumePrefix) + { + I = VolumePrefix.IndexOf('\\'); + if (-1 != I && VolumePrefix.Length > I && '\\' != VolumePrefix[I + 1]) + { + I = VolumePrefix.IndexOf('\\', I + 1); + if (-1 != I && + VolumePrefix.Length > I + 1 && + ( + ('A' <= VolumePrefix[I + 1] && VolumePrefix[I + 1] <= 'Z') || + ('a' <= VolumePrefix[I + 1] && VolumePrefix[I + 1] <= 'z') + ) && + '$' == VolumePrefix[I + 2]) + { + PassThrough = String.Format("{0}:{1}", VolumePrefix[I + 1], VolumePrefix.Substring(I + 3)); + } + } + } + + if (null == PassThrough || null == MountPoint) + throw new CommandLineUsageException(); + + if (null != DebugLogFile) + if (0 > FileSystemHost.SetDebugLogFile(DebugLogFile)) + throw new CommandLineUsageException("cannot open debug log file"); + + Host = new FileSystemHost(Ptfs = new Ptfs(PassThrough)); + Host.Prefix = VolumePrefix; + if (0 > Host.Mount(MountPoint, null, true, DebugFlags)) + throw new IOException("cannot mount file system"); + MountPoint = Host.MountPoint(); + _Host = Host; + + Log(EVENTLOG_INFORMATION_TYPE, String.Format("{0}{1}{2} -p {3} -m {4}", + PROGNAME, + null != VolumePrefix && 0 < VolumePrefix.Length ? " -u " : "", + null != VolumePrefix && 0 < VolumePrefix.Length ? VolumePrefix : "", + PassThrough, + MountPoint)); + } + catch (CommandLineUsageException ex) + { + Log(EVENTLOG_ERROR_TYPE, String.Format( + "{0}" + + "usage: {1} OPTIONS\n" + + "\n" + + "options:\n" + + " -d DebugFlags [-1: enable all debug logs]\n" + + " -D DebugLogFile [file path; use - for stderr]\n" + + " -u \\Server\\Share [UNC prefix (single backslash)]\n" + + " -p Directory [directory to expose as pass through file system]\n" + + " -m MountPoint [X:|*|directory]\n", + ex.HasMessage ? ex.Message + "\n" : "", + PROGNAME)); + throw; + } + catch (Exception ex) + { + Log(EVENTLOG_ERROR_TYPE, String.Format("{0}", ex.Message)); + throw; + } + } + protected override void OnStop() + { + _Host.Unmount(); + _Host = null; + } + + private static void argtos(String[] Args, ref int I, ref String V) + { + if (Args.Length > ++I) + V = Args[I]; + else + throw new CommandLineUsageException(); + } + private static void argtol(String[] Args, ref int I, ref UInt32 V) + { + Int32 R; + if (Args.Length > ++I) + V = Int32.TryParse(Args[I], out R) ? (UInt32)R : V; + else + throw new CommandLineUsageException(); + } + + private FileSystemHost _Host; + } + + class Program + { + static void Main(string[] args) + { + Environment.ExitCode = new PtfsService().Run(); + } + } +} diff --git a/3rd_party/winfsp-1.10/samples/passthrough-dotnet/passthrough-dotnet.csproj b/3rd_party/winfsp-1.10/samples/passthrough-dotnet/passthrough-dotnet.csproj new file mode 100644 index 00000000..b9c7e15c --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-dotnet/passthrough-dotnet.csproj @@ -0,0 +1,84 @@ + + + + + Debug + AnyCPU + {6EC13EBC-BD7E-4997-9B29-49D5F06103A6} + Exe + passthrough + passthrough-dotnet + v4.5.2 + 512 + true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + AnyCPU + true + full + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\ + $(BaseIntermediateOutputPath)$(Configuration)\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\ + $(BaseIntermediateOutputPath)$(Configuration)\ + TRACE + prompt + 4 + + + + + False + $(MSBuildProgramFiles32)\WinFsp\bin\winfsp-msil.dll + + + + + + + + False + Microsoft .NET Framework 4.5.2 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/passthrough-dotnet/passthrough-dotnet.sln b/3rd_party/winfsp-1.10/samples/passthrough-dotnet/passthrough-dotnet.sln new file mode 100644 index 00000000..7f940a60 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-dotnet/passthrough-dotnet.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "passthrough-dotnet", "passthrough-dotnet.csproj", "{6EC13EBC-BD7E-4997-9B29-49D5F06103A6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6EC13EBC-BD7E-4997-9B29-49D5F06103A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6EC13EBC-BD7E-4997-9B29-49D5F06103A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6EC13EBC-BD7E-4997-9B29-49D5F06103A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6EC13EBC-BD7E-4997-9B29-49D5F06103A6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse/Makefile b/3rd_party/winfsp-1.10/samples/passthrough-fuse/Makefile new file mode 100644 index 00000000..3d403593 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse/Makefile @@ -0,0 +1,18 @@ +usage: + @echo "make cygfuse|winfsp-fuse" 1>&2 + @echo "" 1>&2 + @echo " cygfuse Link with CYGFUSE" 1>&2 + @echo " winfsp-fuse Link with WinFsp-FUSE" 1>&2 + @exit 2 + +cygfuse: passthrough-cygfuse + +winfsp-fuse: passthrough-winfsp-fuse + +passthrough-cygfuse: passthrough-fuse.c + gcc $^ -o $@ -g -Wall `pkg-config fuse --cflags --libs` + +passthrough-winfsp-fuse: export PKG_CONFIG_PATH=$(PWD)/winfsp.install/lib +passthrough-winfsp-fuse: passthrough-fuse.c + ln -nsf "`regtool --wow32 get '/HKLM/Software/WinFsp/InstallDir' | cygpath -au -f -`" winfsp.install + gcc $^ -o $@ -g -Wall `pkg-config fuse --cflags --libs` diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse/README.md b/3rd_party/winfsp-1.10/samples/passthrough-fuse/README.md new file mode 100644 index 00000000..e7f43f27 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse/README.md @@ -0,0 +1,7 @@ +`Passthrough-fuse` is a simple FUSE file system that passes all file system operations to an underlying file system. + +It can be built with the following tools: + +- Using Visual Studio (`winfsp.sln`). +- Using Cygwin GCC and linking directly with the WinFsp DLL (`make winfsp-fuse`). +- Using Cygwin GCC and linking to CYGFUSE (`make cygfuse`). diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse/passthrough-fuse.c b/3rd_party/winfsp-1.10/samples/passthrough-fuse/passthrough-fuse.c new file mode 100644 index 00000000..5cad0897 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse/passthrough-fuse.c @@ -0,0 +1,417 @@ +/** + * @file passthrough-fuse.c + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#include +#include +#include +#include +#include + +#include + +#if defined(_WIN64) || defined(_WIN32) +#include "winposix.h" +#else +#include +#include +#endif + +#define PTFS_UTIMENS + +#define FSNAME "passthrough" +#define PROGNAME "passthrough-fuse" + +#define concat_path(ptfs, fn, fp) (sizeof fp > (unsigned)snprintf(fp, sizeof fp, "%s%s", ptfs->rootdir, fn)) + +#define fi_dirbit (0x8000000000000000ULL) +#define fi_fh(fi, MASK) ((fi)->fh & (MASK)) +#define fi_setfh(fi, FH, MASK) ((fi)->fh = (intptr_t)(FH) | (MASK)) +#define fi_fd(fi) (fi_fh(fi, fi_dirbit) ? \ + dirfd((DIR *)(intptr_t)fi_fh(fi, ~fi_dirbit)) : (int)fi_fh(fi, ~fi_dirbit)) +#define fi_dirp(fi) ((DIR *)(intptr_t)fi_fh(fi, ~fi_dirbit)) +#define fi_setfd(fi, fd) (fi_setfh(fi, fd, 0)) +#define fi_setdirp(fi, dirp) (fi_setfh(fi, dirp, fi_dirbit)) + +#define ptfs_impl_fullpath(n) \ + char full ## n[PATH_MAX]; \ + if (!concat_path(((PTFS *)fuse_get_context()->private_data), n, full ## n))\ + return -ENAMETOOLONG; \ + n = full ## n + +typedef struct +{ + const char *rootdir; +} PTFS; + +static int ptfs_getattr(const char *path, struct fuse_stat *stbuf) +{ + ptfs_impl_fullpath(path); + + return -1 != lstat(path, stbuf) ? 0 : -errno; +} + +static int ptfs_mkdir(const char *path, fuse_mode_t mode) +{ + ptfs_impl_fullpath(path); + + return -1 != mkdir(path, mode) ? 0 : -errno; +} + +static int ptfs_unlink(const char *path) +{ + ptfs_impl_fullpath(path); + + return -1 != unlink(path) ? 0 : -errno; +} + +static int ptfs_rmdir(const char *path) +{ + ptfs_impl_fullpath(path); + + return -1 != rmdir(path) ? 0 : -errno; +} + +static int ptfs_rename(const char *oldpath, const char *newpath) +{ + ptfs_impl_fullpath(newpath); + ptfs_impl_fullpath(oldpath); + + return -1 != rename(oldpath, newpath) ? 0 : -errno; +} + +static int ptfs_chmod(const char *path, fuse_mode_t mode) +{ + ptfs_impl_fullpath(path); + + return -1 != chmod(path, mode) ? 0 : -errno; +} + +static int ptfs_chown(const char *path, fuse_uid_t uid, fuse_gid_t gid) +{ + ptfs_impl_fullpath(path); + + return -1 != lchown(path, uid, gid) ? 0 : -errno; +} + +static int ptfs_truncate(const char *path, fuse_off_t size) +{ + ptfs_impl_fullpath(path); + + return -1 != truncate(path, size) ? 0 : -errno; +} + +#if !defined(PTFS_UTIMENS) +static int ptfs_utime(const char *path, struct fuse_utimbuf *timbuf) +{ + ptfs_impl_fullpath(path); + + return -1 != utime(path, timbuf) ? 0 : -errno; +} +#endif + +static int ptfs_open(const char *path, struct fuse_file_info *fi) +{ + ptfs_impl_fullpath(path); + + int fd; + return -1 != (fd = open(path, fi->flags)) ? (fi_setfd(fi, fd), 0) : -errno; +} + +static int ptfs_read(const char *path, char *buf, size_t size, fuse_off_t off, + struct fuse_file_info *fi) +{ + int fd = fi_fd(fi); + + int nb; + return -1 != (nb = pread(fd, buf, size, off)) ? nb : -errno; +} + +static int ptfs_write(const char *path, const char *buf, size_t size, fuse_off_t off, + struct fuse_file_info *fi) +{ + int fd = fi_fd(fi); + + int nb; + return -1 != (nb = pwrite(fd, buf, size, off)) ? nb : -errno; +} + +static int ptfs_statfs(const char *path, struct fuse_statvfs *stbuf) +{ + ptfs_impl_fullpath(path); + + return -1 != statvfs(path, stbuf) ? 0 : -errno; +} + +static int ptfs_release(const char *path, struct fuse_file_info *fi) +{ + int fd = fi_fd(fi); + + close(fd); + return 0; +} + +static int ptfs_fsync(const char *path, int datasync, struct fuse_file_info *fi) +{ + int fd = fi_fd(fi); + + return -1 != fsync(fd) ? 0 : -errno; +} + +static int ptfs_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) +{ + ptfs_impl_fullpath(path); + + return -1 != lsetxattr(path, name, value, size, flags) ? 0 : -errno; +} + +static int ptfs_getxattr(const char *path, const char *name, char *value, size_t size) +{ + ptfs_impl_fullpath(path); + + int nb; + return -1 != (nb = lgetxattr(path, name, value, size)) ? nb : -errno; +} + +static int ptfs_listxattr(const char *path, char *namebuf, size_t size) +{ + ptfs_impl_fullpath(path); + + int nb; + return -1 != (nb = llistxattr(path, namebuf, size)) ? nb : -errno; +} + +static int ptfs_removexattr(const char *path, const char *name) +{ + ptfs_impl_fullpath(path); + + return -1 != lremovexattr(path, name) ? 0 : -errno; +} + +static int ptfs_opendir(const char *path, struct fuse_file_info *fi) +{ + ptfs_impl_fullpath(path); + + DIR *dirp; + return 0 != (dirp = opendir(path)) ? (fi_setdirp(fi, dirp), 0) : -errno; +} + +static int ptfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, fuse_off_t off, + struct fuse_file_info *fi) +{ + DIR *dirp = fi_dirp(fi); + struct dirent *de; + + rewinddir(dirp); + for (;;) + { + errno = 0; + if (0 == (de = readdir(dirp))) + break; +#if defined(_WIN64) || defined(_WIN32) + if (0 != filler(buf, de->d_name, &de->d_stat, 0)) +#else + if (0 != filler(buf, de->d_name, 0, 0)) +#endif + return -ENOMEM; + } + + return -errno; +} + +static int ptfs_releasedir(const char *path, struct fuse_file_info *fi) +{ + DIR *dirp = fi_dirp(fi); + + return -1 != closedir(dirp) ? 0 : -errno; +} + +static void *ptfs_init(struct fuse_conn_info *conn) +{ +#if defined(_WIN64) || defined(_WIN32) +#if defined(FSP_FUSE_CAP_READDIR_PLUS) + conn->want |= (conn->capable & FSP_FUSE_CAP_READDIR_PLUS); +#endif + +#if defined(FSP_FUSE_USE_STAT_EX) && defined(FSP_FUSE_CAP_STAT_EX) + conn->want |= (conn->capable & FSP_FUSE_CAP_STAT_EX); +#endif + +#if defined(FSP_FUSE_CAP_CASE_INSENSITIVE) + conn->want |= (conn->capable & FSP_FUSE_CAP_CASE_INSENSITIVE); +#endif +#endif + + return fuse_get_context()->private_data; +} + +static int ptfs_create(const char *path, fuse_mode_t mode, struct fuse_file_info *fi) +{ + ptfs_impl_fullpath(path); + + int fd; + return -1 != (fd = open(path, fi->flags, mode)) ? (fi_setfd(fi, fd), 0) : -errno; +} + +static int ptfs_ftruncate(const char *path, fuse_off_t off, struct fuse_file_info *fi) +{ + int fd = fi_fd(fi); + + return -1 != ftruncate(fd, off) ? 0 : -errno; +} + +static int ptfs_fgetattr(const char *path, struct fuse_stat *stbuf, struct fuse_file_info *fi) +{ + int fd = fi_fd(fi); + + return -1 != fstat(fd, stbuf) ? 0 : -errno; +} + +#if defined(PTFS_UTIMENS) +static int ptfs_utimens(const char *path, const struct fuse_timespec tv[2]) +{ + ptfs_impl_fullpath(path); + + return -1 != utimensat(AT_FDCWD, path, tv, AT_SYMLINK_NOFOLLOW) ? 0 : -errno; +} +#endif + +#if defined(_WIN64) || defined(_WIN32) +static int ptfs_setcrtime(const char *path, const struct fuse_timespec *tv) +{ + ptfs_impl_fullpath(path); + + return -1 != setcrtime(path, tv) ? 0 : -errno; +} +#endif + +#if defined(FSP_FUSE_USE_STAT_EX) +static int ptfs_chflags(const char *path, uint32_t flags) +{ + ptfs_impl_fullpath(path); + + return -1 != lchflags(path, flags) ? 0 : -errno; +} +#endif + +static struct fuse_operations ptfs_ops = +{ + .getattr = ptfs_getattr, + .mkdir = ptfs_mkdir, + .unlink = ptfs_unlink, + .rmdir = ptfs_rmdir, + .rename = ptfs_rename, + .chmod = ptfs_chmod, + .chown = ptfs_chown, + .truncate = ptfs_truncate, +#if !defined(PTFS_UTIMENS) + .utime = ptfs_utime, +#endif + .open = ptfs_open, + .read = ptfs_read, + .write = ptfs_write, + .statfs = ptfs_statfs, + .release = ptfs_release, + .fsync = ptfs_fsync, + .setxattr = ptfs_setxattr, + .getxattr = ptfs_getxattr, + .listxattr = ptfs_listxattr, + .removexattr = ptfs_removexattr, + .opendir = ptfs_opendir, + .readdir = ptfs_readdir, + .releasedir = ptfs_releasedir, + .init = ptfs_init, + .create = ptfs_create, + .ftruncate = ptfs_ftruncate, + .fgetattr = ptfs_fgetattr, +#if defined(PTFS_UTIMENS) + .utimens = ptfs_utimens, +#endif +#if defined(_WIN64) || defined(_WIN32) + .setcrtime = ptfs_setcrtime, +#endif +#if defined(FSP_FUSE_USE_STAT_EX) + .chflags = ptfs_chflags, +#endif +}; + +static void usage(void) +{ + fprintf(stderr, "usage: " PROGNAME " [FUSE options] rootdir mountpoint\n"); + exit(2); +} + +int main(int argc, char *argv[]) +{ + PTFS ptfs = { 0 }; + + if (3 <= argc && '-' != argv[argc - 2][0] && '-' != argv[argc - 1][0]) + { + ptfs.rootdir = realpath(argv[argc - 2], 0); /* memory freed at process end */ + argv[argc - 2] = argv[argc - 1]; + argc--; + } + +#if defined(_WIN64) || defined(_WIN32) + /* + * When building for Windows (rather than Cygwin or POSIX OS) + * allow the path to be specified using the --VolumePrefix + * switch using the syntax \\passthrough-fuse\C$\Path. This + * allows us to run the file system under the WinFsp.Launcher + * and start it using commands like: + * + * net use z: \\passthrough-fuse\C$\Path + */ + if (0 == ptfs.rootdir) + for (int argi = 1; argc > argi; argi++) + { + int strncmp(const char *a, const char *b, size_t length); + char *strchr(const char *s, int c); + char *p = 0; + + if (0 == strncmp("--UNC=", argv[argi], sizeof "--UNC=" - 1)) + p = argv[argi] + sizeof "--UNC=" - 1; + else if (0 == strncmp("--VolumePrefix=", argv[argi], sizeof "--VolumePrefix=" - 1)) + p = argv[argi] + sizeof "--VolumePrefix=" - 1; + + if (0 != p && '\\' != p[1]) + { + p = strchr(p + 1, '\\'); + if (0 != p && + ( + ('A' <= p[1] && p[1] <= 'Z') || + ('a' <= p[1] && p[1] <= 'z') + ) && + '$' == p[2]) + { + p[2] = ':'; + ptfs.rootdir = realpath(p + 1, 0); /* memory freed at process end */ + p[2] = '$'; + break; + } + } + } +#endif + + if (0 == ptfs.rootdir) + usage(); + + return fuse_main(argc, argv, &ptfs_ops, &ptfs); +} diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse/passthrough-fuse.sln b/3rd_party/winfsp-1.10/samples/passthrough-fuse/passthrough-fuse.sln new file mode 100644 index 00000000..83a5c6c8 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse/passthrough-fuse.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "passthrough-fuse", "passthrough-fuse.vcxproj", "{C753851C-142F-4AAD-B2F7-CBF905C2A600}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C753851C-142F-4AAD-B2F7-CBF905C2A600}.Debug|x64.ActiveCfg = Debug|x64 + {C753851C-142F-4AAD-B2F7-CBF905C2A600}.Debug|x64.Build.0 = Debug|x64 + {C753851C-142F-4AAD-B2F7-CBF905C2A600}.Debug|x86.ActiveCfg = Debug|Win32 + {C753851C-142F-4AAD-B2F7-CBF905C2A600}.Debug|x86.Build.0 = Debug|Win32 + {C753851C-142F-4AAD-B2F7-CBF905C2A600}.Release|x64.ActiveCfg = Release|x64 + {C753851C-142F-4AAD-B2F7-CBF905C2A600}.Release|x64.Build.0 = Release|x64 + {C753851C-142F-4AAD-B2F7-CBF905C2A600}.Release|x86.ActiveCfg = Release|Win32 + {C753851C-142F-4AAD-B2F7-CBF905C2A600}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse/passthrough-fuse.vcxproj b/3rd_party/winfsp-1.10/samples/passthrough-fuse/passthrough-fuse.vcxproj new file mode 100644 index 00000000..bdca6304 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse/passthrough-fuse.vcxproj @@ -0,0 +1,190 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {C753851C-142F-4AAD-B2F7-CBF905C2A600} + Win32Proj + passthroughfuse + $(LatestTargetPlatformVersion) + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + + + + Level3 + Disabled + FSP_FUSE_USE_STAT_EX;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + 4996 + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + Level3 + Disabled + FSP_FUSE_USE_STAT_EX;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + 4996 + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + FSP_FUSE_USE_STAT_EX;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + 4996 + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + FSP_FUSE_USE_STAT_EX;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + 4996 + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + + + + + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse/passthrough-fuse.vcxproj.filters b/3rd_party/winfsp-1.10/samples/passthrough-fuse/passthrough-fuse.vcxproj.filters new file mode 100644 index 00000000..d5920b89 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse/passthrough-fuse.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source + + + Source + + + + + Source + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse/winposix.c b/3rd_party/winfsp-1.10/samples/passthrough-fuse/winposix.c new file mode 100644 index 00000000..54097142 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse/winposix.c @@ -0,0 +1,892 @@ +/** + * @file winposix.c + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +/* + * This is a very simple Windows POSIX layer. It handles all the POSIX + * file API's required to implement passthrough-fuse in POSIX, however + * the API handling is rather unsophisticated. + * + * Ways to improve it: use the FspPosix* API's to properly handle + * file names and security. + */ + +#include +#include +#include +#include "winposix.h" + +#pragma comment(lib, "ntdll.lib") + +typedef struct _FILE_GET_EA_INFORMATION +{ + ULONG NextEntryOffset; + UCHAR EaNameLength; + CHAR EaName[1]; +} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION; + +NTSYSAPI NTSTATUS NTAPI NtQueryEaFile( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID Buffer, + IN ULONG Length, + IN BOOLEAN ReturnSingleEntry, + IN PVOID EaList OPTIONAL, + IN ULONG EaListLength, + IN PULONG EaIndex OPTIONAL, + IN BOOLEAN RestartScan); +NTSYSAPI NTSTATUS NTAPI NtSetEaFile( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PVOID EaBuffer, + IN ULONG EaBufferSize); +#define NEXT_EA(Ea, EaEnd) \ + (0 != (Ea)->NextEntryOffset ? (PVOID)((PUINT8)(Ea) + (Ea)->NextEntryOffset) : (EaEnd)) + +struct _DIR +{ + HANDLE h, fh; + struct dirent de; + char path[]; +}; + +#if defined(FSP_FUSE_USE_STAT_EX) +static inline uint32_t MapFileAttributesToFlags(UINT32 FileAttributes) +{ + uint32_t flags = 0; + + if (FileAttributes & FILE_ATTRIBUTE_READONLY) + flags |= FSP_FUSE_UF_READONLY; + if (FileAttributes & FILE_ATTRIBUTE_HIDDEN) + flags |= FSP_FUSE_UF_HIDDEN; + if (FileAttributes & FILE_ATTRIBUTE_SYSTEM) + flags |= FSP_FUSE_UF_SYSTEM; + if (FileAttributes & FILE_ATTRIBUTE_ARCHIVE) + flags |= FSP_FUSE_UF_ARCHIVE; + + return flags; +} + +static inline UINT32 MapFlagsToFileAttributes(uint32_t flags) +{ + UINT32 FileAttributes = 0; + + if (flags & FSP_FUSE_UF_READONLY) + FileAttributes |= FILE_ATTRIBUTE_READONLY; + if (flags & FSP_FUSE_UF_HIDDEN) + FileAttributes |= FILE_ATTRIBUTE_HIDDEN; + if (flags & FSP_FUSE_UF_SYSTEM) + FileAttributes |= FILE_ATTRIBUTE_SYSTEM; + if (flags & FSP_FUSE_UF_ARCHIVE) + FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; + + return FileAttributes; +} +#endif + +static int maperror(int winerrno); + +static inline void *error0(void) +{ + errno = maperror(GetLastError()); + return 0; +} + +static inline int error(void) +{ + errno = maperror(GetLastError()); + return -1; +} + +char *realpath(const char *path, char *resolved) +{ + char *result; + + if (0 == resolved) + { + result = malloc(PATH_MAX); /* sets errno */ + if (0 == result) + return 0; + } + else + result = resolved; + + int err = 0; + DWORD len = GetFullPathNameA(path, PATH_MAX, result, 0); + if (0 == len) + err = GetLastError(); + else if (PATH_MAX < len) + err = ERROR_INVALID_PARAMETER; + + if (0 == err) + { + HANDLE h = CreateFileA(result, + FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE != h) + CloseHandle(h); + else + err = GetLastError(); + } + + if (0 != err) + { + if (result != resolved) + free(result); + + errno = maperror(err); + result = 0; + } + + return result; +} + +int statvfs(const char *path, struct fuse_statvfs *stbuf) +{ + char root[PATH_MAX]; + DWORD + VolumeSerialNumber, + MaxComponentLength, + SectorsPerCluster, + BytesPerSector, + NumberOfFreeClusters, + TotalNumberOfClusters; + + if (!GetVolumePathNameA(path, root, PATH_MAX) || + !GetVolumeInformationA(root, 0, 0, &VolumeSerialNumber, &MaxComponentLength, 0, 0, 0) || + !GetDiskFreeSpaceA(root, &SectorsPerCluster, &BytesPerSector, + &NumberOfFreeClusters, &TotalNumberOfClusters)) + { + return error(); + } + + memset(stbuf, 0, sizeof *stbuf); + stbuf->f_bsize = SectorsPerCluster * BytesPerSector; + stbuf->f_frsize = SectorsPerCluster * BytesPerSector; + stbuf->f_blocks = TotalNumberOfClusters; + stbuf->f_bfree = NumberOfFreeClusters; + stbuf->f_bavail = TotalNumberOfClusters; + stbuf->f_fsid = VolumeSerialNumber; + stbuf->f_namemax = MaxComponentLength; + + return 0; +} + +int open(const char *path, int oflag, ...) +{ + static DWORD da[] = { GENERIC_READ, GENERIC_WRITE, GENERIC_READ | GENERIC_WRITE, 0 }; + static DWORD cd[] = { OPEN_EXISTING, OPEN_ALWAYS, TRUNCATE_EXISTING, CREATE_ALWAYS }; + DWORD DesiredAccess = 0 == (oflag & _O_APPEND) ? + da[oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)] : + (da[oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)] & ~FILE_WRITE_DATA) | FILE_APPEND_DATA; + DWORD CreationDisposition = (_O_CREAT | _O_EXCL) == (oflag & (_O_CREAT | _O_EXCL)) ? + CREATE_NEW : + cd[(oflag & (_O_CREAT | _O_TRUNC)) >> 8]; + + HANDLE h = CreateFileA(path, + DesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0/* default security */, + CreationDisposition, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0); + + if (INVALID_HANDLE_VALUE == h) + return error(); + + return (int)(intptr_t)h; +} + +int fstat(int fd, struct fuse_stat *stbuf) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + BY_HANDLE_FILE_INFORMATION FileInfo; + + if (!GetFileInformationByHandle(h, &FileInfo)) + return error(); + + memset(stbuf, 0, sizeof *stbuf); + stbuf->st_mode = 0777 | + ((FileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 0040000/* S_IFDIR */ : 0); + stbuf->st_nlink = 1; + stbuf->st_size = ((UINT64)FileInfo.nFileSizeHigh << 32) | ((UINT64)FileInfo.nFileSizeLow); + FspPosixFileTimeToUnixTime(*(PUINT64)&FileInfo.ftCreationTime, (void *)&stbuf->st_birthtim); + FspPosixFileTimeToUnixTime(*(PUINT64)&FileInfo.ftLastAccessTime, (void *)&stbuf->st_atim); + FspPosixFileTimeToUnixTime(*(PUINT64)&FileInfo.ftLastWriteTime, (void *)&stbuf->st_mtim); + FspPosixFileTimeToUnixTime(*(PUINT64)&FileInfo.ftLastWriteTime, (void *)&stbuf->st_ctim); +#if defined(FSP_FUSE_USE_STAT_EX) + stbuf->st_flags = MapFileAttributesToFlags(FileInfo.dwFileAttributes); +#endif + + return 0; +} + +int ftruncate(int fd, fuse_off_t size) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + FILE_END_OF_FILE_INFO EndOfFileInfo; + + EndOfFileInfo.EndOfFile.QuadPart = size; + + if (!SetFileInformationByHandle(h, FileEndOfFileInfo, &EndOfFileInfo, sizeof EndOfFileInfo)) + return error(); + + return 0; +} + +int pread(int fd, void *buf, size_t nbyte, fuse_off_t offset) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + OVERLAPPED Overlapped = { 0 }; + DWORD BytesTransferred; + + Overlapped.Offset = (DWORD)offset; + Overlapped.OffsetHigh = (DWORD)(offset >> 32); + + if (!ReadFile(h, buf, (DWORD)nbyte, &BytesTransferred, &Overlapped)) + { + if (ERROR_HANDLE_EOF == GetLastError()) + return 0; + return error(); + } + + return BytesTransferred; +} + +int pwrite(int fd, const void *buf, size_t nbyte, fuse_off_t offset) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + OVERLAPPED Overlapped = { 0 }; + DWORD BytesTransferred; + + Overlapped.Offset = (DWORD)offset; + Overlapped.OffsetHigh = (DWORD)(offset >> 32); + + if (!WriteFile(h, buf, (DWORD)nbyte, &BytesTransferred, &Overlapped)) + return error(); + + return BytesTransferred; +} + +int fsync(int fd) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + + if (!FlushFileBuffers(h)) + return error(); + + return 0; +} + +int close(int fd) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + + if (!CloseHandle(h)) + return error(); + + return 0; +} + +int lstat(const char *path, struct fuse_stat *stbuf) +{ + HANDLE h = CreateFileA(path, + FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + int res = fstat((int)(intptr_t)h, stbuf); + + CloseHandle(h); + + return res; +} + +int chmod(const char *path, fuse_mode_t mode) +{ + /* we do not support file security */ + return 0; +} + +int lchown(const char *path, fuse_uid_t uid, fuse_gid_t gid) +{ + /* we do not support file security */ + return 0; +} + +int lchflags(const char *path, uint32_t flags) +{ +#if defined(FSP_FUSE_USE_STAT_EX) + UINT32 FileAttributes = MapFlagsToFileAttributes(flags); + + if (0 == FileAttributes) + FileAttributes = FILE_ATTRIBUTE_NORMAL; + + if (!SetFileAttributesA(path, FileAttributes)) + return error(); +#endif + + return 0; +} + +int truncate(const char *path, fuse_off_t size) +{ + HANDLE h = CreateFileA(path, + FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + int res = ftruncate((int)(intptr_t)h, size); + + CloseHandle(h); + + return res; +} + +int utime(const char *path, const struct fuse_utimbuf *timbuf) +{ + if (0 == timbuf) + return utimensat(AT_FDCWD, path, 0, AT_SYMLINK_NOFOLLOW); + else + { + struct fuse_timespec times[2]; + times[0].tv_sec = timbuf->actime; + times[0].tv_nsec = 0; + times[1].tv_sec = timbuf->modtime; + times[1].tv_nsec = 0; + return utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW); + } +} + +int utimensat(int dirfd, const char *path, const struct fuse_timespec times[2], int flag) +{ + /* ignore dirfd and assume that it is always AT_FDCWD */ + /* ignore flag and assume that it is always AT_SYMLINK_NOFOLLOW */ + + HANDLE h = CreateFileA(path, + FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + UINT64 LastAccessTime, LastWriteTime; + if (0 == times) + { + FILETIME FileTime; + GetSystemTimeAsFileTime(&FileTime); + LastAccessTime = LastWriteTime = *(PUINT64)&FileTime; + } + else + { + FspPosixUnixTimeToFileTime((void *)×[0], &LastAccessTime); + FspPosixUnixTimeToFileTime((void *)×[1], &LastWriteTime); + } + + int res = SetFileTime(h, + 0, (PFILETIME)&LastAccessTime, (PFILETIME)&LastWriteTime) ? 0 : error(); + + CloseHandle(h); + + return res; +} + +int setcrtime(const char *path, const struct fuse_timespec *tv) +{ + HANDLE h = CreateFileA(path, + FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + UINT64 CreationTime; + FspPosixUnixTimeToFileTime((void *)tv, &CreationTime); + + int res = SetFileTime(h, + (PFILETIME)&CreationTime, 0, 0) ? 0 : error(); + + CloseHandle(h); + + return res; +} + +int unlink(const char *path) +{ + if (!DeleteFileA(path)) + return error(); + + return 0; +} + +int rename(const char *oldpath, const char *newpath) +{ + if (!MoveFileExA(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) + return error(); + + return 0; +} + +static int lsetea(const char *path, PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength) +{ + HANDLE h = CreateFileA(path, + FILE_WRITE_EA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + IO_STATUS_BLOCK Iosb; + NTSTATUS Status = NtSetEaFile(h, &Iosb, Ea, EaLength); + + CloseHandle(h); + + if (!NT_SUCCESS(Status)) + switch (Status) + { + case STATUS_INVALID_EA_NAME: + case STATUS_EA_LIST_INCONSISTENT: + case STATUS_EA_CORRUPT_ERROR: + case STATUS_NONEXISTENT_EA_ENTRY: + case STATUS_NO_MORE_EAS: + case STATUS_NO_EAS_ON_FILE: + errno = EINVAL; + return -1; + default: + SetLastError(RtlNtStatusToDosError(Status)); + return error(); + } + + return 0; +} + +static int lgetea(const char *path, + PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength) +{ + HANDLE h = CreateFileA(path, + FILE_READ_EA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + IO_STATUS_BLOCK Iosb; + NTSTATUS Status = NtQueryEaFile(h, &Iosb, Ea, EaLength, FALSE, GetEa, GetEaLength, 0, TRUE); + + CloseHandle(h); + + if (!NT_SUCCESS(Status)) + switch (Status) + { + case STATUS_INVALID_EA_NAME: + case STATUS_EA_LIST_INCONSISTENT: + case STATUS_EA_CORRUPT_ERROR: + case STATUS_NONEXISTENT_EA_ENTRY: + case STATUS_NO_MORE_EAS: + errno = EINVAL; + return -1; + case STATUS_NO_EAS_ON_FILE: + if (0 == GetEa) + return 0; + else + { + errno = ENODATA; + return -1; + } + default: + SetLastError(RtlNtStatusToDosError(Status)); + return error(); + } + else if (0 == GetEa && + (FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) > Iosb.Information || 0 == Ea->EaValueLength)) + { + errno = ENODATA; + return -1; + } + + return (ULONG)Iosb.Information; +} + +int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) +{ + union + { + FILE_FULL_EA_INFORMATION V; + UINT8 B[1024]; + } EaBuf; + PFILE_FULL_EA_INFORMATION Ea = &EaBuf.V; + ULONG EaLength; + + size_t namelen = strlen(name); + if (254 < namelen || 0xffff < size) + { + errno = EINVAL; + return -1; + } + + EaLength = (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + namelen + 1 + size); + if (sizeof EaBuf < EaLength) + { + Ea = malloc(EaLength); /* sets errno */ + if (0 == Ea) + return -1; + } + + memset(Ea, 0, sizeof(FILE_FULL_EA_INFORMATION)); + Ea->EaNameLength = (UCHAR)namelen; + Ea->EaValueLength = (USHORT)size; + memcpy(Ea->EaName, name, namelen + 1); + memcpy(Ea->EaName + namelen + 1, value, size); + + int res = lsetea(path, Ea, EaLength); /* sets errno */ + + if (&EaBuf.V != Ea) + free(Ea); + + return res; +} + +int lgetxattr(const char *path, const char *name, void *value, size_t size0) +{ + size_t size = 0 == size0 || 0xffff < size0 ? 0xffff : size0; + union + { + FILE_GET_EA_INFORMATION V; + UINT8 B[FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + 255]; + } GetEaBuf; + PFILE_GET_EA_INFORMATION GetEa = &GetEaBuf.V; + union + { + FILE_FULL_EA_INFORMATION V; + UINT8 B[1024]; + } EaBuf; + PFILE_FULL_EA_INFORMATION Ea = &EaBuf.V; + ULONG GetEaLength, EaLength; + + size_t namelen = strlen(name); + if (254 < namelen) + { + errno = EINVAL; + return -1; + } + + EaLength = (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + namelen + 1 + size); + if (sizeof EaBuf < EaLength) + { + Ea = malloc(EaLength); /* sets errno */ + if (0 == Ea) + return -1; + } + + GetEaLength = (ULONG)(FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + namelen + 1); + memset(GetEa, 0, sizeof(FILE_GET_EA_INFORMATION)); + GetEa->EaNameLength = (UCHAR)namelen; + memcpy(GetEa->EaName, name, namelen + 1); + + int res = lgetea(path, GetEa, GetEaLength, Ea, EaLength); + if (0 < res) + { + res = Ea->EaValueLength; + if (0 == size0) + ; + else if (res <= size0) + memcpy(value, Ea->EaName + Ea->EaNameLength + 1, res); + else + { + errno = ERANGE; + res = -1; + } + } + else if (0 == res) /* should not happen! */ + { + } + + if (&EaBuf.V != Ea) + free(Ea); + + return res; +} + +int llistxattr(const char *path, char *namebuf, size_t size) +{ + PFILE_FULL_EA_INFORMATION Ea = 0; + ULONG EaLength; + + EaLength = (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + 254 + 1 + 0xffff); + Ea = malloc(EaLength); /* sets errno */ + if (0 == Ea) + return -1; + + int res = lgetea(path, 0, 0, Ea, EaLength); + if (0 < res) + { + PFILE_FULL_EA_INFORMATION EaEnd = (PVOID)((PUINT8)Ea + res); + res = 0; + for (PFILE_FULL_EA_INFORMATION EaPtr = Ea; EaEnd > EaPtr; EaPtr = NEXT_EA(EaPtr, EaEnd)) + res += EaPtr->EaNameLength + 1; + + if (0 == size) + ; + else if (res <= size) + { + char *p = namebuf; + for (PFILE_FULL_EA_INFORMATION EaPtr = Ea; EaEnd > EaPtr; EaPtr = NEXT_EA(EaPtr, EaEnd)) + { + memcpy(p, EaPtr->EaName, EaPtr->EaNameLength + 1); + p += EaPtr->EaNameLength + 1; + } + } + else + { + errno = ERANGE; + res = -1; + } + } + + free(Ea); + + return res; +} + +int lremovexattr(const char *path, const char *name) +{ + return lsetxattr(path, name, 0, 0, 0); +} + +int mkdir(const char *path, fuse_mode_t mode) +{ + if (!CreateDirectoryA(path, 0/* default security */)) + return error(); + + return 0; +} + +int rmdir(const char *path) +{ + if (!RemoveDirectoryA(path)) + return error(); + + return 0; +} + +DIR *opendir(const char *path) +{ + HANDLE h = CreateFileA(path, + FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error0(); + + size_t pathlen = strlen(path); + if (0 < pathlen && '/' == path[pathlen - 1]) + pathlen--; + + DIR *dirp = malloc(sizeof *dirp + pathlen + 3); /* sets errno */ + if (0 == dirp) + { + CloseHandle(h); + return 0; + } + + memset(dirp, 0, sizeof *dirp); + dirp->h = h; + dirp->fh = INVALID_HANDLE_VALUE; + memcpy(dirp->path, path, pathlen); + dirp->path[pathlen + 0] = '/'; + dirp->path[pathlen + 1] = '*'; + dirp->path[pathlen + 2] = '\0'; + + return dirp; +} + +int dirfd(DIR *dirp) +{ + return (int)(intptr_t)dirp->h; +} + +void rewinddir(DIR *dirp) +{ + if (INVALID_HANDLE_VALUE != dirp->fh) + { + FindClose(dirp->fh); + dirp->fh = INVALID_HANDLE_VALUE; + } +} + +struct dirent *readdir(DIR *dirp) +{ + WIN32_FIND_DATAA FindData; + struct fuse_stat *stbuf = &dirp->de.d_stat; + + if (INVALID_HANDLE_VALUE == dirp->fh) + { + dirp->fh = FindFirstFileA(dirp->path, &FindData); + if (INVALID_HANDLE_VALUE == dirp->fh) + return error0(); + } + else + { + if (!FindNextFileA(dirp->fh, &FindData)) + { + if (ERROR_NO_MORE_FILES == GetLastError()) + return 0; + return error0(); + } + } + + memset(stbuf, 0, sizeof *stbuf); + stbuf->st_mode = 0777 | + ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 0040000/* S_IFDIR */ : 0); + stbuf->st_nlink = 1; + stbuf->st_size = ((UINT64)FindData.nFileSizeHigh << 32) | ((UINT64)FindData.nFileSizeLow); + FspPosixFileTimeToUnixTime(*(PUINT64)&FindData.ftCreationTime, (void *)&stbuf->st_birthtim); + FspPosixFileTimeToUnixTime(*(PUINT64)&FindData.ftLastAccessTime, (void *)&stbuf->st_atim); + FspPosixFileTimeToUnixTime(*(PUINT64)&FindData.ftLastWriteTime, (void *)&stbuf->st_mtim); + FspPosixFileTimeToUnixTime(*(PUINT64)&FindData.ftLastWriteTime, (void *)&stbuf->st_ctim); +#if defined(FSP_FUSE_USE_STAT_EX) + stbuf->st_flags = MapFileAttributesToFlags(FindData.dwFileAttributes); +#endif + + strcpy(dirp->de.d_name, FindData.cFileName); + + return &dirp->de; +} + +int closedir(DIR *dirp) +{ + if (INVALID_HANDLE_VALUE != dirp->fh) + FindClose(dirp->fh); + + CloseHandle(dirp->h); + free(dirp); + + return 0; +} + +static int maperror(int winerrno) +{ + switch (winerrno) + { + case ERROR_INVALID_FUNCTION: + return EINVAL; + case ERROR_FILE_NOT_FOUND: + return ENOENT; + case ERROR_PATH_NOT_FOUND: + return ENOENT; + case ERROR_TOO_MANY_OPEN_FILES: + return EMFILE; + case ERROR_ACCESS_DENIED: + return EACCES; + case ERROR_INVALID_HANDLE: + return EBADF; + case ERROR_ARENA_TRASHED: + return ENOMEM; + case ERROR_NOT_ENOUGH_MEMORY: + return ENOMEM; + case ERROR_INVALID_BLOCK: + return ENOMEM; + case ERROR_BAD_ENVIRONMENT: + return E2BIG; + case ERROR_BAD_FORMAT: + return ENOEXEC; + case ERROR_INVALID_ACCESS: + return EINVAL; + case ERROR_INVALID_DATA: + return EINVAL; + case ERROR_INVALID_DRIVE: + return ENOENT; + case ERROR_CURRENT_DIRECTORY: + return EACCES; + case ERROR_NOT_SAME_DEVICE: + return EXDEV; + case ERROR_NO_MORE_FILES: + return ENOENT; + case ERROR_LOCK_VIOLATION: + return EACCES; + case ERROR_BAD_NETPATH: + return ENOENT; + case ERROR_NETWORK_ACCESS_DENIED: + return EACCES; + case ERROR_BAD_NET_NAME: + return ENOENT; + case ERROR_FILE_EXISTS: + return EEXIST; + case ERROR_CANNOT_MAKE: + return EACCES; + case ERROR_FAIL_I24: + return EACCES; + case ERROR_INVALID_PARAMETER: + return EINVAL; + case ERROR_NO_PROC_SLOTS: + return EAGAIN; + case ERROR_DRIVE_LOCKED: + return EACCES; + case ERROR_BROKEN_PIPE: + return EPIPE; + case ERROR_DISK_FULL: + return ENOSPC; + case ERROR_INVALID_TARGET_HANDLE: + return EBADF; + case ERROR_WAIT_NO_CHILDREN: + return ECHILD; + case ERROR_CHILD_NOT_COMPLETE: + return ECHILD; + case ERROR_DIRECT_ACCESS_HANDLE: + return EBADF; + case ERROR_NEGATIVE_SEEK: + return EINVAL; + case ERROR_SEEK_ON_DEVICE: + return EACCES; + case ERROR_DIR_NOT_EMPTY: + return ENOTEMPTY; + case ERROR_NOT_LOCKED: + return EACCES; + case ERROR_BAD_PATHNAME: + return ENOENT; + case ERROR_MAX_THRDS_REACHED: + return EAGAIN; + case ERROR_LOCK_FAILED: + return EACCES; + case ERROR_ALREADY_EXISTS: + return EEXIST; + case ERROR_FILENAME_EXCED_RANGE: + return ENOENT; + case ERROR_NESTING_NOT_ALLOWED: + return EAGAIN; + case ERROR_NOT_ENOUGH_QUOTA: + return ENOMEM; + default: + if (ERROR_WRITE_PROTECT <= winerrno && winerrno <= ERROR_SHARING_BUFFER_EXCEEDED) + return EACCES; + else if (ERROR_INVALID_STARTING_CODESEG <= winerrno && winerrno <= ERROR_INFLOOP_IN_RELOC_CHAIN) + return ENOEXEC; + else + return EINVAL; + } +} + +long WinFspLoad(void) +{ + return FspLoad(0); +} diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse/winposix.h b/3rd_party/winfsp-1.10/samples/passthrough-fuse/winposix.h new file mode 100644 index 00000000..b9150c22 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse/winposix.h @@ -0,0 +1,86 @@ +/** + * @file winposix.h + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef WINPOSIX_H_INCLUDED +#define WINPOSIX_H_INCLUDED + +#define O_RDONLY _O_RDONLY +#define O_WRONLY _O_WRONLY +#define O_RDWR _O_RDWR +#define O_APPEND _O_APPEND +#define O_CREAT _O_CREAT +#define O_EXCL _O_EXCL +#define O_TRUNC _O_TRUNC + +#define PATH_MAX 1024 +#define AT_FDCWD -2 +#define AT_SYMLINK_NOFOLLOW 2 + +typedef struct _DIR DIR; +struct dirent +{ + struct fuse_stat d_stat; + char d_name[255]; +}; + +char *realpath(const char *path, char *resolved); + +int statvfs(const char *path, struct fuse_statvfs *stbuf); + +int open(const char *path, int oflag, ...); +int fstat(int fd, struct fuse_stat *stbuf); +int ftruncate(int fd, fuse_off_t size); +int pread(int fd, void *buf, size_t nbyte, fuse_off_t offset); +int pwrite(int fd, const void *buf, size_t nbyte, fuse_off_t offset); +int fsync(int fd); +int close(int fd); + +int lstat(const char *path, struct fuse_stat *stbuf); +int chmod(const char *path, fuse_mode_t mode); +int lchown(const char *path, fuse_uid_t uid, fuse_gid_t gid); +int lchflags(const char *path, uint32_t flags); +int truncate(const char *path, fuse_off_t size); +int utime(const char *path, const struct fuse_utimbuf *timbuf); +int utimensat(int dirfd, const char *path, const struct fuse_timespec times[2], int flag); +int setcrtime(const char *path, const struct fuse_timespec *tv); +int unlink(const char *path); +int rename(const char *oldpath, const char *newpath); + +int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags); +int lgetxattr(const char *path, const char *name, void *value, size_t size); +int llistxattr(const char *path, char *namebuf, size_t size); +int lremovexattr(const char *path, const char *name); + +int mkdir(const char *path, fuse_mode_t mode); +int rmdir(const char *path); + +DIR *opendir(const char *path); +int dirfd(DIR *dirp); +void rewinddir(DIR *dirp); +struct dirent *readdir(DIR *dirp); +int closedir(DIR *dirp); + +long WinFspLoad(void); +#undef fuse_main +#define fuse_main(argc, argv, ops, data)\ + (WinFspLoad(), fuse_main_real(argc, argv, ops, sizeof *(ops), data)) + +#endif diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse3/Makefile b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/Makefile new file mode 100644 index 00000000..e729a044 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/Makefile @@ -0,0 +1,18 @@ +usage: + @echo "make cygfuse3|winfsp-fuse3" 1>&2 + @echo "" 1>&2 + @echo " cygfuse3 Link with CYGFUSE3" 1>&2 + @echo " winfsp-fuse3 Link with WinFsp-FUSE3" 1>&2 + @exit 2 + +cygfuse3: passthrough-cygfuse3 + +winfsp-fuse3: passthrough-winfsp-fuse3 + +passthrough-cygfuse3: passthrough-fuse3.c + gcc $^ -o $@ -g -Wall `pkg-config fuse3 --cflags --libs` + +passthrough-winfsp-fuse3: export PKG_CONFIG_PATH=$(PWD)/winfsp.install/lib +passthrough-winfsp-fuse3: passthrough-fuse3.c + ln -nsf "`regtool --wow32 get '/HKLM/Software/WinFsp/InstallDir' | cygpath -au -f -`" winfsp.install + gcc $^ -o $@ -g -Wall `pkg-config fuse3 --cflags --libs` diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse3/README.md b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/README.md new file mode 100644 index 00000000..3cf08b70 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/README.md @@ -0,0 +1,7 @@ +`Passthrough-fuse3` is a simple FUSE3 file system that passes all file system operations to an underlying file system. + +It can be built with the following tools: + +- Using Visual Studio (`winfsp.sln`). +- Using Cygwin GCC and linking directly with the WinFsp DLL (`make winfsp-fuse3`). +- Using Cygwin GCC and linking to CYGFUSE3 (`make cygfuse3`). diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse3/passthrough-fuse3.c b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/passthrough-fuse3.c new file mode 100644 index 00000000..baa0804c --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/passthrough-fuse3.c @@ -0,0 +1,369 @@ +/** + * @file passthrough-fuse.c + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#include +#include +#include +#include +#include + +#include + +#if defined(_WIN64) || defined(_WIN32) +#include "winposix.h" +#else +#include +#include +#endif + +#define FSNAME "passthrough" +#define PROGNAME "passthrough-fuse" + +#define concat_path(ptfs, fn, fp) (sizeof fp > (unsigned)snprintf(fp, sizeof fp, "%s%s", ptfs->rootdir, fn)) + +#define fi_dirbit (0x8000000000000000ULL) +#define fi_fh(fi, MASK) ((fi)->fh & (MASK)) +#define fi_setfh(fi, FH, MASK) ((fi)->fh = (intptr_t)(FH) | (MASK)) +#define fi_fd(fi) (fi_fh(fi, fi_dirbit) ? \ + dirfd((DIR *)(intptr_t)fi_fh(fi, ~fi_dirbit)) : (int)fi_fh(fi, ~fi_dirbit)) +#define fi_dirp(fi) ((DIR *)(intptr_t)fi_fh(fi, ~fi_dirbit)) +#define fi_setfd(fi, fd) (fi_setfh(fi, fd, 0)) +#define fi_setdirp(fi, dirp) (fi_setfh(fi, dirp, fi_dirbit)) + +#define ptfs_impl_fullpath(n) \ + char full ## n[PATH_MAX]; \ + if (!concat_path(((PTFS *)fuse_get_context()->private_data), n, full ## n))\ + return -ENAMETOOLONG; \ + n = full ## n + +typedef struct +{ + const char *rootdir; +} PTFS; + +static int ptfs_getattr(const char *path, struct fuse_stat *stbuf, struct fuse_file_info *fi) +{ + if (0 == fi) + { + ptfs_impl_fullpath(path); + + return -1 != lstat(path, stbuf) ? 0 : -errno; + } + else + { + int fd = fi_fd(fi); + + return -1 != fstat(fd, stbuf) ? 0 : -errno; + } +} + +static int ptfs_mkdir(const char *path, fuse_mode_t mode) +{ + ptfs_impl_fullpath(path); + + return -1 != mkdir(path, mode) ? 0 : -errno; +} + +static int ptfs_unlink(const char *path) +{ + ptfs_impl_fullpath(path); + + return -1 != unlink(path) ? 0 : -errno; +} + +static int ptfs_rmdir(const char *path) +{ + ptfs_impl_fullpath(path); + + return -1 != rmdir(path) ? 0 : -errno; +} + +static int ptfs_rename(const char *oldpath, const char *newpath, unsigned int flags) +{ + ptfs_impl_fullpath(newpath); + ptfs_impl_fullpath(oldpath); + + return -1 != rename(oldpath, newpath) ? 0 : -errno; +} + +static int ptfs_chmod(const char *path, fuse_mode_t mode, struct fuse_file_info *fi) +{ + ptfs_impl_fullpath(path); + + return -1 != chmod(path, mode) ? 0 : -errno; +} + +static int ptfs_chown(const char *path, fuse_uid_t uid, fuse_gid_t gid, struct fuse_file_info *fi) +{ + ptfs_impl_fullpath(path); + + return -1 != lchown(path, uid, gid) ? 0 : -errno; +} + +static int ptfs_truncate(const char *path, fuse_off_t size, struct fuse_file_info *fi) +{ + if (0 == fi) + { + ptfs_impl_fullpath(path); + + return -1 != truncate(path, size) ? 0 : -errno; + } + else + { + int fd = fi_fd(fi); + + return -1 != ftruncate(fd, size) ? 0 : -errno; + } +} + +static int ptfs_open(const char *path, struct fuse_file_info *fi) +{ + ptfs_impl_fullpath(path); + + int fd; + return -1 != (fd = open(path, fi->flags)) ? (fi_setfd(fi, fd), 0) : -errno; +} + +static int ptfs_read(const char *path, char *buf, size_t size, fuse_off_t off, + struct fuse_file_info *fi) +{ + int fd = fi_fd(fi); + + int nb; + return -1 != (nb = pread(fd, buf, size, off)) ? nb : -errno; +} + +static int ptfs_write(const char *path, const char *buf, size_t size, fuse_off_t off, + struct fuse_file_info *fi) +{ + int fd = fi_fd(fi); + + int nb; + return -1 != (nb = pwrite(fd, buf, size, off)) ? nb : -errno; +} + +static int ptfs_statfs(const char *path, struct fuse_statvfs *stbuf) +{ + ptfs_impl_fullpath(path); + + return -1 != statvfs(path, stbuf) ? 0 : -errno; +} + +static int ptfs_release(const char *path, struct fuse_file_info *fi) +{ + int fd = fi_fd(fi); + + close(fd); + return 0; +} + +static int ptfs_fsync(const char *path, int datasync, struct fuse_file_info *fi) +{ + int fd = fi_fd(fi); + + return -1 != fsync(fd) ? 0 : -errno; +} + +static int ptfs_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) +{ + ptfs_impl_fullpath(path); + + return -1 != lsetxattr(path, name, value, size, flags) ? 0 : -errno; +} + +static int ptfs_getxattr(const char *path, const char *name, char *value, size_t size) +{ + ptfs_impl_fullpath(path); + + int nb; + return -1 != (nb = lgetxattr(path, name, value, size)) ? nb : -errno; +} + +static int ptfs_listxattr(const char *path, char *namebuf, size_t size) +{ + ptfs_impl_fullpath(path); + + int nb; + return -1 != (nb = llistxattr(path, namebuf, size)) ? nb : -errno; +} + +static int ptfs_removexattr(const char *path, const char *name) +{ + ptfs_impl_fullpath(path); + + return -1 != lremovexattr(path, name) ? 0 : -errno; +} + +static int ptfs_opendir(const char *path, struct fuse_file_info *fi) +{ + ptfs_impl_fullpath(path); + + DIR *dirp; + return 0 != (dirp = opendir(path)) ? (fi_setdirp(fi, dirp), 0) : -errno; +} + +static int ptfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, fuse_off_t off, + struct fuse_file_info *fi, enum fuse_readdir_flags flags) +{ + DIR *dirp = fi_dirp(fi); + struct dirent *de; + + rewinddir(dirp); + for (;;) + { + errno = 0; + if (0 == (de = readdir(dirp))) + break; +#if defined(_WIN64) || defined(_WIN32) + if (0 != filler(buf, de->d_name, &de->d_stat, 0, FUSE_FILL_DIR_PLUS)) +#else + if (0 != filler(buf, de->d_name, 0, 0, 0)) +#endif + return -ENOMEM; + } + + return -errno; +} + +static int ptfs_releasedir(const char *path, struct fuse_file_info *fi) +{ + DIR *dirp = fi_dirp(fi); + + return -1 != closedir(dirp) ? 0 : -errno; +} + +static void *ptfs_init(struct fuse_conn_info *conn, struct fuse_config *conf) +{ + conn->want |= (conn->capable & FUSE_CAP_READDIRPLUS); + +#if defined(FSP_FUSE_CAP_CASE_INSENSITIVE) + conn->want |= (conn->capable & FSP_FUSE_CAP_CASE_INSENSITIVE); +#endif + + return fuse_get_context()->private_data; +} + +static int ptfs_create(const char *path, fuse_mode_t mode, struct fuse_file_info *fi) +{ + ptfs_impl_fullpath(path); + + int fd; + return -1 != (fd = open(path, fi->flags, mode)) ? (fi_setfd(fi, fd), 0) : -errno; +} + +static int ptfs_utimens(const char *path, const struct fuse_timespec tv[2], struct fuse_file_info *fi) +{ + ptfs_impl_fullpath(path); + + return -1 != utimensat(AT_FDCWD, path, tv, AT_SYMLINK_NOFOLLOW) ? 0 : -errno; +} + +static struct fuse_operations ptfs_ops = +{ + .getattr = ptfs_getattr, + .mkdir = ptfs_mkdir, + .unlink = ptfs_unlink, + .rmdir = ptfs_rmdir, + .rename = ptfs_rename, + .chmod = ptfs_chmod, + .chown = ptfs_chown, + .truncate = ptfs_truncate, + .open = ptfs_open, + .read = ptfs_read, + .write = ptfs_write, + .statfs = ptfs_statfs, + .release = ptfs_release, + .fsync = ptfs_fsync, + .setxattr = ptfs_setxattr, + .getxattr = ptfs_getxattr, + .listxattr = ptfs_listxattr, + .removexattr = ptfs_removexattr, + .opendir = ptfs_opendir, + .readdir = ptfs_readdir, + .releasedir = ptfs_releasedir, + .init = ptfs_init, + .create = ptfs_create, + .utimens = ptfs_utimens, +}; + +static void usage(void) +{ + fprintf(stderr, "usage: " PROGNAME " [FUSE options] rootdir mountpoint\n"); + exit(2); +} + +int main(int argc, char *argv[]) +{ + PTFS ptfs = { 0 }; + + if (3 <= argc && '-' != argv[argc - 2][0] && '-' != argv[argc - 1][0]) + { + ptfs.rootdir = realpath(argv[argc - 2], 0); /* memory freed at process end */ + argv[argc - 2] = argv[argc - 1]; + argc--; + } + +#if defined(_WIN64) || defined(_WIN32) + /* + * When building for Windows (rather than Cygwin or POSIX OS) + * allow the path to be specified using the --VolumePrefix + * switch using the syntax \\passthrough-fuse\C$\Path. This + * allows us to run the file system under the WinFsp.Launcher + * and start it using commands like: + * + * net use z: \\passthrough-fuse\C$\Path + */ + if (0 == ptfs.rootdir) + for (int argi = 1; argc > argi; argi++) + { + int strncmp(const char *a, const char *b, size_t length); + char *strchr(const char *s, int c); + char *p = 0; + + if (0 == strncmp("--UNC=", argv[argi], sizeof "--UNC=" - 1)) + p = argv[argi] + sizeof "--UNC=" - 1; + else if (0 == strncmp("--VolumePrefix=", argv[argi], sizeof "--VolumePrefix=" - 1)) + p = argv[argi] + sizeof "--VolumePrefix=" - 1; + + if (0 != p && '\\' != p[1]) + { + p = strchr(p + 1, '\\'); + if (0 != p && + ( + ('A' <= p[1] && p[1] <= 'Z') || + ('a' <= p[1] && p[1] <= 'z') + ) && + '$' == p[2]) + { + p[2] = ':'; + ptfs.rootdir = realpath(p + 1, 0); /* memory freed at process end */ + p[2] = '$'; + break; + } + } + } +#endif + + if (0 == ptfs.rootdir) + usage(); + + return fuse_main(argc, argv, &ptfs_ops, &ptfs); +} diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse3/passthrough-fuse3.sln b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/passthrough-fuse3.sln new file mode 100644 index 00000000..5eaced8d --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/passthrough-fuse3.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "passthrough-fuse3", "passthrough-fuse3.vcxproj", "{5E99498C-D30C-48EF-A04A-7977C0305FAC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5E99498C-D30C-48EF-A04A-7977C0305FAC}.Debug|x64.ActiveCfg = Debug|x64 + {5E99498C-D30C-48EF-A04A-7977C0305FAC}.Debug|x64.Build.0 = Debug|x64 + {5E99498C-D30C-48EF-A04A-7977C0305FAC}.Debug|x86.ActiveCfg = Debug|Win32 + {5E99498C-D30C-48EF-A04A-7977C0305FAC}.Debug|x86.Build.0 = Debug|Win32 + {5E99498C-D30C-48EF-A04A-7977C0305FAC}.Release|x64.ActiveCfg = Release|x64 + {5E99498C-D30C-48EF-A04A-7977C0305FAC}.Release|x64.Build.0 = Release|x64 + {5E99498C-D30C-48EF-A04A-7977C0305FAC}.Release|x86.ActiveCfg = Release|Win32 + {5E99498C-D30C-48EF-A04A-7977C0305FAC}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse3/passthrough-fuse3.vcxproj b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/passthrough-fuse3.vcxproj new file mode 100644 index 00000000..6dde9088 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/passthrough-fuse3.vcxproj @@ -0,0 +1,190 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {5E99498C-D30C-48EF-A04A-7977C0305FAC} + Win32Proj + passthroughfuse3 + $(LatestTargetPlatformVersion) + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse3;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + 4996 + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse3;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + 4996 + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse3;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + 4996 + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc\fuse3;$(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + 4996 + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + + + + + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse3/passthrough-fuse3.vcxproj.filters b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/passthrough-fuse3.vcxproj.filters new file mode 100644 index 00000000..fb964d9e --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/passthrough-fuse3.vcxproj.filters @@ -0,0 +1,21 @@ + + + + + {bfbcc136-ea14-4445-8f9b-1fa7f8aedc71} + + + + + Source + + + Source + + + + + Source + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse3/winposix.c b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/winposix.c new file mode 100644 index 00000000..54097142 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/winposix.c @@ -0,0 +1,892 @@ +/** + * @file winposix.c + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +/* + * This is a very simple Windows POSIX layer. It handles all the POSIX + * file API's required to implement passthrough-fuse in POSIX, however + * the API handling is rather unsophisticated. + * + * Ways to improve it: use the FspPosix* API's to properly handle + * file names and security. + */ + +#include +#include +#include +#include "winposix.h" + +#pragma comment(lib, "ntdll.lib") + +typedef struct _FILE_GET_EA_INFORMATION +{ + ULONG NextEntryOffset; + UCHAR EaNameLength; + CHAR EaName[1]; +} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION; + +NTSYSAPI NTSTATUS NTAPI NtQueryEaFile( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID Buffer, + IN ULONG Length, + IN BOOLEAN ReturnSingleEntry, + IN PVOID EaList OPTIONAL, + IN ULONG EaListLength, + IN PULONG EaIndex OPTIONAL, + IN BOOLEAN RestartScan); +NTSYSAPI NTSTATUS NTAPI NtSetEaFile( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PVOID EaBuffer, + IN ULONG EaBufferSize); +#define NEXT_EA(Ea, EaEnd) \ + (0 != (Ea)->NextEntryOffset ? (PVOID)((PUINT8)(Ea) + (Ea)->NextEntryOffset) : (EaEnd)) + +struct _DIR +{ + HANDLE h, fh; + struct dirent de; + char path[]; +}; + +#if defined(FSP_FUSE_USE_STAT_EX) +static inline uint32_t MapFileAttributesToFlags(UINT32 FileAttributes) +{ + uint32_t flags = 0; + + if (FileAttributes & FILE_ATTRIBUTE_READONLY) + flags |= FSP_FUSE_UF_READONLY; + if (FileAttributes & FILE_ATTRIBUTE_HIDDEN) + flags |= FSP_FUSE_UF_HIDDEN; + if (FileAttributes & FILE_ATTRIBUTE_SYSTEM) + flags |= FSP_FUSE_UF_SYSTEM; + if (FileAttributes & FILE_ATTRIBUTE_ARCHIVE) + flags |= FSP_FUSE_UF_ARCHIVE; + + return flags; +} + +static inline UINT32 MapFlagsToFileAttributes(uint32_t flags) +{ + UINT32 FileAttributes = 0; + + if (flags & FSP_FUSE_UF_READONLY) + FileAttributes |= FILE_ATTRIBUTE_READONLY; + if (flags & FSP_FUSE_UF_HIDDEN) + FileAttributes |= FILE_ATTRIBUTE_HIDDEN; + if (flags & FSP_FUSE_UF_SYSTEM) + FileAttributes |= FILE_ATTRIBUTE_SYSTEM; + if (flags & FSP_FUSE_UF_ARCHIVE) + FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; + + return FileAttributes; +} +#endif + +static int maperror(int winerrno); + +static inline void *error0(void) +{ + errno = maperror(GetLastError()); + return 0; +} + +static inline int error(void) +{ + errno = maperror(GetLastError()); + return -1; +} + +char *realpath(const char *path, char *resolved) +{ + char *result; + + if (0 == resolved) + { + result = malloc(PATH_MAX); /* sets errno */ + if (0 == result) + return 0; + } + else + result = resolved; + + int err = 0; + DWORD len = GetFullPathNameA(path, PATH_MAX, result, 0); + if (0 == len) + err = GetLastError(); + else if (PATH_MAX < len) + err = ERROR_INVALID_PARAMETER; + + if (0 == err) + { + HANDLE h = CreateFileA(result, + FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE != h) + CloseHandle(h); + else + err = GetLastError(); + } + + if (0 != err) + { + if (result != resolved) + free(result); + + errno = maperror(err); + result = 0; + } + + return result; +} + +int statvfs(const char *path, struct fuse_statvfs *stbuf) +{ + char root[PATH_MAX]; + DWORD + VolumeSerialNumber, + MaxComponentLength, + SectorsPerCluster, + BytesPerSector, + NumberOfFreeClusters, + TotalNumberOfClusters; + + if (!GetVolumePathNameA(path, root, PATH_MAX) || + !GetVolumeInformationA(root, 0, 0, &VolumeSerialNumber, &MaxComponentLength, 0, 0, 0) || + !GetDiskFreeSpaceA(root, &SectorsPerCluster, &BytesPerSector, + &NumberOfFreeClusters, &TotalNumberOfClusters)) + { + return error(); + } + + memset(stbuf, 0, sizeof *stbuf); + stbuf->f_bsize = SectorsPerCluster * BytesPerSector; + stbuf->f_frsize = SectorsPerCluster * BytesPerSector; + stbuf->f_blocks = TotalNumberOfClusters; + stbuf->f_bfree = NumberOfFreeClusters; + stbuf->f_bavail = TotalNumberOfClusters; + stbuf->f_fsid = VolumeSerialNumber; + stbuf->f_namemax = MaxComponentLength; + + return 0; +} + +int open(const char *path, int oflag, ...) +{ + static DWORD da[] = { GENERIC_READ, GENERIC_WRITE, GENERIC_READ | GENERIC_WRITE, 0 }; + static DWORD cd[] = { OPEN_EXISTING, OPEN_ALWAYS, TRUNCATE_EXISTING, CREATE_ALWAYS }; + DWORD DesiredAccess = 0 == (oflag & _O_APPEND) ? + da[oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)] : + (da[oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)] & ~FILE_WRITE_DATA) | FILE_APPEND_DATA; + DWORD CreationDisposition = (_O_CREAT | _O_EXCL) == (oflag & (_O_CREAT | _O_EXCL)) ? + CREATE_NEW : + cd[(oflag & (_O_CREAT | _O_TRUNC)) >> 8]; + + HANDLE h = CreateFileA(path, + DesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0/* default security */, + CreationDisposition, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, 0); + + if (INVALID_HANDLE_VALUE == h) + return error(); + + return (int)(intptr_t)h; +} + +int fstat(int fd, struct fuse_stat *stbuf) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + BY_HANDLE_FILE_INFORMATION FileInfo; + + if (!GetFileInformationByHandle(h, &FileInfo)) + return error(); + + memset(stbuf, 0, sizeof *stbuf); + stbuf->st_mode = 0777 | + ((FileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 0040000/* S_IFDIR */ : 0); + stbuf->st_nlink = 1; + stbuf->st_size = ((UINT64)FileInfo.nFileSizeHigh << 32) | ((UINT64)FileInfo.nFileSizeLow); + FspPosixFileTimeToUnixTime(*(PUINT64)&FileInfo.ftCreationTime, (void *)&stbuf->st_birthtim); + FspPosixFileTimeToUnixTime(*(PUINT64)&FileInfo.ftLastAccessTime, (void *)&stbuf->st_atim); + FspPosixFileTimeToUnixTime(*(PUINT64)&FileInfo.ftLastWriteTime, (void *)&stbuf->st_mtim); + FspPosixFileTimeToUnixTime(*(PUINT64)&FileInfo.ftLastWriteTime, (void *)&stbuf->st_ctim); +#if defined(FSP_FUSE_USE_STAT_EX) + stbuf->st_flags = MapFileAttributesToFlags(FileInfo.dwFileAttributes); +#endif + + return 0; +} + +int ftruncate(int fd, fuse_off_t size) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + FILE_END_OF_FILE_INFO EndOfFileInfo; + + EndOfFileInfo.EndOfFile.QuadPart = size; + + if (!SetFileInformationByHandle(h, FileEndOfFileInfo, &EndOfFileInfo, sizeof EndOfFileInfo)) + return error(); + + return 0; +} + +int pread(int fd, void *buf, size_t nbyte, fuse_off_t offset) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + OVERLAPPED Overlapped = { 0 }; + DWORD BytesTransferred; + + Overlapped.Offset = (DWORD)offset; + Overlapped.OffsetHigh = (DWORD)(offset >> 32); + + if (!ReadFile(h, buf, (DWORD)nbyte, &BytesTransferred, &Overlapped)) + { + if (ERROR_HANDLE_EOF == GetLastError()) + return 0; + return error(); + } + + return BytesTransferred; +} + +int pwrite(int fd, const void *buf, size_t nbyte, fuse_off_t offset) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + OVERLAPPED Overlapped = { 0 }; + DWORD BytesTransferred; + + Overlapped.Offset = (DWORD)offset; + Overlapped.OffsetHigh = (DWORD)(offset >> 32); + + if (!WriteFile(h, buf, (DWORD)nbyte, &BytesTransferred, &Overlapped)) + return error(); + + return BytesTransferred; +} + +int fsync(int fd) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + + if (!FlushFileBuffers(h)) + return error(); + + return 0; +} + +int close(int fd) +{ + HANDLE h = (HANDLE)(intptr_t)fd; + + if (!CloseHandle(h)) + return error(); + + return 0; +} + +int lstat(const char *path, struct fuse_stat *stbuf) +{ + HANDLE h = CreateFileA(path, + FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + int res = fstat((int)(intptr_t)h, stbuf); + + CloseHandle(h); + + return res; +} + +int chmod(const char *path, fuse_mode_t mode) +{ + /* we do not support file security */ + return 0; +} + +int lchown(const char *path, fuse_uid_t uid, fuse_gid_t gid) +{ + /* we do not support file security */ + return 0; +} + +int lchflags(const char *path, uint32_t flags) +{ +#if defined(FSP_FUSE_USE_STAT_EX) + UINT32 FileAttributes = MapFlagsToFileAttributes(flags); + + if (0 == FileAttributes) + FileAttributes = FILE_ATTRIBUTE_NORMAL; + + if (!SetFileAttributesA(path, FileAttributes)) + return error(); +#endif + + return 0; +} + +int truncate(const char *path, fuse_off_t size) +{ + HANDLE h = CreateFileA(path, + FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + int res = ftruncate((int)(intptr_t)h, size); + + CloseHandle(h); + + return res; +} + +int utime(const char *path, const struct fuse_utimbuf *timbuf) +{ + if (0 == timbuf) + return utimensat(AT_FDCWD, path, 0, AT_SYMLINK_NOFOLLOW); + else + { + struct fuse_timespec times[2]; + times[0].tv_sec = timbuf->actime; + times[0].tv_nsec = 0; + times[1].tv_sec = timbuf->modtime; + times[1].tv_nsec = 0; + return utimensat(AT_FDCWD, path, times, AT_SYMLINK_NOFOLLOW); + } +} + +int utimensat(int dirfd, const char *path, const struct fuse_timespec times[2], int flag) +{ + /* ignore dirfd and assume that it is always AT_FDCWD */ + /* ignore flag and assume that it is always AT_SYMLINK_NOFOLLOW */ + + HANDLE h = CreateFileA(path, + FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + UINT64 LastAccessTime, LastWriteTime; + if (0 == times) + { + FILETIME FileTime; + GetSystemTimeAsFileTime(&FileTime); + LastAccessTime = LastWriteTime = *(PUINT64)&FileTime; + } + else + { + FspPosixUnixTimeToFileTime((void *)×[0], &LastAccessTime); + FspPosixUnixTimeToFileTime((void *)×[1], &LastWriteTime); + } + + int res = SetFileTime(h, + 0, (PFILETIME)&LastAccessTime, (PFILETIME)&LastWriteTime) ? 0 : error(); + + CloseHandle(h); + + return res; +} + +int setcrtime(const char *path, const struct fuse_timespec *tv) +{ + HANDLE h = CreateFileA(path, + FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + UINT64 CreationTime; + FspPosixUnixTimeToFileTime((void *)tv, &CreationTime); + + int res = SetFileTime(h, + (PFILETIME)&CreationTime, 0, 0) ? 0 : error(); + + CloseHandle(h); + + return res; +} + +int unlink(const char *path) +{ + if (!DeleteFileA(path)) + return error(); + + return 0; +} + +int rename(const char *oldpath, const char *newpath) +{ + if (!MoveFileExA(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) + return error(); + + return 0; +} + +static int lsetea(const char *path, PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength) +{ + HANDLE h = CreateFileA(path, + FILE_WRITE_EA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + IO_STATUS_BLOCK Iosb; + NTSTATUS Status = NtSetEaFile(h, &Iosb, Ea, EaLength); + + CloseHandle(h); + + if (!NT_SUCCESS(Status)) + switch (Status) + { + case STATUS_INVALID_EA_NAME: + case STATUS_EA_LIST_INCONSISTENT: + case STATUS_EA_CORRUPT_ERROR: + case STATUS_NONEXISTENT_EA_ENTRY: + case STATUS_NO_MORE_EAS: + case STATUS_NO_EAS_ON_FILE: + errno = EINVAL; + return -1; + default: + SetLastError(RtlNtStatusToDosError(Status)); + return error(); + } + + return 0; +} + +static int lgetea(const char *path, + PFILE_GET_EA_INFORMATION GetEa, ULONG GetEaLength, + PFILE_FULL_EA_INFORMATION Ea, ULONG EaLength) +{ + HANDLE h = CreateFileA(path, + FILE_READ_EA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error(); + + IO_STATUS_BLOCK Iosb; + NTSTATUS Status = NtQueryEaFile(h, &Iosb, Ea, EaLength, FALSE, GetEa, GetEaLength, 0, TRUE); + + CloseHandle(h); + + if (!NT_SUCCESS(Status)) + switch (Status) + { + case STATUS_INVALID_EA_NAME: + case STATUS_EA_LIST_INCONSISTENT: + case STATUS_EA_CORRUPT_ERROR: + case STATUS_NONEXISTENT_EA_ENTRY: + case STATUS_NO_MORE_EAS: + errno = EINVAL; + return -1; + case STATUS_NO_EAS_ON_FILE: + if (0 == GetEa) + return 0; + else + { + errno = ENODATA; + return -1; + } + default: + SetLastError(RtlNtStatusToDosError(Status)); + return error(); + } + else if (0 == GetEa && + (FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) > Iosb.Information || 0 == Ea->EaValueLength)) + { + errno = ENODATA; + return -1; + } + + return (ULONG)Iosb.Information; +} + +int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) +{ + union + { + FILE_FULL_EA_INFORMATION V; + UINT8 B[1024]; + } EaBuf; + PFILE_FULL_EA_INFORMATION Ea = &EaBuf.V; + ULONG EaLength; + + size_t namelen = strlen(name); + if (254 < namelen || 0xffff < size) + { + errno = EINVAL; + return -1; + } + + EaLength = (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + namelen + 1 + size); + if (sizeof EaBuf < EaLength) + { + Ea = malloc(EaLength); /* sets errno */ + if (0 == Ea) + return -1; + } + + memset(Ea, 0, sizeof(FILE_FULL_EA_INFORMATION)); + Ea->EaNameLength = (UCHAR)namelen; + Ea->EaValueLength = (USHORT)size; + memcpy(Ea->EaName, name, namelen + 1); + memcpy(Ea->EaName + namelen + 1, value, size); + + int res = lsetea(path, Ea, EaLength); /* sets errno */ + + if (&EaBuf.V != Ea) + free(Ea); + + return res; +} + +int lgetxattr(const char *path, const char *name, void *value, size_t size0) +{ + size_t size = 0 == size0 || 0xffff < size0 ? 0xffff : size0; + union + { + FILE_GET_EA_INFORMATION V; + UINT8 B[FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + 255]; + } GetEaBuf; + PFILE_GET_EA_INFORMATION GetEa = &GetEaBuf.V; + union + { + FILE_FULL_EA_INFORMATION V; + UINT8 B[1024]; + } EaBuf; + PFILE_FULL_EA_INFORMATION Ea = &EaBuf.V; + ULONG GetEaLength, EaLength; + + size_t namelen = strlen(name); + if (254 < namelen) + { + errno = EINVAL; + return -1; + } + + EaLength = (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + namelen + 1 + size); + if (sizeof EaBuf < EaLength) + { + Ea = malloc(EaLength); /* sets errno */ + if (0 == Ea) + return -1; + } + + GetEaLength = (ULONG)(FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + namelen + 1); + memset(GetEa, 0, sizeof(FILE_GET_EA_INFORMATION)); + GetEa->EaNameLength = (UCHAR)namelen; + memcpy(GetEa->EaName, name, namelen + 1); + + int res = lgetea(path, GetEa, GetEaLength, Ea, EaLength); + if (0 < res) + { + res = Ea->EaValueLength; + if (0 == size0) + ; + else if (res <= size0) + memcpy(value, Ea->EaName + Ea->EaNameLength + 1, res); + else + { + errno = ERANGE; + res = -1; + } + } + else if (0 == res) /* should not happen! */ + { + } + + if (&EaBuf.V != Ea) + free(Ea); + + return res; +} + +int llistxattr(const char *path, char *namebuf, size_t size) +{ + PFILE_FULL_EA_INFORMATION Ea = 0; + ULONG EaLength; + + EaLength = (ULONG)(FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + 254 + 1 + 0xffff); + Ea = malloc(EaLength); /* sets errno */ + if (0 == Ea) + return -1; + + int res = lgetea(path, 0, 0, Ea, EaLength); + if (0 < res) + { + PFILE_FULL_EA_INFORMATION EaEnd = (PVOID)((PUINT8)Ea + res); + res = 0; + for (PFILE_FULL_EA_INFORMATION EaPtr = Ea; EaEnd > EaPtr; EaPtr = NEXT_EA(EaPtr, EaEnd)) + res += EaPtr->EaNameLength + 1; + + if (0 == size) + ; + else if (res <= size) + { + char *p = namebuf; + for (PFILE_FULL_EA_INFORMATION EaPtr = Ea; EaEnd > EaPtr; EaPtr = NEXT_EA(EaPtr, EaEnd)) + { + memcpy(p, EaPtr->EaName, EaPtr->EaNameLength + 1); + p += EaPtr->EaNameLength + 1; + } + } + else + { + errno = ERANGE; + res = -1; + } + } + + free(Ea); + + return res; +} + +int lremovexattr(const char *path, const char *name) +{ + return lsetxattr(path, name, 0, 0, 0); +} + +int mkdir(const char *path, fuse_mode_t mode) +{ + if (!CreateDirectoryA(path, 0/* default security */)) + return error(); + + return 0; +} + +int rmdir(const char *path) +{ + if (!RemoveDirectoryA(path)) + return error(); + + return 0; +} + +DIR *opendir(const char *path) +{ + HANDLE h = CreateFileA(path, + FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == h) + return error0(); + + size_t pathlen = strlen(path); + if (0 < pathlen && '/' == path[pathlen - 1]) + pathlen--; + + DIR *dirp = malloc(sizeof *dirp + pathlen + 3); /* sets errno */ + if (0 == dirp) + { + CloseHandle(h); + return 0; + } + + memset(dirp, 0, sizeof *dirp); + dirp->h = h; + dirp->fh = INVALID_HANDLE_VALUE; + memcpy(dirp->path, path, pathlen); + dirp->path[pathlen + 0] = '/'; + dirp->path[pathlen + 1] = '*'; + dirp->path[pathlen + 2] = '\0'; + + return dirp; +} + +int dirfd(DIR *dirp) +{ + return (int)(intptr_t)dirp->h; +} + +void rewinddir(DIR *dirp) +{ + if (INVALID_HANDLE_VALUE != dirp->fh) + { + FindClose(dirp->fh); + dirp->fh = INVALID_HANDLE_VALUE; + } +} + +struct dirent *readdir(DIR *dirp) +{ + WIN32_FIND_DATAA FindData; + struct fuse_stat *stbuf = &dirp->de.d_stat; + + if (INVALID_HANDLE_VALUE == dirp->fh) + { + dirp->fh = FindFirstFileA(dirp->path, &FindData); + if (INVALID_HANDLE_VALUE == dirp->fh) + return error0(); + } + else + { + if (!FindNextFileA(dirp->fh, &FindData)) + { + if (ERROR_NO_MORE_FILES == GetLastError()) + return 0; + return error0(); + } + } + + memset(stbuf, 0, sizeof *stbuf); + stbuf->st_mode = 0777 | + ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 0040000/* S_IFDIR */ : 0); + stbuf->st_nlink = 1; + stbuf->st_size = ((UINT64)FindData.nFileSizeHigh << 32) | ((UINT64)FindData.nFileSizeLow); + FspPosixFileTimeToUnixTime(*(PUINT64)&FindData.ftCreationTime, (void *)&stbuf->st_birthtim); + FspPosixFileTimeToUnixTime(*(PUINT64)&FindData.ftLastAccessTime, (void *)&stbuf->st_atim); + FspPosixFileTimeToUnixTime(*(PUINT64)&FindData.ftLastWriteTime, (void *)&stbuf->st_mtim); + FspPosixFileTimeToUnixTime(*(PUINT64)&FindData.ftLastWriteTime, (void *)&stbuf->st_ctim); +#if defined(FSP_FUSE_USE_STAT_EX) + stbuf->st_flags = MapFileAttributesToFlags(FindData.dwFileAttributes); +#endif + + strcpy(dirp->de.d_name, FindData.cFileName); + + return &dirp->de; +} + +int closedir(DIR *dirp) +{ + if (INVALID_HANDLE_VALUE != dirp->fh) + FindClose(dirp->fh); + + CloseHandle(dirp->h); + free(dirp); + + return 0; +} + +static int maperror(int winerrno) +{ + switch (winerrno) + { + case ERROR_INVALID_FUNCTION: + return EINVAL; + case ERROR_FILE_NOT_FOUND: + return ENOENT; + case ERROR_PATH_NOT_FOUND: + return ENOENT; + case ERROR_TOO_MANY_OPEN_FILES: + return EMFILE; + case ERROR_ACCESS_DENIED: + return EACCES; + case ERROR_INVALID_HANDLE: + return EBADF; + case ERROR_ARENA_TRASHED: + return ENOMEM; + case ERROR_NOT_ENOUGH_MEMORY: + return ENOMEM; + case ERROR_INVALID_BLOCK: + return ENOMEM; + case ERROR_BAD_ENVIRONMENT: + return E2BIG; + case ERROR_BAD_FORMAT: + return ENOEXEC; + case ERROR_INVALID_ACCESS: + return EINVAL; + case ERROR_INVALID_DATA: + return EINVAL; + case ERROR_INVALID_DRIVE: + return ENOENT; + case ERROR_CURRENT_DIRECTORY: + return EACCES; + case ERROR_NOT_SAME_DEVICE: + return EXDEV; + case ERROR_NO_MORE_FILES: + return ENOENT; + case ERROR_LOCK_VIOLATION: + return EACCES; + case ERROR_BAD_NETPATH: + return ENOENT; + case ERROR_NETWORK_ACCESS_DENIED: + return EACCES; + case ERROR_BAD_NET_NAME: + return ENOENT; + case ERROR_FILE_EXISTS: + return EEXIST; + case ERROR_CANNOT_MAKE: + return EACCES; + case ERROR_FAIL_I24: + return EACCES; + case ERROR_INVALID_PARAMETER: + return EINVAL; + case ERROR_NO_PROC_SLOTS: + return EAGAIN; + case ERROR_DRIVE_LOCKED: + return EACCES; + case ERROR_BROKEN_PIPE: + return EPIPE; + case ERROR_DISK_FULL: + return ENOSPC; + case ERROR_INVALID_TARGET_HANDLE: + return EBADF; + case ERROR_WAIT_NO_CHILDREN: + return ECHILD; + case ERROR_CHILD_NOT_COMPLETE: + return ECHILD; + case ERROR_DIRECT_ACCESS_HANDLE: + return EBADF; + case ERROR_NEGATIVE_SEEK: + return EINVAL; + case ERROR_SEEK_ON_DEVICE: + return EACCES; + case ERROR_DIR_NOT_EMPTY: + return ENOTEMPTY; + case ERROR_NOT_LOCKED: + return EACCES; + case ERROR_BAD_PATHNAME: + return ENOENT; + case ERROR_MAX_THRDS_REACHED: + return EAGAIN; + case ERROR_LOCK_FAILED: + return EACCES; + case ERROR_ALREADY_EXISTS: + return EEXIST; + case ERROR_FILENAME_EXCED_RANGE: + return ENOENT; + case ERROR_NESTING_NOT_ALLOWED: + return EAGAIN; + case ERROR_NOT_ENOUGH_QUOTA: + return ENOMEM; + default: + if (ERROR_WRITE_PROTECT <= winerrno && winerrno <= ERROR_SHARING_BUFFER_EXCEEDED) + return EACCES; + else if (ERROR_INVALID_STARTING_CODESEG <= winerrno && winerrno <= ERROR_INFLOOP_IN_RELOC_CHAIN) + return ENOEXEC; + else + return EINVAL; + } +} + +long WinFspLoad(void) +{ + return FspLoad(0); +} diff --git a/3rd_party/winfsp-1.10/samples/passthrough-fuse3/winposix.h b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/winposix.h new file mode 100644 index 00000000..b9150c22 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough-fuse3/winposix.h @@ -0,0 +1,86 @@ +/** + * @file winposix.h + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#ifndef WINPOSIX_H_INCLUDED +#define WINPOSIX_H_INCLUDED + +#define O_RDONLY _O_RDONLY +#define O_WRONLY _O_WRONLY +#define O_RDWR _O_RDWR +#define O_APPEND _O_APPEND +#define O_CREAT _O_CREAT +#define O_EXCL _O_EXCL +#define O_TRUNC _O_TRUNC + +#define PATH_MAX 1024 +#define AT_FDCWD -2 +#define AT_SYMLINK_NOFOLLOW 2 + +typedef struct _DIR DIR; +struct dirent +{ + struct fuse_stat d_stat; + char d_name[255]; +}; + +char *realpath(const char *path, char *resolved); + +int statvfs(const char *path, struct fuse_statvfs *stbuf); + +int open(const char *path, int oflag, ...); +int fstat(int fd, struct fuse_stat *stbuf); +int ftruncate(int fd, fuse_off_t size); +int pread(int fd, void *buf, size_t nbyte, fuse_off_t offset); +int pwrite(int fd, const void *buf, size_t nbyte, fuse_off_t offset); +int fsync(int fd); +int close(int fd); + +int lstat(const char *path, struct fuse_stat *stbuf); +int chmod(const char *path, fuse_mode_t mode); +int lchown(const char *path, fuse_uid_t uid, fuse_gid_t gid); +int lchflags(const char *path, uint32_t flags); +int truncate(const char *path, fuse_off_t size); +int utime(const char *path, const struct fuse_utimbuf *timbuf); +int utimensat(int dirfd, const char *path, const struct fuse_timespec times[2], int flag); +int setcrtime(const char *path, const struct fuse_timespec *tv); +int unlink(const char *path); +int rename(const char *oldpath, const char *newpath); + +int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags); +int lgetxattr(const char *path, const char *name, void *value, size_t size); +int llistxattr(const char *path, char *namebuf, size_t size); +int lremovexattr(const char *path, const char *name); + +int mkdir(const char *path, fuse_mode_t mode); +int rmdir(const char *path); + +DIR *opendir(const char *path); +int dirfd(DIR *dirp); +void rewinddir(DIR *dirp); +struct dirent *readdir(DIR *dirp); +int closedir(DIR *dirp); + +long WinFspLoad(void); +#undef fuse_main +#define fuse_main(argc, argv, ops, data)\ + (WinFspLoad(), fuse_main_real(argc, argv, ops, sizeof *(ops), data)) + +#endif diff --git a/3rd_party/winfsp-1.10/samples/passthrough/passthrough.c b/3rd_party/winfsp-1.10/samples/passthrough/passthrough.c new file mode 100644 index 00000000..43c63af5 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough/passthrough.c @@ -0,0 +1,940 @@ +/** + * @file passthrough.c + * + * @copyright 2015-2021 Bill Zissimopoulos + */ +/* + * This file is part of WinFsp. + * + * You can redistribute it and/or modify it under the terms of the GNU + * General Public License version 3 as published by the Free Software + * Foundation. + * + * Licensees holding a valid commercial license may use this software + * in accordance with the commercial license agreement provided in + * conjunction with the software. The terms and conditions of any such + * commercial license agreement shall govern, supersede, and render + * ineffective any application of the GPLv3 license to this software, + * notwithstanding of any reference thereto in the software or + * associated repository. + */ + +#include +#include + +#define PROGNAME "passthrough" +#define ALLOCATION_UNIT 4096 +#define FULLPATH_SIZE (MAX_PATH + FSP_FSCTL_TRANSACT_PATH_SIZEMAX / sizeof(WCHAR)) + +#define info(format, ...) FspServiceLog(EVENTLOG_INFORMATION_TYPE, format, __VA_ARGS__) +#define warn(format, ...) FspServiceLog(EVENTLOG_WARNING_TYPE, format, __VA_ARGS__) +#define fail(format, ...) FspServiceLog(EVENTLOG_ERROR_TYPE, format, __VA_ARGS__) + +#define ConcatPath(Ptfs, FN, FP) (0 == StringCbPrintfW(FP, sizeof FP, L"%s%s", Ptfs->Path, FN)) +#define HandleFromContext(FC) (((PTFS_FILE_CONTEXT *)(FC))->Handle) + +typedef struct +{ + FSP_FILE_SYSTEM *FileSystem; + PWSTR Path; +} PTFS; + +typedef struct +{ + HANDLE Handle; + PVOID DirBuffer; +} PTFS_FILE_CONTEXT; + +static NTSTATUS GetFileInfoInternal(HANDLE Handle, FSP_FSCTL_FILE_INFO *FileInfo) +{ + BY_HANDLE_FILE_INFORMATION ByHandleFileInfo; + + if (!GetFileInformationByHandle(Handle, &ByHandleFileInfo)) + return FspNtStatusFromWin32(GetLastError()); + + FileInfo->FileAttributes = ByHandleFileInfo.dwFileAttributes; + FileInfo->ReparseTag = 0; + FileInfo->FileSize = + ((UINT64)ByHandleFileInfo.nFileSizeHigh << 32) | (UINT64)ByHandleFileInfo.nFileSizeLow; + FileInfo->AllocationSize = (FileInfo->FileSize + ALLOCATION_UNIT - 1) + / ALLOCATION_UNIT * ALLOCATION_UNIT; + FileInfo->CreationTime = ((PLARGE_INTEGER)&ByHandleFileInfo.ftCreationTime)->QuadPart; + FileInfo->LastAccessTime = ((PLARGE_INTEGER)&ByHandleFileInfo.ftLastAccessTime)->QuadPart; + FileInfo->LastWriteTime = ((PLARGE_INTEGER)&ByHandleFileInfo.ftLastWriteTime)->QuadPart; + FileInfo->ChangeTime = FileInfo->LastWriteTime; + FileInfo->IndexNumber = 0; + FileInfo->HardLinks = 0; + + return STATUS_SUCCESS; +} + +static NTSTATUS GetVolumeInfo(FSP_FILE_SYSTEM *FileSystem, + FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + PTFS *Ptfs = (PTFS *)FileSystem->UserContext; + WCHAR Root[MAX_PATH]; + ULARGE_INTEGER TotalSize, FreeSize; + + if (!GetVolumePathName(Ptfs->Path, Root, MAX_PATH)) + return FspNtStatusFromWin32(GetLastError()); + + if (!GetDiskFreeSpaceEx(Root, 0, &TotalSize, &FreeSize)) + return FspNtStatusFromWin32(GetLastError()); + + VolumeInfo->TotalSize = TotalSize.QuadPart; + VolumeInfo->FreeSize = FreeSize.QuadPart; + + return STATUS_SUCCESS; +} + +static NTSTATUS SetVolumeLabel_(FSP_FILE_SYSTEM *FileSystem, + PWSTR VolumeLabel, + FSP_FSCTL_VOLUME_INFO *VolumeInfo) +{ + /* we do not support changing the volume label */ + return STATUS_INVALID_DEVICE_REQUEST; +} + +static NTSTATUS GetSecurityByName(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, PUINT32 PFileAttributes, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + PTFS *Ptfs = (PTFS *)FileSystem->UserContext; + WCHAR FullPath[FULLPATH_SIZE]; + HANDLE Handle; + FILE_ATTRIBUTE_TAG_INFO AttributeTagInfo; + DWORD SecurityDescriptorSizeNeeded; + NTSTATUS Result; + + if (!ConcatPath(Ptfs, FileName, FullPath)) + return STATUS_OBJECT_NAME_INVALID; + + Handle = CreateFileW(FullPath, + FILE_READ_ATTRIBUTES | READ_CONTROL, 0, 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == Handle) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + if (0 != PFileAttributes) + { + if (!GetFileInformationByHandleEx(Handle, + FileAttributeTagInfo, &AttributeTagInfo, sizeof AttributeTagInfo)) + { + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + *PFileAttributes = AttributeTagInfo.FileAttributes; + } + + if (0 != PSecurityDescriptorSize) + { + if (!GetKernelObjectSecurity(Handle, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + SecurityDescriptor, (DWORD)*PSecurityDescriptorSize, &SecurityDescriptorSizeNeeded)) + { + *PSecurityDescriptorSize = SecurityDescriptorSizeNeeded; + Result = FspNtStatusFromWin32(GetLastError()); + goto exit; + } + + *PSecurityDescriptorSize = SecurityDescriptorSizeNeeded; + } + + Result = STATUS_SUCCESS; + +exit: + if (INVALID_HANDLE_VALUE != Handle) + CloseHandle(Handle); + + return Result; +} + +static NTSTATUS Create(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + UINT32 FileAttributes, PSECURITY_DESCRIPTOR SecurityDescriptor, UINT64 AllocationSize, + PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo) +{ + PTFS *Ptfs = (PTFS *)FileSystem->UserContext; + WCHAR FullPath[FULLPATH_SIZE]; + SECURITY_ATTRIBUTES SecurityAttributes; + ULONG CreateFlags; + PTFS_FILE_CONTEXT *FileContext; + + if (!ConcatPath(Ptfs, FileName, FullPath)) + return STATUS_OBJECT_NAME_INVALID; + + FileContext = malloc(sizeof *FileContext); + if (0 == FileContext) + return STATUS_INSUFFICIENT_RESOURCES; + memset(FileContext, 0, sizeof *FileContext); + + SecurityAttributes.nLength = sizeof SecurityAttributes; + SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor; + SecurityAttributes.bInheritHandle = FALSE; + + CreateFlags = FILE_FLAG_BACKUP_SEMANTICS; + if (CreateOptions & FILE_DELETE_ON_CLOSE) + CreateFlags |= FILE_FLAG_DELETE_ON_CLOSE; + + if (CreateOptions & FILE_DIRECTORY_FILE) + { + /* + * It is not widely known but CreateFileW can be used to create directories! + * It requires the specification of both FILE_FLAG_BACKUP_SEMANTICS and + * FILE_FLAG_POSIX_SEMANTICS. It also requires that FileAttributes has + * FILE_ATTRIBUTE_DIRECTORY set. + */ + CreateFlags |= FILE_FLAG_POSIX_SEMANTICS; + FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; + } + else + FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY; + + if (0 == FileAttributes) + FileAttributes = FILE_ATTRIBUTE_NORMAL; + + FileContext->Handle = CreateFileW(FullPath, + GrantedAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SecurityAttributes, + CREATE_NEW, CreateFlags | FileAttributes, 0); + if (INVALID_HANDLE_VALUE == FileContext->Handle) + { + free(FileContext); + return FspNtStatusFromWin32(GetLastError()); + } + + *PFileContext = FileContext; + + return GetFileInfoInternal(FileContext->Handle, FileInfo); +} + +static NTSTATUS Open(FSP_FILE_SYSTEM *FileSystem, + PWSTR FileName, UINT32 CreateOptions, UINT32 GrantedAccess, + PVOID *PFileContext, FSP_FSCTL_FILE_INFO *FileInfo) +{ + PTFS *Ptfs = (PTFS *)FileSystem->UserContext; + WCHAR FullPath[FULLPATH_SIZE]; + ULONG CreateFlags; + PTFS_FILE_CONTEXT *FileContext; + + if (!ConcatPath(Ptfs, FileName, FullPath)) + return STATUS_OBJECT_NAME_INVALID; + + FileContext = malloc(sizeof *FileContext); + if (0 == FileContext) + return STATUS_INSUFFICIENT_RESOURCES; + memset(FileContext, 0, sizeof *FileContext); + + CreateFlags = FILE_FLAG_BACKUP_SEMANTICS; + if (CreateOptions & FILE_DELETE_ON_CLOSE) + CreateFlags |= FILE_FLAG_DELETE_ON_CLOSE; + + FileContext->Handle = CreateFileW(FullPath, + GrantedAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, + OPEN_EXISTING, CreateFlags, 0); + if (INVALID_HANDLE_VALUE == FileContext->Handle) + { + free(FileContext); + return FspNtStatusFromWin32(GetLastError()); + } + + *PFileContext = FileContext; + + return GetFileInfoInternal(FileContext->Handle, FileInfo); +} + +static NTSTATUS Overwrite(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 FileAttributes, BOOLEAN ReplaceFileAttributes, UINT64 AllocationSize, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + HANDLE Handle = HandleFromContext(FileContext); + FILE_BASIC_INFO BasicInfo = { 0 }; + FILE_ALLOCATION_INFO AllocationInfo = { 0 }; + FILE_ATTRIBUTE_TAG_INFO AttributeTagInfo; + + if (ReplaceFileAttributes) + { + if (0 == FileAttributes) + FileAttributes = FILE_ATTRIBUTE_NORMAL; + + BasicInfo.FileAttributes = FileAttributes; + if (!SetFileInformationByHandle(Handle, + FileBasicInfo, &BasicInfo, sizeof BasicInfo)) + return FspNtStatusFromWin32(GetLastError()); + } + else if (0 != FileAttributes) + { + if (!GetFileInformationByHandleEx(Handle, + FileAttributeTagInfo, &AttributeTagInfo, sizeof AttributeTagInfo)) + return FspNtStatusFromWin32(GetLastError()); + + BasicInfo.FileAttributes = FileAttributes | AttributeTagInfo.FileAttributes; + if (BasicInfo.FileAttributes ^ FileAttributes) + { + if (!SetFileInformationByHandle(Handle, + FileBasicInfo, &BasicInfo, sizeof BasicInfo)) + return FspNtStatusFromWin32(GetLastError()); + } + } + + if (!SetFileInformationByHandle(Handle, + FileAllocationInfo, &AllocationInfo, sizeof AllocationInfo)) + return FspNtStatusFromWin32(GetLastError()); + + return GetFileInfoInternal(Handle, FileInfo); +} + +static VOID Cleanup(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PWSTR FileName, ULONG Flags) +{ + HANDLE Handle = HandleFromContext(FileContext); + + if (Flags & FspCleanupDelete) + { + CloseHandle(Handle); + + /* this will make all future uses of Handle to fail with STATUS_INVALID_HANDLE */ + HandleFromContext(FileContext) = INVALID_HANDLE_VALUE; + } +} + +static VOID Close(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext0) +{ + PTFS_FILE_CONTEXT *FileContext = FileContext0; + HANDLE Handle = HandleFromContext(FileContext); + + CloseHandle(Handle); + + FspFileSystemDeleteDirectoryBuffer(&FileContext->DirBuffer); + free(FileContext); +} + +static NTSTATUS Read(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PVOID Buffer, UINT64 Offset, ULONG Length, + PULONG PBytesTransferred) +{ + HANDLE Handle = HandleFromContext(FileContext); + OVERLAPPED Overlapped = { 0 }; + + Overlapped.Offset = (DWORD)Offset; + Overlapped.OffsetHigh = (DWORD)(Offset >> 32); + + if (!ReadFile(Handle, Buffer, Length, PBytesTransferred, &Overlapped)) + return FspNtStatusFromWin32(GetLastError()); + + return STATUS_SUCCESS; +} + +static NTSTATUS Write(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PVOID Buffer, UINT64 Offset, ULONG Length, + BOOLEAN WriteToEndOfFile, BOOLEAN ConstrainedIo, + PULONG PBytesTransferred, FSP_FSCTL_FILE_INFO *FileInfo) +{ + HANDLE Handle = HandleFromContext(FileContext); + LARGE_INTEGER FileSize; + OVERLAPPED Overlapped = { 0 }; + + if (ConstrainedIo) + { + if (!GetFileSizeEx(Handle, &FileSize)) + return FspNtStatusFromWin32(GetLastError()); + + if (Offset >= (UINT64)FileSize.QuadPart) + return STATUS_SUCCESS; + if (Offset + Length > (UINT64)FileSize.QuadPart) + Length = (ULONG)((UINT64)FileSize.QuadPart - Offset); + } + + Overlapped.Offset = (DWORD)Offset; + Overlapped.OffsetHigh = (DWORD)(Offset >> 32); + + if (!WriteFile(Handle, Buffer, Length, PBytesTransferred, &Overlapped)) + return FspNtStatusFromWin32(GetLastError()); + + return GetFileInfoInternal(Handle, FileInfo); +} + +NTSTATUS Flush(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + HANDLE Handle = HandleFromContext(FileContext); + + /* we do not flush the whole volume, so just return SUCCESS */ + if (0 == Handle) + return STATUS_SUCCESS; + + if (!FlushFileBuffers(Handle)) + return FspNtStatusFromWin32(GetLastError()); + + return GetFileInfoInternal(Handle, FileInfo); +} + +static NTSTATUS GetFileInfo(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + HANDLE Handle = HandleFromContext(FileContext); + + return GetFileInfoInternal(Handle, FileInfo); +} + +static NTSTATUS SetBasicInfo(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT32 FileAttributes, + UINT64 CreationTime, UINT64 LastAccessTime, UINT64 LastWriteTime, UINT64 ChangeTime, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + HANDLE Handle = HandleFromContext(FileContext); + FILE_BASIC_INFO BasicInfo = { 0 }; + + if (INVALID_FILE_ATTRIBUTES == FileAttributes) + FileAttributes = 0; + else if (0 == FileAttributes) + FileAttributes = FILE_ATTRIBUTE_NORMAL; + + BasicInfo.FileAttributes = FileAttributes; + BasicInfo.CreationTime.QuadPart = CreationTime; + BasicInfo.LastAccessTime.QuadPart = LastAccessTime; + BasicInfo.LastWriteTime.QuadPart = LastWriteTime; + //BasicInfo.ChangeTime = ChangeTime; + + if (!SetFileInformationByHandle(Handle, + FileBasicInfo, &BasicInfo, sizeof BasicInfo)) + return FspNtStatusFromWin32(GetLastError()); + + return GetFileInfoInternal(Handle, FileInfo); +} + +static NTSTATUS SetFileSize(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, UINT64 NewSize, BOOLEAN SetAllocationSize, + FSP_FSCTL_FILE_INFO *FileInfo) +{ + HANDLE Handle = HandleFromContext(FileContext); + FILE_ALLOCATION_INFO AllocationInfo; + FILE_END_OF_FILE_INFO EndOfFileInfo; + + if (SetAllocationSize) + { + /* + * This file system does not maintain AllocationSize, although NTFS clearly can. + * However it must always be FileSize <= AllocationSize and NTFS will make sure + * to truncate the FileSize if it sees an AllocationSize < FileSize. + * + * If OTOH a very large AllocationSize is passed, the call below will increase + * the AllocationSize of the underlying file, although our file system does not + * expose this fact. This AllocationSize is only temporary as NTFS will reset + * the AllocationSize of the underlying file when it is closed. + */ + + AllocationInfo.AllocationSize.QuadPart = NewSize; + + if (!SetFileInformationByHandle(Handle, + FileAllocationInfo, &AllocationInfo, sizeof AllocationInfo)) + return FspNtStatusFromWin32(GetLastError()); + } + else + { + EndOfFileInfo.EndOfFile.QuadPart = NewSize; + + if (!SetFileInformationByHandle(Handle, + FileEndOfFileInfo, &EndOfFileInfo, sizeof EndOfFileInfo)) + return FspNtStatusFromWin32(GetLastError()); + } + + return GetFileInfoInternal(Handle, FileInfo); +} + +static NTSTATUS Rename(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PWSTR FileName, PWSTR NewFileName, BOOLEAN ReplaceIfExists) +{ + PTFS *Ptfs = (PTFS *)FileSystem->UserContext; + WCHAR FullPath[FULLPATH_SIZE], NewFullPath[FULLPATH_SIZE]; + + if (!ConcatPath(Ptfs, FileName, FullPath)) + return STATUS_OBJECT_NAME_INVALID; + + if (!ConcatPath(Ptfs, NewFileName, NewFullPath)) + return STATUS_OBJECT_NAME_INVALID; + + if (!MoveFileExW(FullPath, NewFullPath, ReplaceIfExists ? MOVEFILE_REPLACE_EXISTING : 0)) + return FspNtStatusFromWin32(GetLastError()); + + return STATUS_SUCCESS; +} + +static NTSTATUS GetSecurity(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + PSECURITY_DESCRIPTOR SecurityDescriptor, SIZE_T *PSecurityDescriptorSize) +{ + HANDLE Handle = HandleFromContext(FileContext); + DWORD SecurityDescriptorSizeNeeded; + + if (!GetKernelObjectSecurity(Handle, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + SecurityDescriptor, (DWORD)*PSecurityDescriptorSize, &SecurityDescriptorSizeNeeded)) + { + *PSecurityDescriptorSize = SecurityDescriptorSizeNeeded; + return FspNtStatusFromWin32(GetLastError()); + } + + *PSecurityDescriptorSize = SecurityDescriptorSizeNeeded; + + return STATUS_SUCCESS; +} + +static NTSTATUS SetSecurity(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, + SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR ModificationDescriptor) +{ + HANDLE Handle = HandleFromContext(FileContext); + + if (!SetKernelObjectSecurity(Handle, SecurityInformation, ModificationDescriptor)) + return FspNtStatusFromWin32(GetLastError()); + + return STATUS_SUCCESS; +} + +static NTSTATUS ReadDirectory(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext0, PWSTR Pattern, PWSTR Marker, + PVOID Buffer, ULONG BufferLength, PULONG PBytesTransferred) +{ + PTFS *Ptfs = (PTFS *)FileSystem->UserContext; + PTFS_FILE_CONTEXT *FileContext = FileContext0; + HANDLE Handle = HandleFromContext(FileContext); + WCHAR FullPath[FULLPATH_SIZE]; + ULONG Length, PatternLength; + HANDLE FindHandle; + WIN32_FIND_DATAW FindData; + union + { + UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) + MAX_PATH * sizeof(WCHAR)]; + FSP_FSCTL_DIR_INFO D; + } DirInfoBuf; + FSP_FSCTL_DIR_INFO *DirInfo = &DirInfoBuf.D; + NTSTATUS DirBufferResult; + + DirBufferResult = STATUS_SUCCESS; + if (FspFileSystemAcquireDirectoryBuffer(&FileContext->DirBuffer, 0 == Marker, &DirBufferResult)) + { + if (0 == Pattern) + Pattern = L"*"; + PatternLength = (ULONG)wcslen(Pattern); + + Length = GetFinalPathNameByHandleW(Handle, FullPath, FULLPATH_SIZE - 1, 0); + if (0 == Length) + DirBufferResult = FspNtStatusFromWin32(GetLastError()); + else if (Length + 1 + PatternLength >= FULLPATH_SIZE) + DirBufferResult = STATUS_OBJECT_NAME_INVALID; + if (!NT_SUCCESS(DirBufferResult)) + { + FspFileSystemReleaseDirectoryBuffer(&FileContext->DirBuffer); + return DirBufferResult; + } + + if (L'\\' != FullPath[Length - 1]) + FullPath[Length++] = L'\\'; + memcpy(FullPath + Length, Pattern, PatternLength * sizeof(WCHAR)); + FullPath[Length + PatternLength] = L'\0'; + + FindHandle = FindFirstFileW(FullPath, &FindData); + if (INVALID_HANDLE_VALUE != FindHandle) + { + do + { + memset(DirInfo, 0, sizeof *DirInfo); + Length = (ULONG)wcslen(FindData.cFileName); + DirInfo->Size = (UINT16)(FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) + Length * sizeof(WCHAR)); + DirInfo->FileInfo.FileAttributes = FindData.dwFileAttributes; + DirInfo->FileInfo.ReparseTag = 0; + DirInfo->FileInfo.FileSize = + ((UINT64)FindData.nFileSizeHigh << 32) | (UINT64)FindData.nFileSizeLow; + DirInfo->FileInfo.AllocationSize = (DirInfo->FileInfo.FileSize + ALLOCATION_UNIT - 1) + / ALLOCATION_UNIT * ALLOCATION_UNIT; + DirInfo->FileInfo.CreationTime = ((PLARGE_INTEGER)&FindData.ftCreationTime)->QuadPart; + DirInfo->FileInfo.LastAccessTime = ((PLARGE_INTEGER)&FindData.ftLastAccessTime)->QuadPart; + DirInfo->FileInfo.LastWriteTime = ((PLARGE_INTEGER)&FindData.ftLastWriteTime)->QuadPart; + DirInfo->FileInfo.ChangeTime = DirInfo->FileInfo.LastWriteTime; + DirInfo->FileInfo.IndexNumber = 0; + DirInfo->FileInfo.HardLinks = 0; + memcpy(DirInfo->FileNameBuf, FindData.cFileName, Length * sizeof(WCHAR)); + + if (!FspFileSystemFillDirectoryBuffer(&FileContext->DirBuffer, DirInfo, &DirBufferResult)) + break; + } while (FindNextFileW(FindHandle, &FindData)); + + FindClose(FindHandle); + } + + FspFileSystemReleaseDirectoryBuffer(&FileContext->DirBuffer); + } + + if (!NT_SUCCESS(DirBufferResult)) + return DirBufferResult; + + FspFileSystemReadDirectoryBuffer(&FileContext->DirBuffer, + Marker, Buffer, BufferLength, PBytesTransferred); + + return STATUS_SUCCESS; +} + +static NTSTATUS SetDelete(FSP_FILE_SYSTEM *FileSystem, + PVOID FileContext, PWSTR FileName, BOOLEAN DeleteFile) +{ + HANDLE Handle = HandleFromContext(FileContext); + FILE_DISPOSITION_INFO DispositionInfo; + + DispositionInfo.DeleteFile = DeleteFile; + + if (!SetFileInformationByHandle(Handle, + FileDispositionInfo, &DispositionInfo, sizeof DispositionInfo)) + return FspNtStatusFromWin32(GetLastError()); + + return STATUS_SUCCESS; +} + +static FSP_FILE_SYSTEM_INTERFACE PtfsInterface = +{ + .GetVolumeInfo = GetVolumeInfo, + .SetVolumeLabel = SetVolumeLabel_, + .GetSecurityByName = GetSecurityByName, + .Create = Create, + .Open = Open, + .Overwrite = Overwrite, + .Cleanup = Cleanup, + .Close = Close, + .Read = Read, + .Write = Write, + .Flush = Flush, + .GetFileInfo = GetFileInfo, + .SetBasicInfo = SetBasicInfo, + .SetFileSize = SetFileSize, + .Rename = Rename, + .GetSecurity = GetSecurity, + .SetSecurity = SetSecurity, + .ReadDirectory = ReadDirectory, + .SetDelete = SetDelete, +}; + +static VOID PtfsDelete(PTFS *Ptfs); + +static NTSTATUS PtfsCreate(PWSTR Path, PWSTR VolumePrefix, PWSTR MountPoint, UINT32 DebugFlags, + PTFS **PPtfs) +{ + WCHAR FullPath[MAX_PATH]; + ULONG Length; + HANDLE Handle; + FILETIME CreationTime; + DWORD LastError; + FSP_FSCTL_VOLUME_PARAMS VolumeParams; + PTFS *Ptfs = 0; + NTSTATUS Result; + + *PPtfs = 0; + + Handle = CreateFileW( + Path, FILE_READ_ATTRIBUTES, 0, 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + if (INVALID_HANDLE_VALUE == Handle) + return FspNtStatusFromWin32(GetLastError()); + + Length = GetFinalPathNameByHandleW(Handle, FullPath, FULLPATH_SIZE - 1, 0); + if (0 == Length) + { + LastError = GetLastError(); + CloseHandle(Handle); + return FspNtStatusFromWin32(LastError); + } + if (L'\\' == FullPath[Length - 1]) + FullPath[--Length] = L'\0'; + + if (!GetFileTime(Handle, &CreationTime, 0, 0)) + { + LastError = GetLastError(); + CloseHandle(Handle); + return FspNtStatusFromWin32(LastError); + } + + CloseHandle(Handle); + + /* from now on we must goto exit on failure */ + + Ptfs = malloc(sizeof *Ptfs); + if (0 == Ptfs) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + memset(Ptfs, 0, sizeof *Ptfs); + + Length = (Length + 1) * sizeof(WCHAR); + Ptfs->Path = malloc(Length); + if (0 == Ptfs->Path) + { + Result = STATUS_INSUFFICIENT_RESOURCES; + goto exit; + } + memcpy(Ptfs->Path, FullPath, Length); + + memset(&VolumeParams, 0, sizeof VolumeParams); + VolumeParams.SectorSize = ALLOCATION_UNIT; + VolumeParams.SectorsPerAllocationUnit = 1; + VolumeParams.VolumeCreationTime = ((PLARGE_INTEGER)&CreationTime)->QuadPart; + VolumeParams.VolumeSerialNumber = 0; + VolumeParams.FileInfoTimeout = 1000; + VolumeParams.CaseSensitiveSearch = 0; + VolumeParams.CasePreservedNames = 1; + VolumeParams.UnicodeOnDisk = 1; + VolumeParams.PersistentAcls = 1; + VolumeParams.PostCleanupWhenModifiedOnly = 1; + VolumeParams.PassQueryDirectoryPattern = 1; + VolumeParams.FlushAndPurgeOnCleanup = 1; + VolumeParams.UmFileContextIsUserContext2 = 1; + if (0 != VolumePrefix) + wcscpy_s(VolumeParams.Prefix, sizeof VolumeParams.Prefix / sizeof(WCHAR), VolumePrefix); + wcscpy_s(VolumeParams.FileSystemName, sizeof VolumeParams.FileSystemName / sizeof(WCHAR), + L"" PROGNAME); + + Result = FspFileSystemCreate( + VolumeParams.Prefix[0] ? L"" FSP_FSCTL_NET_DEVICE_NAME : L"" FSP_FSCTL_DISK_DEVICE_NAME, + &VolumeParams, + &PtfsInterface, + &Ptfs->FileSystem); + if (!NT_SUCCESS(Result)) + goto exit; + Ptfs->FileSystem->UserContext = Ptfs; + + Result = FspFileSystemSetMountPoint(Ptfs->FileSystem, MountPoint); + if (!NT_SUCCESS(Result)) + goto exit; + + FspFileSystemSetDebugLog(Ptfs->FileSystem, DebugFlags); + + Result = STATUS_SUCCESS; + +exit: + if (NT_SUCCESS(Result)) + *PPtfs = Ptfs; + else if (0 != Ptfs) + PtfsDelete(Ptfs); + + return Result; +} + +static VOID PtfsDelete(PTFS *Ptfs) +{ + if (0 != Ptfs->FileSystem) + FspFileSystemDelete(Ptfs->FileSystem); + + if (0 != Ptfs->Path) + free(Ptfs->Path); + + free(Ptfs); +} + +static NTSTATUS EnableBackupRestorePrivileges(VOID) +{ + union + { + TOKEN_PRIVILEGES P; + UINT8 B[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)]; + } Privileges; + HANDLE Token; + + Privileges.P.PrivilegeCount = 2; + Privileges.P.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + Privileges.P.Privileges[1].Attributes = SE_PRIVILEGE_ENABLED; + + if (!LookupPrivilegeValueW(0, SE_BACKUP_NAME, &Privileges.P.Privileges[0].Luid) || + !LookupPrivilegeValueW(0, SE_RESTORE_NAME, &Privileges.P.Privileges[1].Luid)) + return FspNtStatusFromWin32(GetLastError()); + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &Token)) + return FspNtStatusFromWin32(GetLastError()); + + if (!AdjustTokenPrivileges(Token, FALSE, &Privileges.P, 0, 0, 0)) + { + CloseHandle(Token); + + return FspNtStatusFromWin32(GetLastError()); + } + + CloseHandle(Token); + + return STATUS_SUCCESS; +} + +static ULONG wcstol_deflt(wchar_t *w, ULONG deflt) +{ + wchar_t *endp; + ULONG ul = wcstol(w, &endp, 0); + return L'\0' != w[0] && L'\0' == *endp ? ul : deflt; +} + +static NTSTATUS SvcStart(FSP_SERVICE *Service, ULONG argc, PWSTR *argv) +{ +#define argtos(v) if (arge > ++argp) v = *argp; else goto usage +#define argtol(v) if (arge > ++argp) v = wcstol_deflt(*argp, v); else goto usage + + wchar_t **argp, **arge; + PWSTR DebugLogFile = 0; + ULONG DebugFlags = 0; + PWSTR VolumePrefix = 0; + PWSTR PassThrough = 0; + PWSTR MountPoint = 0; + HANDLE DebugLogHandle = INVALID_HANDLE_VALUE; + WCHAR PassThroughBuf[MAX_PATH]; + PTFS *Ptfs = 0; + NTSTATUS Result; + + for (argp = argv + 1, arge = argv + argc; arge > argp; argp++) + { + if (L'-' != argp[0][0]) + break; + switch (argp[0][1]) + { + case L'?': + goto usage; + case L'd': + argtol(DebugFlags); + break; + case L'D': + argtos(DebugLogFile); + break; + case L'm': + argtos(MountPoint); + break; + case L'p': + argtos(PassThrough); + break; + case L'u': + argtos(VolumePrefix); + break; + default: + goto usage; + } + } + + if (arge > argp) + goto usage; + + if (0 == PassThrough && 0 != VolumePrefix) + { + PWSTR P; + + P = wcschr(VolumePrefix, L'\\'); + if (0 != P && L'\\' != P[1]) + { + P = wcschr(P + 1, L'\\'); + if (0 != P && + ( + (L'A' <= P[1] && P[1] <= L'Z') || + (L'a' <= P[1] && P[1] <= L'z') + ) && + L'$' == P[2]) + { + StringCbPrintf(PassThroughBuf, sizeof PassThroughBuf, L"%c:%s", P[1], P + 3); + PassThrough = PassThroughBuf; + } + } + } + + if (0 == PassThrough || 0 == MountPoint) + goto usage; + + EnableBackupRestorePrivileges(); + + if (0 != DebugLogFile) + { + if (0 == wcscmp(L"-", DebugLogFile)) + DebugLogHandle = GetStdHandle(STD_ERROR_HANDLE); + else + DebugLogHandle = CreateFileW( + DebugLogFile, + FILE_APPEND_DATA, + FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0); + if (INVALID_HANDLE_VALUE == DebugLogHandle) + { + fail(L"cannot open debug log file"); + goto usage; + } + + FspDebugLogSetHandle(DebugLogHandle); + } + + Result = PtfsCreate(PassThrough, VolumePrefix, MountPoint, DebugFlags, &Ptfs); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot create file system"); + goto exit; + } + + Result = FspFileSystemStartDispatcher(Ptfs->FileSystem, 0); + if (!NT_SUCCESS(Result)) + { + fail(L"cannot start file system"); + goto exit; + } + + MountPoint = FspFileSystemMountPoint(Ptfs->FileSystem); + + info(L"%s%s%s -p %s -m %s", + L"" PROGNAME, + 0 != VolumePrefix && L'\0' != VolumePrefix[0] ? L" -u " : L"", + 0 != VolumePrefix && L'\0' != VolumePrefix[0] ? VolumePrefix : L"", + PassThrough, + MountPoint); + + Service->UserContext = Ptfs; + Result = STATUS_SUCCESS; + +exit: + if (!NT_SUCCESS(Result) && 0 != Ptfs) + PtfsDelete(Ptfs); + + return Result; + +usage: + static wchar_t usage[] = L"" + "usage: %s OPTIONS\n" + "\n" + "options:\n" + " -d DebugFlags [-1: enable all debug logs]\n" + " -D DebugLogFile [file path; use - for stderr]\n" + " -u \\Server\\Share [UNC prefix (single backslash)]\n" + " -p Directory [directory to expose as pass through file system]\n" + " -m MountPoint [X:|*|directory]\n"; + + fail(usage, L"" PROGNAME); + + return STATUS_UNSUCCESSFUL; + +#undef argtos +#undef argtol +} + +static NTSTATUS SvcStop(FSP_SERVICE *Service) +{ + PTFS *Ptfs = Service->UserContext; + + FspFileSystemStopDispatcher(Ptfs->FileSystem); + PtfsDelete(Ptfs); + + return STATUS_SUCCESS; +} + +int wmain(int argc, wchar_t **argv) +{ + if (!NT_SUCCESS(FspLoad(0))) + return ERROR_DELAY_LOAD_FAILED; + + return FspServiceRun(L"" PROGNAME, SvcStart, SvcStop, 0); +} diff --git a/3rd_party/winfsp-1.10/samples/passthrough/passthrough.sln b/3rd_party/winfsp-1.10/samples/passthrough/passthrough.sln new file mode 100644 index 00000000..fbea6c04 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough/passthrough.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "passthrough", "passthrough.vcxproj", "{9E0E5997-7316-4818-A130-00B3AF1AD354}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9E0E5997-7316-4818-A130-00B3AF1AD354}.Debug|x64.ActiveCfg = Debug|x64 + {9E0E5997-7316-4818-A130-00B3AF1AD354}.Debug|x64.Build.0 = Debug|x64 + {9E0E5997-7316-4818-A130-00B3AF1AD354}.Debug|x86.ActiveCfg = Debug|Win32 + {9E0E5997-7316-4818-A130-00B3AF1AD354}.Debug|x86.Build.0 = Debug|Win32 + {9E0E5997-7316-4818-A130-00B3AF1AD354}.Release|x64.ActiveCfg = Release|x64 + {9E0E5997-7316-4818-A130-00B3AF1AD354}.Release|x64.Build.0 = Release|x64 + {9E0E5997-7316-4818-A130-00B3AF1AD354}.Release|x86.ActiveCfg = Release|Win32 + {9E0E5997-7316-4818-A130-00B3AF1AD354}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/3rd_party/winfsp-1.10/samples/passthrough/passthrough.vcxproj b/3rd_party/winfsp-1.10/samples/passthrough/passthrough.vcxproj new file mode 100644 index 00000000..bb497572 --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough/passthrough.vcxproj @@ -0,0 +1,182 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + {9E0E5997-7316-4818-A130-00B3AF1AD354} + Win32Proj + passthrough + $(LatestTargetPlatformVersion) + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + + + Console + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + $(MSBuildProgramFiles32)\WinFsp\inc + MultiThreaded + + + Console + true + true + true + $(MSBuildProgramFiles32)\WinFsp\lib\winfsp-$(PlatformTarget).lib;%(AdditionalDependencies) + winfsp-$(PlatformTarget).dll + + + + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/samples/passthrough/passthrough.vcxproj.filters b/3rd_party/winfsp-1.10/samples/passthrough/passthrough.vcxproj.filters new file mode 100644 index 00000000..f9e6e47e --- /dev/null +++ b/3rd_party/winfsp-1.10/samples/passthrough/passthrough.vcxproj.filters @@ -0,0 +1,14 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source + + + \ No newline at end of file diff --git a/3rd_party/winfsp-1.10/sym/fsptool-x64.pdb b/3rd_party/winfsp-1.10/sym/fsptool-x64.pdb new file mode 100644 index 00000000..16631b7a Binary files /dev/null and b/3rd_party/winfsp-1.10/sym/fsptool-x64.pdb differ diff --git a/3rd_party/winfsp-1.10/sym/fsptool-x86.pdb b/3rd_party/winfsp-1.10/sym/fsptool-x86.pdb new file mode 100644 index 00000000..f244d2f3 Binary files /dev/null and b/3rd_party/winfsp-1.10/sym/fsptool-x86.pdb differ diff --git a/3rd_party/winfsp-1.10/sym/launchctl-x64.pdb b/3rd_party/winfsp-1.10/sym/launchctl-x64.pdb new file mode 100644 index 00000000..ee073f1e Binary files /dev/null and b/3rd_party/winfsp-1.10/sym/launchctl-x64.pdb differ diff --git a/3rd_party/winfsp-1.10/sym/launchctl-x86.pdb b/3rd_party/winfsp-1.10/sym/launchctl-x86.pdb new file mode 100644 index 00000000..e2dd46d6 Binary files /dev/null and b/3rd_party/winfsp-1.10/sym/launchctl-x86.pdb differ diff --git a/3rd_party/winfsp-1.10/sym/launcher-x64.pdb b/3rd_party/winfsp-1.10/sym/launcher-x64.pdb new file mode 100644 index 00000000..8aa1c0c1 Binary files /dev/null and b/3rd_party/winfsp-1.10/sym/launcher-x64.pdb differ diff --git a/3rd_party/winfsp-1.10/sym/launcher-x86.pdb b/3rd_party/winfsp-1.10/sym/launcher-x86.pdb new file mode 100644 index 00000000..0f02455b Binary files /dev/null and b/3rd_party/winfsp-1.10/sym/launcher-x86.pdb differ diff --git a/3rd_party/winfsp-1.10/sym/memfs-x64.pdb b/3rd_party/winfsp-1.10/sym/memfs-x64.pdb new file mode 100644 index 00000000..bb7b7c34 Binary files /dev/null and b/3rd_party/winfsp-1.10/sym/memfs-x64.pdb differ diff --git a/3rd_party/winfsp-1.10/sym/memfs-x86.pdb b/3rd_party/winfsp-1.10/sym/memfs-x86.pdb new file mode 100644 index 00000000..dd1865ec Binary files /dev/null and b/3rd_party/winfsp-1.10/sym/memfs-x86.pdb differ diff --git a/3rd_party/winfsp-1.10/sym/winfsp-x64.dll.pdb b/3rd_party/winfsp-1.10/sym/winfsp-x64.dll.pdb new file mode 100644 index 00000000..b2e35eeb Binary files /dev/null and b/3rd_party/winfsp-1.10/sym/winfsp-x64.dll.pdb differ diff --git a/3rd_party/winfsp-1.10/sym/winfsp-x64.sys.pdb b/3rd_party/winfsp-1.10/sym/winfsp-x64.sys.pdb new file mode 100644 index 00000000..baa258a5 Binary files /dev/null and b/3rd_party/winfsp-1.10/sym/winfsp-x64.sys.pdb differ diff --git a/3rd_party/winfsp-1.10/sym/winfsp-x86.dll.pdb b/3rd_party/winfsp-1.10/sym/winfsp-x86.dll.pdb new file mode 100644 index 00000000..7c1b0bbc Binary files /dev/null and b/3rd_party/winfsp-1.10/sym/winfsp-x86.dll.pdb differ diff --git a/3rd_party/winfsp-1.10/sym/winfsp-x86.sys.pdb b/3rd_party/winfsp-1.10/sym/winfsp-x86.sys.pdb new file mode 100644 index 00000000..6b849569 Binary files /dev/null and b/3rd_party/winfsp-1.10/sym/winfsp-x86.sys.pdb differ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..f2088128 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,20 @@ +# Changelog +## 2.0.0-rc +* \#5 S3 mounts should support encryption +* \#136: Simultaneous Skynet uploads not being tracked in portals +* \#131: Label 'centos7' amd64 build as generic 'linux' amd64 +* \#132: Switch to `XChaCha20-Poly1305` for Skynet encryption +* Added replay protection to remote mounts +* Support TUS uploads on Skynet (large upload support) +* Removed 1.1.x and 1.2.x releases +* Refactored build scripts +* Support remote FUSE base64 writes +* Switched to `XChaCha20-Poly1305` for remote mounts +* Updated `cURL` to v7.77.0 +* Updated `OpenSSL` to v1.1.1k +* Updated `libmicrohttpd` to v0.9.73 +* Fixed encrypted Skynet import +* Implemented chunked read and write +* Removed `repertory-ui` support +* Case refactoring +* Writes for non-cached files are performed in chunks of 8Mib diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..033bc5ae --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,508 @@ +cmake_minimum_required(VERSION 3.22) +project(repertory) + +include(CheckIncludeFiles) +include(CheckIncludeFileCXX) +include(ExternalProject) + +set(AWSCPP_VERSION 1.9.206) +set(BOOST_MAJOR_VERSION 1) +set(BOOST_MINOR_VERSION 78) +set(BOOST_MSVC_TOOLSET msvc-14.2) +set(BOOST_MSVC_TOOLSET_DLL 142) +set(BOOST_REVISION 0) +set(BOOST_VERSION ${BOOST_MAJOR_VERSION}.${BOOST_MINOR_VERSION}.${BOOST_REVISION}) +set(BOOST_VERSION_DL ${BOOST_MAJOR_VERSION}_${BOOST_MINOR_VERSION}_${BOOST_REVISION}) +set(BOOST_VERSION_DLL ${BOOST_MAJOR_VERSION}_${BOOST_MINOR_VERSION}) +set(CRYPTOPP_VERSION 8_2_0) +set(CURL_VERSION 7_81_0) +set(GTEST_VERSION 1.11.0) +set(LIBHTTPSERVER_VERSION 0.18.2) +set(LIBMICROHTTPD_VERSION 0.9.73) +set(LIBUUID_VERSION 1.6.2) +set(OPENSSL_VERSION 1_1_1m) +set(ROCKSDB_VERSION 6.27.3) +set(WINFSP_VERSION 1.10) +set(ZLIB_VERSION v1.2.11) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +option(REPERTORY_ENABLE_TESTING "Enable testing" ON) + +option(REPERTORY_ENABLE_S3 "Enable S3 mount support" OFF) +if (REPERTORY_ENABLE_S3) + add_definitions(-DREPERTORY_ENABLE_S3) + option(REPERTORY_ENABLE_S3_TESTING "Enable S3 unit tests" OFF) + if (REPERTORY_ENABLE_S3_TESTING) + add_definitions(-DREPERTORY_ENABLE_S3_TESTING) + endif () +endif () + +option(REPERTORY_ENABLE_SKYNET "Enable Skynet mount support" ON) +if (REPERTORY_ENABLE_SKYNET) + add_definitions(-DREPERTORY_ENABLE_SKYNET) + option(REPERTORY_ENABLE_SKYNET_PREMIUM_TESTS "Enable Skynet premium portal unit tests" OFF) + if (REPERTORY_ENABLE_SKYNET_PREMIUM_TESTS) + add_definitions(-DREPERTORY_ENABLE_SKYNET_PREMIUM_TESTS) + endif() +endif () + +set(REPERTORY_MAJOR 2) +set(REPERTORY_MINOR 0) +set(REPERTORY_REV 0) +set(REPERTORY_RELEASE_NUM 2000000) +set(REPERTORY_RELEASE_ITER rc) +set(REPERTORY_VERSION ${REPERTORY_MAJOR}.${REPERTORY_MINOR}.${REPERTORY_REV}-${REPERTORY_RELEASE_ITER}) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + set(IS_CLANG_COMPILER TRUE) +endif() + +if(UNIX AND NOT APPLE) + if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + set(FREEBSD TRUE) + else() + set(LINUX TRUE) + endif() +elseif(UNIX AND APPLE) + set(MACOS TRUE) + set(CMAKE_OSX_ARCHITECTURES x86_64) +endif() + +if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "4") + message(FATAL_ERROR "32-bit compilation is not supported") +else() + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*|arm64.*|ARM64.*)") + set(REPERTORY_UNIX_ARCH armv8-a) + set(IS_ARM64 TRUE) + add_definitions("-DIS_ARM64") + else() + set(REPERTORY_UNIX_ARCH x86-64) + endif() + + set(WINFSP_LIBRARY_BASENAME winfsp-x64) + set(REPERTORY_LIB_DIR lib64) +endif() + +if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/cacert.pem") + file(REMOVE ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/cacert.pem) +endif() +file(DOWNLOAD https://curl.haxx.se/ca/cacert.pem ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/cacert.pem) + +execute_process( + COMMAND git rev-parse --short HEAD + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE REPERTORY_GIT_REV + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +if (NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() + +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(THREADS_PREFER_PTHREAD_FLAG ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif () +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(DEBUG_EXTRA d) + set(DEBUG_EXTRA2 -d) + set(DEBUG_EXTRA3 _d) + set(BOOST_DEBUG_EXTRA gd-) + set(CMAKE_BUILD_TYPE_LOWER debug) +else() + set(CMAKE_BUILD_TYPE_LOWER release) +endif() +string(TOLOWER "${CMAKE_GENERATOR}" CMAKE_GENERATOR_LOWER) + +set(EXTERNAL_BUILD_ROOT ${CMAKE_BINARY_DIR}/external) +set(EXTERNAL_BUILD_TYPE ${CMAKE_BUILD_TYPE}) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/common.cpp.in ${CMAKE_CURRENT_SOURCE_DIR}/src/common.cpp @ONLY) + +link_directories( + ${EXTERNAL_BUILD_ROOT}/lib +) + +if (UNIX) + set(STATIC_LIB_EXT a) + + set(REPERTORY_C_FLAGS_LIST + ${REPERTORY_C_FLAGS_LIST} + -march=${REPERTORY_UNIX_ARCH} + -mtune=generic + -D_FILE_OFFSET_BITS=64 + ) + + set(REPERTORY_CXX_FLAGS_LIST + ${REPERTORY_CXX_FLAGS_LIST} + -march=${REPERTORY_UNIX_ARCH} + -mtune=generic + -D_FILE_OFFSET_BITS=64 + -std=c++${CMAKE_CXX_STANDARD} + ) + + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(REPERTORY_C_FLAGS_LIST + ${REPERTORY_C_FLAGS_LIST} + -g + -gdwarf-4 + -DDEBUG + -O0 + -fno-omit-frame-pointer + -D_DEBUG + -Wall + -Wextra + -Wpedantic + ) + + set(REPERTORY_CXX_FLAGS_LIST + ${REPERTORY_CXX_FLAGS_LIST} + -g + -gdwarf-4 + -DDEBUG + -O0 + -fno-omit-frame-pointer + -D_DEBUG + -Wall + -Wextra + -Wpedantic + ) + else () + set(REPERTORY_C_FLAGS_LIST + ${REPERTORY_C_FLAGS_LIST} + -O3 + -DNDEBUG + ) + + set(REPERTORY_CXX_FLAGS_LIST + ${REPERTORY_CXX_FLAGS_LIST} + -O3 + -DNDEBUG + ) + + if (NOT IS_CLANG_COMPILER) + set(REPERTORY_C_FLAGS_LIST + ${REPERTORY_C_FLAGS_LIST} + -s + ) + + set(REPERTORY_CXX_FLAGS_LIST + ${REPERTORY_CXX_FLAGS_LIST} + -s + ) + endif () + endif() +endif() + +if (LINUX) + execute_process(COMMAND /bin/bash ${CMAKE_CURRENT_SOURCE_DIR}/detect_linux_build.sh OUTPUT_VARIABLE LINUX_DISTRO) + string(REGEX REPLACE "\n$" "" LINUX_DISTRO "${LINUX_DISTRO}") + if (LINUX_DISTRO STREQUAL "debian9") + set(IS_DEBIAN9_DISTRO TRUE) + add_definitions("-DIS_DEBIAN9_DISTRO") + endif() + + set(ENV{PKG_CONFIG_PATH} "${EXTERNAL_BUILD_ROOT}/lib/pkgconfig:${EXTERNAL_BUILD_ROOT}/${REPERTORY_LIB_DIR}/pkgconfig:$ENV{PKG_CONFIG_PATH}") + find_package(PkgConfig REQUIRED) + pkg_check_modules(LIBFUSE REQUIRED fuse>=2.9.0) + + set(Boost_USE_STATIC_LIBS ON) + + include_directories(/usr/local/include) + + if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.5) + message(FATAL_ERROR "Require at least gcc-5.5") + endif() + + link_directories( + ${EXTERNAL_BUILD_ROOT}/${REPERTORY_LIB_DIR} + /usr/local/lib + /usr/local/${REPERTORY_LIB_DIR} + ) + set(REPERTORY_LINK_LIBRARIES + dl + pthread + ) +elseif (FREEBSD) + message(FATAL_ERROR "FreeBSD is no longer supported") +elseif (MACOS) + add_definitions("-DBOOST_ASIO_HAS_STD_STRING_VIEW") + include_directories(/usr/local/include) + find_library(OSXFUSE NAMES OSXFUSE) + if (OSXFUSE-NOTFOUND) + message(FATAL_ERROR "FUSE for macOS not found (https://osxfuse.github.io/)") + endif () + set(LIBFUSE_LIBRARIES fuse) + set(REPERTORY_LINK_LIBRARIES + z + pthread + dl + "-L/usr/local/lib" + "-framework Foundation" + "-framework AppKit" + "-framework SystemConfiguration" + ) +elseif (MSVC) + set(STATIC_LIB_EXT lib) + + set(REPERTORY_C_FLAGS_LIST + ${REPERTORY_C_FLAGS_LIST} + /D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING + /DNOMINMAX + /bigobj + /Zi + ) + + set(REPERTORY_CXX_FLAGS_LIST + ${REPERTORY_CXX_FLAGS_LIST} + /D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING + /DNOMINMAX + /bigobj + /Zi + ) + + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(REPERTORY_C_FLAGS_LIST + ${REPERTORY_C_FLAGS_LIST} + /DEBUG + ) + + set(REPERTORY_CXX_FLAGS_LIST + ${REPERTORY_CXX_FLAGS_LIST} + /DEBUG + ) + + set(REPERTORY_SHARED_LINKER_FLAGS_LIST + ${REPERTORY_SHARED_LINKER_FLAGS_LIST} + /DEBUG + /OPT:REF + /OPT:ICF + ) + endif() + + set(REPERTORY_VER_FILEVERSION ${REPERTORY_MAJOR},${REPERTORY_MINOR},${REPERTORY_REV},${REPERTORY_RELEASE_NUM}) + set(REPERTORY_VER_FILEVERSION_STR ${REPERTORY_MAJOR}.${REPERTORY_MINOR}.${REPERTORY_REV}.${REPERTORY_RELEASE_NUM}) + set(REPERTORY_VER_PRODUCTVERSION ${REPERTORY_MAJOR},${REPERTORY_MINOR},${REPERTORY_REV},${REPERTORY_RELEASE_NUM}) + set(REPERTORY_VER_PRODUCTVERSION_STR ${REPERTORY_MAJOR}.${REPERTORY_MINOR}.${REPERTORY_REV}.${REPERTORY_RELEASE_NUM}) + set(REPERTORY_VER_PRERELEASE VS_FF_PRERELEASE) + set(REPERTORY_VER_COMPANYNAME_STR "https://git.fifthgrid.com/blockstorage") + set(REPERTORY_VER_LEGALCOPYRIGHT_STR "Copyright 2018 ") + set(REPERTORY_VER_FILEDESCRIPTION_STR "Mount utility for Sia, Skynet and S3") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in ${CMAKE_CURRENT_SOURCE_DIR}/src/version.rc @ONLY) + set(WINDOWS_VERSION_RC ${CMAKE_CURRENT_SOURCE_DIR}/src/version.rc) + + set(OPENSSL_USE_STATIC_LIBS TRUE) + if (NOT OPENSSL_ROOT_DIR) + set (OPENSSL_ROOT_DIR "C:\\Program Files\\OpenSSL-Win64") + endif() + find_package(OpenSSL 1.1.1 REQUIRED) + set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) + + include_directories(SYSTEM ${CMAKE_SOURCE_DIR}/3rd_party/winfsp-${WINFSP_VERSION}/inc) + add_definitions(-DTTMATH_NOASM -D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING) + link_directories(${CMAKE_SOURCE_DIR}/3rd_party/winfsp-${WINFSP_VERSION}/lib) + set(REPERTORY_LINK_LIBRARIES + Ncrypt.lib + Rpcrt4.lib + Secur32.lib + Shlwapi.lib + Userenv.lib + Version.lib + Winhttp.lib + Wininet.lib + Ws2_32.lib + advapi32.lib + bcrypt.lib + comdlg32.lib + crypt32.lib + gdi32.lib + httpapi.lib + kernel32.lib + ole32.lib + oleaut32.lib + shell32.lib + user32.lib + uuid.lib + winspool.lib + ${WINFSP_LIBRARY_BASENAME}.lib + ) + + if (CMAKE_GENERATOR MATCHES "NMake Makefiles") + set(REPERTORY_DLL_OUTPUT_DIR ${CMAKE_BINARY_DIR}) + else() + set(REPERTORY_DLL_OUTPUT_DIR ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}) + endif() + + function(CopySupportFiles target) + add_custom_command( + TARGET ${target} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_SOURCE_DIR}/3rd_party/winfsp-${WINFSP_VERSION}/bin/${WINFSP_LIBRARY_BASENAME}.dll + ${REPERTORY_DLL_OUTPUT_DIR}/${WINFSP_LIBRARY_BASENAME}.dll) + add_custom_command( + TARGET ${target} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_SOURCE_DIR}/3rd_party/cacert.pem + ${REPERTORY_DLL_OUTPUT_DIR}/cacert.pem) + endfunction() +endif() + +list(JOIN REPERTORY_CXX_FLAGS_LIST " " REPERTORY_CXX_FLAGS_LIST) +list(JOIN REPERTORY_C_FLAGS_LIST " " REPERTORY_C_FLAGS_LIST) +list(JOIN REPERTORY_SHARED_LINKER_FLAGS_LIST " " REPERTORY_SHARED_LINKER_FLAGS_LIST) + +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${REPERTORY_C_FLAGS_LIST}") +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${REPERTORY_CXX_FLAGS_LIST}") +set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${REPERTORY_SHARED_LINKER_FLAGS_LIST}") + +include(cmake/zlib.cmake) +include(cmake/openssl.cmake) +include(cmake/curl.cmake) +include(cmake/boost.cmake) +include(cmake/libuuid.cmake) +include(cmake/rocksdb.cmake) +include(cmake/cryptopp.cmake) +include(cmake/awscpp.cmake) +include(cmake/libmicrohttpd.cmake) +include(cmake/libhttpserver.cmake) + +include_directories(include) +include_directories(SYSTEM + ${Boost_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/json + ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/jsonrpcpp-1.1.1/lib + ${LIBUUID_INCLUDE_DIR} + ${EXTERNAL_BUILD_ROOT}/include + ${EXTERNAL_BUILD_ROOT}/include/cryptopp + ${OPENSSL_INCLUDE_DIR} + ${CURL_INCLUDE_DIRS} + ${LIBFUSE_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/3rd_party/ttmath-0.9.3/ttmath + ${ROCKSDB_INCLUDE_DIRS} +) + +if (NOT IS_DEBIAN9_DISTRO) + check_include_file_cxx(optional HAS_STD_OPTIONAL) + if (HAS_STD_OPTIONAL) + add_definitions(-DHAS_STD_OPTIONAL) + endif() +endif() + +if (UNIX) + macro(install_symlink filepath sympath) + install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${filepath} ${sympath})") + install(CODE "message(\"-- Created symlink: ${sympath} -> ${filepath}\")") + endmacro(install_symlink) + check_include_files(sys/xattr.h HAS_SETXATTR) + if (HAS_SETXATTR) + add_definitions(-DHAS_SETXATTR) + endif() +endif() + +set(REPERTORY_LINK_LIBRARIES + ${ROCKSDB_LIBRARIES} + ${LIBFUSE_LIBRARIES} + ${LIBUUID_LIBRARIES} + ${AWSCPP_LIBRARIES} + ${LIBHTTPSERVER_LIBRARIES} + ${LIBMICROHTTPD_LIBRARIES} + ${Boost_LIBRARIES} + ${CURL_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${ZLIB_LIBRARIES} + ${REPERTORY_LINK_LIBRARIES} +) + +file(GLOB_RECURSE REPERTORY_HEADERS + ${REPERTORY_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hh + ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/**/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/**/*.hh + ${CMAKE_CURRENT_SOURCE_DIR}/include/**/*.hpp +) + +file(GLOB_RECURSE REPERTORY_SOURCES + ${REPERTORY_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/**/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/jsonrpcpp-1.1.1/lib/jsonrp.cpp +) +list(REMOVE_ITEM REPERTORY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp) + +add_library(librepertory STATIC ${REPERTORY_SOURCES} ${REPERTORY_HEADERS}) +set_target_properties(librepertory PROPERTIES PREFIX "") +target_link_libraries(librepertory LINK_INTERFACE_LIBRARIES ${REPERTORY_LINK_LIBRARIES}) + +add_dependencies(librepertory + rocksdb_project + cryptopp_project + libhttpserver_project +) +if (REPERTORY_ENABLE_S3) + add_dependencies(librepertory awscpp_project) +endif () + +if (LINUX) + add_dependencies(librepertory + libuuid_project + boost_project + curl_project + openssl_project + ) +elseif (MACOS) + add_dependencies(librepertory + openssl_project + boost_project + curl_project + ) +elseif (MSVC) + add_dependencies(librepertory + curl_project + zlib_project + boost_project + ) +endif() + +add_executable(repertory + src/main.cpp + ${WINDOWS_VERSION_RC} + ${REPERTORY_HEADERS} +) + +add_custom_command( + TARGET repertory + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/cacert.pem + ${CMAKE_BINARY_DIR}/cacert.pem +) + +add_dependencies(repertory librepertory) + +target_link_libraries(repertory PRIVATE librepertory) + +if (MACOS) + set_target_properties(repertory PROPERTIES COMPILE_FLAGS -fvisibility=hidden) +elseif (MSVC) + CopySupportFiles(repertory) +endif() + +include(cmake/testing.cmake) + +if (UNIX) + install(TARGETS repertory RUNTIME DESTINATION bin) + if (LINUX) + install_symlink(${CMAKE_INSTALL_PREFIX}/bin/repertory /sbin/mount.repertory) + endif() +endif() + +message(STATUS "Repertory Version: ${REPERTORY_VERSION}") +message(STATUS "Git Revision: ${REPERTORY_GIT_REV}") diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 00000000..f6ac548d --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,8 @@ +# Repertory +* Lars Floe +* Oleg Nypadymka +* Scott E. Graves + +# Deprecated SiaDrive +* Christoph Schaefer +* Michael Lynch diff --git a/CPPLINT.cfg b/CPPLINT.cfg new file mode 100644 index 00000000..31ba6dad --- /dev/null +++ b/CPPLINT.cfg @@ -0,0 +1,2 @@ +filter=-build/include_what_you_use,-readability/alt_tokens,-whitespace/comments,+build/c++14,-build/c++11,-build/namespaces,-runtime/references,-whitespace/indent +linelength=100 diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..dd84d160 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,923 @@ +# Repertory MIT License +### Copyright <2018-2022> + +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. + +# WinFSP GPLv3 License +### The WinFsp project is Copyright (C) Bill Zissimopoulos. It is licensed under the terms of the GPLv3. + +As a special exception to GPLv3, Bill Zissimopoulos grants additional +permissions to Free/Libre and Open Source Software ("FLOSS") without requiring +that such software is covered by the GPLv3. + + 1. Permission to link with a platform specific version of the WinFsp DLL + (one of: winfsp-x64.dll, winfsp-x86.dll, winfsp-msil.dll). + + 2. Permission to distribute unmodified binary releases of the WinFsp + installer (as released by the WinFsp project). + + These permissions (and no other) are granted provided that the software: + + 1. Is distributed under a license that satisfies the Free Software + Definition Version 1.141 (https://www.gnu.org/philosophy/free-sw.en.html) + or the Open Source Definition Version 1.9 (https://opensource.org/osd). + + 2. Includes the copyright notice "WinFsp - Windows File System Proxy, + Copyright (C) Bill Zissimopoulos" and a link to the WinFsp repository in + its user-interface and any user-facing documentation. + + 3. Is not linked or distributed with proprietary (non-FLOSS) software. + [You cannot mix FLOSS and proprietary software while using WinFsp under + this special exception.] + +Commercial licensing options are also available: Please contact +Bill Zissimopoulos . + +The full text of the GPLv3 license follows below. + +----------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +# RocksDB License +### RocksDB: A Persistent Key-Value Store for Flash and RAM Storage + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + 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. diff --git a/README.md b/README.md new file mode 100644 index 00000000..c03615c8 --- /dev/null +++ b/README.md @@ -0,0 +1,498 @@ +# Repertory +Repertory allows you to mount AWS S3, Sia, and Skynet \[EXPERIMENTAL\] via FUSE on Linux/OS X or via WinFSP on Windows. + +# IMPORTANT +Skynet support is considered EXPERIMENTAL. Files added to Skynet should not be considered permanent. For a better understanding of how Skynet currently functions, please visit [siasky.net](https://siasky.net/). + +# Details and Features +* Optimized for [Plex Media Server](https://www.plex.tv/) +* Single application to mount Sia, and/or Skynet +* Only 1 Sia, and 1 Skynet mount per user is supported. +* Remote mounting of Repertory instances (Sia, and Skynet) on Linux, OS X and Windows + * Securely share your mounts over TCP/IP (`XChaCha20-Poly1305` stream cipher) +* Cross-platform support (Linux 64-bit, Linux arm64/aarch64, OS X, Windows 64-bit) + +# Minimum Requirements +* Sia 1.4.1+ +* NOTE: Windows standalone version requires the following dependencies to be installed: + * [WinFSP 2020.2](https://github.com/billziss-gh/winfsp/releases/download/v1.8/winfsp-1.8.20304.msi) + * [Microsoft Visual C++ Redistributable for Visual Studio 2015, 2017 and 2019](https://aka.ms/vs/16/release/vc_redist.x64.exe) +* NOTE: OS X standalone version requires the following dependency to be installed: + * [FUSE for macOS v4.1.2](https://github.com/osxfuse/osxfuse/releases/download/macfuse-4.1.2/macfuse-4.1.2.dmg) + +# Current Version +* Linux `arm64/aarch64` distribution support: + * Debian 9 + * Debian 10 +* Linux 64-bit distribution support: + * Antergos (uses `CentOS 7`) + * Arch Linux (uses `CentOS 7`) + * Bodhi 5.0.0 (uses `CentOS 7`) + * CentOS 7 + * CentOS 8 (uses `CentOS 7`) + * Debian 9 (uses `CentOS 7`) + * Debian 10 (uses `CentOS 7`) + * Elementary OS 5.0, 5.1 (uses `CentOS 7`) + * Fedora 28 (uses `CentOS 7`) + * Fedora 29, 30, 31, 32, 33, 34 (uses `CentOS 7`) + * Linux Mint 19, 19.1, 19.2, 19.3, 20, 20.1 (uses `CentOS 7`) + * Manjaro (uses `CentOS 7`) + * OpenSUSE Leap 15.0, 15.1, 15.2 (uses `CentOS 7`) + * OpenSUSE Tumbleweed (uses `CentOS 7`) + * Solus + * Ubuntu 18.04 (uses `CentOS 7`) + * Ubuntu 18.10, 19.04, 19.10, 20.04, 20.10, 21.04 (uses `CentOS 7`) + +# Upcoming Features +* S3-compatible mount support + +# Planned Features +* Random-write/partial upload support +* NTFS ADS support +* NTFS permissions +* Windows mount-at-boot support + +# Skynet Limitations +* Aside from directory paths included in `skylink`'s, directories are local-only. A local RocksDB is used to manage directories. +* Modifications to files result in a full file re-upload and a new `skylink` will be created. There is no way to modify a file once it has been uploaded to Skynet. +* Renaming files and directories is supported under the following scenarios: + * File has never been uploaded to Skynet + * Parent directory path is renamed, excluding any directory paths included in the `skylink`. Some `skylink`'s include multiple files in a nested directory structure. + * PLANNED ~~File has been previously uploaded and `EnableUploadOnRename` is set to `true` (by default this is `false`).~~ + * ~~NOTE: This may cause noticeable performance issues for files that do not reside in local cache. Repertory will need to fully download the file before it can be renamed. It will then be re-uploaded to Skynet, similar to a file modification.~~ + +# Usage + +## Mac OS X + +### Sia Mount +```repertory -o big_writes ~/sia``` + +### Skynet Mount +```repertory -o big_writes,sk ~/skynet``` + +### Remote Mount +```repertory -o big_writes -rm : ~/remote``` + +* Sia remote mounts use port 20000 by default +* Skynet remote mounts use port 20003 by default + +## Windows + +### Sia Mount +```repertory.exe T:``` + +### Skynet Mount +```repertory.exe -sk T:``` + +### Remote Mount +```repertory -rm : R:``` + +* Sia remote mounts use port 20000 by default +* Skynet remote mounts use port 20000 by default + +# Sharing Mounts +All supported OS's have the ability to share local mounts with other systems. To enable sharing, make sure your drive is not mounted and execute the following commands: + +## Sia Sharing +* `repertory -set RemoteMount.EnableRemoteMount true` + * By default, port 20000 is used. To configure a different port, execute the following: + * `repertory -set RemoteMount.RemotePort ` +* `repertory.exe -set RemoteMount.RemoteToken ` + * `RemoteToken` must be set to the same value on client systems. + +## Skynet Sharing +* `repertory -sk -set RemoteMount.EnableRemoteMount true` + * By default, port 20003 is used. To configure a different port, execute the following: + * `repertory -sk -set RemoteMount.RemotePort ` +* `repertory.exe -sk -set RemoteMount.RemoteToken ` + * `RemoteToken` must be set to the same value on client systems. + +# Mounting a Shared Remote Instance + +## Configuring Connection +Before mounting the first time, you must set the client's `RemoteToken`. + +* `repertory -rm : -set RemoteMount.RemoteToken ` + * `RemoteToken` must be set to the same value as the host system. + +## Remote Mounting + +### Windows +* ```repertory -rm : R:``` + +# Plex Media Server on Linux +Most Linux distributions will create a service user account `plex`. Repertory will need to be mounted as the `plex` user for media discovery and playback. The following is an example of how to configure and mount Repertory for use in Plex: + +```bash +sudo mkdir /home/plex +sudo chown plex.plex /home/plex +sudo -u plex repertory -o uid=,gid=,big_writes /mnt/location +``` + +Alternatively, you can mount as root and allow default permissions. This will create a FUSE mount that mimics a normal disk. Once mounted, you will need to set filesystem permissions according to your requirements. The following is an example of how to setup a Repertory mount in `/etc/fstab`: + +```bash +sudo ln -s /sbin/mount.repertory +``` +Example `/etc/fstab` entries for Sia: +``` +none /mnt/sia repertory defaults,_netdev,default_permissions,allow_other 0 0 +``` +``` +repertory /mnt/sia fuse defaults,_netdev,default_permissions,allow_other 0 0 +``` +Please note that the Sia daemon must also be launched at boot time for `fstab` mounts to work. + +# Data Locations +You will find all relevant configuration and logging files in the root data directory of repertory. Consult the list below to determine this location for your operating system. + +* Mac OS X + * Sia + * `~/Library/Application Support/repertory2/sia` + * Skynet + * `~/Library/Application Support/repertory2/skynet` + * Remote Mounts + * `~/Library/Application Support/repertory2/remote` +* Windows + * Sia + * `%LOCALAPPDATA%\repertory\sia` + * Skynet + * `%LOCALAPPDATA%\repertory\skynet` + * Remote Mounts + * `%LOCALAPPDATA%\repertory\remote` + +# Configuration Details +If you have enabled an API password in Sia, it is recommended to pre-create repertory configuration prior to mounting. + +## Mac OS X + +### Generate Sia Configuration +* ```repertory -gc``` + +### Setting API Password +* For Sia edit: + * ```~/Library/Application Support/repertory2/sia/config.json``` + +## Windows + +### Generate Sia Configuration +* ```repertory.exe -gc``` + +### Setting API Password +* For Sia edit: + * ```%LOCALAPPDATA%\repertory\sia\config.json``` + +## Sample Sia Configuration +```json +{ + "ApiAuth": "", + "ApiPort": 11101, + "ApiUser": "repertory", + "ChunkDownloaderTimeoutSeconds": 30, + "ChunkSize": 4096, + "EnableChunkDownloaderTimeout": true, + "EnableCommDurationEvents": false, + "EnableDriveEvents": false, + "EnableMaxCacheSize": true, + "EventLevel": "Normal", + "EvictionDelayMinutes": 30, + "HighFreqIntervalSeconds": 30, + "HostConfig": { + "AgentString": "Sia-Agent", + "ApiPassword": "", + "ApiPort": 9980, + "HostNameOrIp": "localhost", + "TimeoutMs": 60000 + }, + "LowFreqIntervalSeconds": 900, + "MaxCacheSizeBytes": 21474836480, + "MinimumRedundancy": 2.5, + "OnlineCheckRetrySeconds": 60, + "OrphanedFileRetentionDays": 15, + "PreferredDownloadType": "Fallback", + "ReadAheadCount": 4, + "RemoteMount": { + "EnableRemoteMount": false, + "IsRemoteMount": false, + "RemoteClientPoolSize": 10, + "RemoteHostNameOrIp": "", + "RemoteMaxConnections": 20, + "RemotePort": 20000, + "RemoteReceiveTimeoutSeconds": 120, + "RemoteSendTimeoutSeconds": 30, + "RemoteToken": "" + }, + "RingBufferFileSize": 512, + "StorageByteMonth": "0", + "Version": 5 +} +``` + +## Sample Skynet Configuration +```json +{ + "ApiAuth": "", + "ApiPort": 11104, + "ApiUser": "repertory", + "ChunkDownloaderTimeoutSeconds": 30, + "ChunkSize": 4096, + "EnableChunkDownloaderTimeout": true, + "EnableCommDurationEvents": false, + "EnableDriveEvents": false, + "EnableMaxCacheSize": true, + "EventLevel": "Normal", + "EvictionDelayMinutes": 30, + "HighFreqIntervalSeconds": 30, + "LowFreqIntervalSeconds": 3600, + "MaxCacheSizeBytes": 21474836480, + "OnlineCheckRetrySeconds": 60, + "OrphanedFileRetentionDays": 15, + "PreferredDownloadType": "Fallback", + "ReadAheadCount": 4, + "RemoteMount": { + "EnableRemoteMount": false, + "IsRemoteMount": false, + "RemoteClientPoolSize": 10, + "RemoteHostNameOrIp": "", + "RemoteMaxConnections": 20, + "RemotePort": 20003, + "RemoteReceiveTimeoutSeconds": 120, + "RemoteSendTimeoutSeconds": 30, + "RemoteToken": "" + }, + "RingBufferFileSize": 512, + "SkynetConfig": { + "EncryptionToken": "", + "PortalList": [ + "https://siasky.net", + "https://sialoop.net", + "https://skydrain.net", + "https://skynet.luxor.tech", + "https://skynet.tutemwesi.com", + "https://siacdn.com", + "https://vault.lightspeedhosting.com" + ] + }, + "StorageByteMonth": "0", + "Version": 5 +} +``` + +## Configuration Settings +Configuration settings that are not documented below should be ignored for now as they are used internally. + +`StorageByteMonth` should never be altered. On start-up, this value is used to calculate estimated storage space. + +You can change or retrieve these values using the `repertory` executable: + + +`repertory -get EnableDriveEvents` + +`repertory -set EnableDriveEvents false` + +### ApiAuth +Password used to connect to Repertory API. Auto-generated by default. + +### ApiPort +Repertory API port to use for JSON-RPC requests. + +* Sia default: `11101` +* Skynet default: `11104` + +### ApiUser +Username used to connect to Repertory API. Default is 'repertory'. + +### ChunkDownloaderTimeoutSeconds +Files that are not cached locally will download data in `ChunkSize` chunks when a read or write operation occurs. This timeout value specifies the amount of time chunks should continue downloading after the last file handle has been closed. + +### ChunkSize +This is the minimum data size (converted to KiB - value of 8 means 8KiB) used for downloads. This value cannot be less than 8 and should also be a multiple of 8. Default is 2048. + +### EnableDriveEvents +When set to `true`, additional logging for FUSE on UNIX or WinFSP on Windows will occur. It's best to leave this value set to 'false' unless troubleshooting an issue as enabling it may have an adverse affect on performance. + +### EnableMaxCacheSize +If set to `true`, files will begin to be removed from the local cache as soon as `MaxCacheSizeBytes` and `MinimumRedundancy` have been met. This does not mean further attempts to write will fail when `MaxCacheSizeBytes` is reached. Writes will continue as long as there is enough local drive space to accommodate the operation. + +If set to `false`, files will begin to be removed from the local cache as soon as `MinimumRedundancy` has been met. + +In both cases, files that do not have any open handles will be chosen by oldest modification date for removal. + +### EventLevel +Internally, events are fired during certain operations. This setting determines which events should be logged to `repertory.log`. Valid values are `Error`, `Warn`, `Normal`, `Debug`, and `Verbose`. + +### EvictionDelayMinutes +Number of minutes to wait after all file handles are closed before allowing file to be evicted from cache. + +### HostConfig.AgentString +'User-Agent' used when communicating with Sia's API. + +### HostConfig.ApiPassword +Password used when communicating with Sia's API. + +### HostConfig.ApiPort +API port used to connect to Sia daemon. + +This is not the same as your wallet's password, so please do not enter it here. Sia typically auto-generate this value. It is exclusively used for API authentication purposes. + +### HostConfig.HostNameOrIp +IP address or host name of Sia daemon. + +### HostConfig.TimeoutMs +Number of milliseconds to wait for Sia API responses before timing out. + +### MaxCacheSizeBytes +This value specifies the maximum amount of local space to consume before files are removed from cache. `EnableMaxCacheSize` must also be set to `true` for this value to take affect. + +### MinimumRedundancy +Files are elected for removal once this value has been reached. Be aware that this value cannot be set to less than 1.5x. + +### OnlineCheckRetrySeconds +Number of seconds to wait for Sia daemon to become available/connectable. + +### OrphanedFileRetentionDays +Repertory attempts to keep modifications between Sia-UI and the mounted location in sync as much as possible. In the event a file is removed from Sia-UI, it will be marked as orphaned in Repertory and moved into an 'orphaned' directory within Repertory's data directory. This setting specifies the number of days this file is retained before final deletion. + +### PreferredDownloadType +Repertory supports 3 download modes for reading files that are not cached locally: full file allocation, ring buffer mode and direct mode. + +Full file allocation mode pre-allocates the entire file prior to downloading. This mode is required for writes but also ensures the best performance when reading data. + +Ring buffer mode utilizes a fixed size file buffer to enable a reasonable amount of seeking. This alleviates the need to fully allocate a file. By default, it is 512MiB. When the buffer is full, it attempts to maintain the ability to seek 50% ahead or behind the current read location without the need to re-download data from the provider. + +Direct mode utilizes no disk space. All data is read directly from the provider. + +Preferred download type modes are: + +* _Fallback_ - If there isn't enough local space to allocate the full file, ring buffer mode is used. If there isn't enough local space to create the ring buffer's file, then direct mode is used. +* _RingBuffer_ - Full file allocation is always bypassed; however, if there isn't enough space to create the ring buffer's file, then direct mode will be chosen. +* _Direct_ - All files will be read directly from the provider. + +### ReadAheadCount +This value specifies the number of read-ahead chunks to use when downloading a file. This is a per-open file setting and will result in the creation of an equal number of threads. + +### RemoteMount.EnableRemoteMount +Allow mounting this location over TCP. + +### RemoteMount.IsRemoteMount +Used internally. Should not be modified manually and cannot be `true` if `EnableRemoteMount` is also `true`. + +This value is used on client systems to indicate the mount is not local. + +### RemoteMount.RemoteClientPoolSize +Number of threads to use for each unique client. Only available when `EnableRemoteMount` is `true`. + +### RemoteMount.RemoteHostNameOrIp +Host name or IP of host to connect to for remote mounting. + +Only available when `IsRemoteMount` is `true`. + +### RemoteMount.RemoteMaxConnections +Maximum number of TCP connections to use when communicating with remote instances. + +### RemoteMount.RemotePort +TCP port used for remote mounting. + +### RemoteMount.RemoteToken +Encryption token used for remote mounts. This value must be the same on local and remote systems. + +### RingBufferFileSize +The size of the ring buffer file in MiB. Default is 512. Valid values are: 64, 128, 256, 512, 1024. + +### Version +Configuration file version. This is used internally and should not be modified. + +# Compiling + +## Linux 64-bit +* Requires [CMake](https://cmake.org) 3.1 or above +* Requires ```fuse``` 2.9.x binary/kernel module +* Requires ```libfuse``` 2.9.x binary and development package +* Requires ```openssl``` binary and development package +* Requires ```zlib``` binary and development package +* Execute the following from the ```repertory``` source directory: + * ```mkdir build && cd build``` + * Debug build: + * ```cmake -DCMAKE_BUILD_TYPE=Debug ..``` + * Release build: + * ```cmake -DCMAKE_BUILD_TYPE=Release ..``` + * ```make``` + * Optionally: + * ```sudo make install``` + +## Mac OS X +* Requires XCode from Apple Store +* Requires [CMake](https://cmake.org) 3.1 or above +* Requires [FUSE for macOS v4.1.2](https://github.com/osxfuse/osxfuse/releases/download/macfuse-4.1.2/macfuse-4.1.2.dmg) +* Execute the following from the ```repertory``` source directory: + * ```mkdir build && cd build``` + * Debug build: + * ```cmake -DCMAKE_BUILD_TYPE=Debug ..``` + * Release build: + * ```cmake -DCMAKE_BUILD_TYPE=Release ..``` + * ```make``` + +## Windows 7 or above 64-bit +* Install [cmake-3.15.2](https://github.com/Kitware/CMake/releases/download/v3.15.2/cmake-3.15.2-win64-x64.msi) +* Install [Win32 OpenSSL](https://slproweb.com/products/Win32OpenSSL.html) 64-bit version +* Install [Visual Studio 2019](https://visualstudio.microsoft.com/downloads/) + * During installation, make sure to install: + * C++ development support + * Windows 10 SDK +* Install [WinFSP 2020.2](https://github.com/billziss-gh/winfsp/releases/download/v1.8/winfsp-1.8.20304.msi) +* Execute Windows build script + * Debug build (64-bit) + * `scripts\64_bit\build_win64_debug.cmd` + * Release build (64-bit) + * `scripts\64_bit\build_win64_release.cmd` + * NOTE: `cmake.exe` must be in Windows search path. If not, modify the following line in `build_win64.cmd`: + * `set CMAKE=""` + +# Credits +* [AWS SDK for C++](https://aws.amazon.com/sdk-for-cpp/) +* [boost c++ libraries](https://www.boost.org/) +* [curl](https://curl.haxx.se/) +* [FUSE for macOS](https://osxfuse.github.io/) +* [Google Test](https://github.com/google/googletest) +* [JSON for Modern C++](https://github.com/nlohmann/json) +* [jsonrpcpp - C++ JSON-RPC 2.0 library](https://github.com/badaix/jsonrpcpp) +* [libfuse](https://github.com/libfuse/libfuse) +* [libhttpserver](https://github.com/etr/libhttpserver) +* [libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) +* [OpenSSL](https://www.openssl.org/) +* [OSSP uuid](http://www.ossp.org/pkg/lib/uuid/) +* [RocksDB](https://rocksdb.org/) +* [Sia Decentralized Cloud Storage](https://sia.tech/) +* [ttmath - Bignum C++ library](https://www.ttmath.org/) +* [WinFSP - FUSE for Windows](https://github.com/billziss-gh/winfsp) +* [zlib](https://zlib.net/) + +# Developer Public Key +``` +-----BEGIN PUBLIC KEY----- +MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEKfZmq5mMAtD4kSt2Gc/5J +H+HHTYtUZE6YYvsvz8TNG/bNL67ZtNRyaoMyhLTfIN4rPBNLUfD+owNS+u5Yk+lS +ZLYyOuhoCZIFefayYqKLr42G8EeuRbx0IMzXmJtN0a4rqxlWhkYufJubpdQ+V4DF +oeupcPdIATaadCKVeZC7A0G0uaSwoiAVMG5dZqjQW7F2LoQm3PhNkPvAybIJ6vBy +LqdBegS1JrDn43x/pvQHzLO+l+FIG23D1F7iF+yZm3DkzBdcmi/mOMYs/rXZpBym +2/kTuSGh5buuJCeyOwR8N3WdvXw6+KHMU/wWU8qTCTT87mYbzH4YR8HgkjkLHxAO +5waHK6vMu0TxugCdJmVV6BSbiarJsh66VRosn7+6hlq6AdgksxqCeNELZBS+LBki +tb5hKyL+jNZnaHiR0U7USWtmnqZG6FVVRzlCnxP7tZo5O5Ex9AAFGz5JzOzsFNbv +xwQ0zqaTQOze+MJbkda7JfRoC6TncD0+3hoXsiaF4mCn8PqUCn0DwhglcRucZlST +ZvDNDo1WAtxPJebb3aS6uymNhBIquQbVAWxVO4eTrOYEgutxwkHE3yO3is+ogp8d +xot7f/+vzlbsbIDyuZBDe0fFkbTIMTU48QuUUVZpRKmKZTHQloz4EHqminbfX1sh +M7wvDkpJEtqbc0VnG/BukUzP6e7Skvgc7eF1sI3+8jH8du2rivZeZAl7Q2f+L9JA +BY9pjaxttxsud7V5jeFi4tKuDHi21/XhSjlJK2c2C4AiUEK5/WhtGbQ5JjmcOjRq +yXFRqLlerzOcop2kbtU3Ar230wOx3Dj23Wg8++lV3LU4U9vMR/t0qnSbCSGJys7m +ax2JpFlTwj/0wYuTlVFoNQHZJ1cdfyRiRBY4Ou7XO0W5hcBBKiYsC+neEeMMHdCe +iTDIW/ojcVTdFovl+sq3n1u4SBknE90JC/3H+TPE1s2iB+fwORVg0KPosQSNDS0A +7iK6AZCDC3YooFo+OzHkYMt9uLkXiXMSLx70az+qlIwOzVHKxCo7W/QpeKCXUCRZ +MMdlYEUs1PC8x2qIRUEVHuJ0XMTKNyOHmzVLuLK93wUWbToh+rdDxnbhX+emuESn +XH6aKiUwX4olEVKSylRUQw8nVckZGVWXzLDlgpzDrLHC8J8qHzFt7eCqOdiqsxhZ +x1U5LtugxwSWncTZ7vlKl0DuC/AWB7SuDi7bGRMSVp2n+MnD1VLKlsCclHXjIciE +W29n3G3lJ/sOta2sxqLd0j1XBQddrFXl5b609sIY81ocHqu8P2hRu5CpqJ/sGZC5 +mMH3segHBkRj0xJcfOxceRLj1a+ULIIR3xL/3f8s5Id25TDo/nqBoCvu5PeCpo6L +9wIDAQAB +-----END PUBLIC KEY----- +``` diff --git a/arm64/debian10 b/arm64/debian10 new file mode 100755 index 00000000..f373eea1 --- /dev/null +++ b/arm64/debian10 @@ -0,0 +1,39 @@ +#!/bin/bash + +mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc +echo -1 > /proc/sys/fs/binfmt_misc/qemu-aarch64 || true +echo ':qemu-aarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-aarch64-static:OCF' > /proc/sys/fs/binfmt_misc/register + +CHROOT_DIR=./arm64_debian10 +DEBIAN_REL=buster + +if [ ! -d "${CHROOT_DIR}" ]; then + qemu-debootstrap --arch arm64 ${DEBIAN_REL} "${CHROOT_DIR}" + cat << EOF > "${CHROOT_DIR}/update.sh" +#!/bin/bash +apt-get update && apt-get -y install \ + apt-utils \ + build-essential \ + curl \ + autoconf \ + automake \ + libtool \ + pkg-config \ + cmake \ + jq \ + make \ + gcc \ + g++ \ + libfuse-dev \ + libstdc++-8-dev \ + diffutils \ + git \ + tar \ + wget \ + zlib1g-dev \ + zip +EOF + + chmod +x "${CHROOT_DIR}/update.sh" + systemd-nspawn -D "${CHROOT_DIR}" "./update.sh" +fi diff --git a/arm64/debian11 b/arm64/debian11 new file mode 100755 index 00000000..c90dda96 --- /dev/null +++ b/arm64/debian11 @@ -0,0 +1,39 @@ +#!/bin/bash + +mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc +echo -1 > /proc/sys/fs/binfmt_misc/qemu-aarch64 || true +echo ':qemu-aarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-aarch64-static:OCF' > /proc/sys/fs/binfmt_misc/register + +CHROOT_DIR=./arm64_debian11 +DEBIAN_REL=bullseye + +if [ ! -d "${CHROOT_DIR}" ]; then + qemu-debootstrap --arch arm64 ${DEBIAN_REL} "${CHROOT_DIR}" + cat << EOF > "${CHROOT_DIR}/update.sh" +#!/bin/bash +apt-get update && apt-get -y install \ + apt-utils \ + build-essential \ + curl \ + autoconf \ + automake \ + libtool \ + pkg-config \ + cmake \ + jq \ + make \ + gcc \ + g++ \ + libfuse-dev \ + libstdc++-10-dev \ + diffutils \ + git \ + tar \ + wget \ + zlib1g-dev \ + zip +EOF + + chmod +x "${CHROOT_DIR}/update.sh" + systemd-nspawn -D "${CHROOT_DIR}" "./update.sh" +fi diff --git a/arm64/debian9 b/arm64/debian9 new file mode 100755 index 00000000..c59da5db --- /dev/null +++ b/arm64/debian9 @@ -0,0 +1,39 @@ +#!/bin/bash + +mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc +echo -1 > /proc/sys/fs/binfmt_misc/qemu-aarch64 || true +echo ':qemu-aarch64:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-aarch64-static:OCF' > /proc/sys/fs/binfmt_misc/register + +CHROOT_DIR=./arm64_debian9 +DEBIAN_REL=stretch + +if [ ! -d "${CHROOT_DIR}" ]; then + qemu-debootstrap --arch arm64 ${DEBIAN_REL} "${CHROOT_DIR}" + cat << EOF > "${CHROOT_DIR}/update.sh" +#!/bin/bash +apt-get update && apt-get -y install \ + apt-utils \ + build-essential \ + curl \ + autoconf \ + automake \ + libtool \ + pkg-config \ + cmake \ + jq \ + make \ + gcc \ + g++ \ + libfuse-dev \ + libstdc++-6-dev \ + diffutils \ + git \ + tar \ + wget \ + zlib1g-dev \ + zip +EOF + + chmod +x "${CHROOT_DIR}/update.sh" + systemd-nspawn -D "${CHROOT_DIR}" "./update.sh" +fi diff --git a/bin/7za.dll b/bin/7za.dll new file mode 100644 index 00000000..bc2b47a3 Binary files /dev/null and b/bin/7za.dll differ diff --git a/bin/7za.exe b/bin/7za.exe new file mode 100644 index 00000000..9f27b20e Binary files /dev/null and b/bin/7za.exe differ diff --git a/bin/7zxa.dll b/bin/7zxa.dll new file mode 100644 index 00000000..d51e3f0d Binary files /dev/null and b/bin/7zxa.dll differ diff --git a/bin/b64.exe b/bin/b64.exe new file mode 100644 index 00000000..712798d4 Binary files /dev/null and b/bin/b64.exe differ diff --git a/bin/curl/BUILD-HOMEPAGE.url b/bin/curl/BUILD-HOMEPAGE.url new file mode 100644 index 00000000..36278f89 --- /dev/null +++ b/bin/curl/BUILD-HOMEPAGE.url @@ -0,0 +1,2 @@ +[InternetShortcut] +URL=https://github.com/curl/curl-for-win diff --git a/bin/curl/BUILD-README.txt b/bin/curl/BUILD-README.txt new file mode 100644 index 00000000..de1f20ad --- /dev/null +++ b/bin/curl/BUILD-README.txt @@ -0,0 +1,3 @@ +Visit the project page for details about these builds and the list of changes: + + https://github.com/curl/curl-for-win diff --git a/bin/curl/CHANGES.txt b/bin/curl/CHANGES.txt new file mode 100644 index 00000000..0715ca0d --- /dev/null +++ b/bin/curl/CHANGES.txt @@ -0,0 +1,7904 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + + Changelog + +Version 7.65.0 (22 May 2019) + +Daniel Stenberg (22 May 2019) +- RELEASE-NOTES: 7.65.0 release + +- THANKS: from the 7.65.0 release-notes + +- url: convert the zone id from a IPv6 URL to correct scope id + + Reported-by: GitYuanQu on github + Fixes #3902 + Closes #3914 + +- configure: detect getsockname and getpeername on windows too + + Made detection macros for these two functions in the same style as other + functions possibly in winsock in the hope this will work better to + detect these functions when cross-compiling for Windows. + + Follow-up to e91e4816123 + + Fixes #3913 + Closes #3915 + +Marcel Raad (21 May 2019) +- examples: remove unused variables + + Fixes Codacy/CppCheck warnings. + + Closes + +Daniel Gustafsson (21 May 2019) +- udpateconninfo: mark variable unused + + When compiling without getpeername() or getsockname(), the sockfd + paramter to Curl_udpateconninfo() became unused after commit e91e481612 + added ifdef guards. + + Closes #3910 + Fixes https://curl.haxx.se/dev/log.cgi?id=20190520172441-32196 + Reviewed-by: Marcel Raad, Daniel Stenberg + +- ftp: move ftp_ccc in under featureflag + + Commit e91e48161235272ff485ff32bd048c53af731f43 moved ftp_ccc in under + the FTP featureflag in the UserDefined struct, but vtls callsites were + still using it unprotected. + + Closes #3912 + Fixes: https://curl.haxx.se/dev/log.cgi?id=20190520044705-29865 + Reviewed-by: Daniel Stenberg, Marcel Raad + +Daniel Stenberg (20 May 2019) +- curl: report error for "--no-" on non-boolean options + + Reported-by: Olen Andoni + Fixes #3906 + Closes #3907 + +- [Guy Poizat brought this change] + + mbedtls: enable use of EC keys + + Closes #3892 + +- lib1560: add tests for parsing URL with too long scheme + + Ref: #3905 + +- [Omar Ramadan brought this change] + + urlapi: increase supported scheme length to 40 bytes + + The longest currently registered URI scheme at IANA is 36 bytes long. + + Closes #3905 + Closes #3900 + +Marcel Raad (20 May 2019) +- lib: reduce variable scopes + + Fixes Codacy/CppCheck warnings. + + Closes https://github.com/curl/curl/pull/3872 + +- tool_formparse: remove redundant assignment + + Just initialize word_begin with the correct value. + + Closes https://github.com/curl/curl/pull/3873 + +- ssh: move variable declaration to where it's used + + This way, we need only one call to free. + + Closes https://github.com/curl/curl/pull/3873 + +- ssh-libssh: remove unused variable + + sock was only used to be assigned to fd_read. + + Closes https://github.com/curl/curl/pull/3873 + +Daniel Stenberg (20 May 2019) +- test332: verify the blksize fix + +- tftp: use the current blksize for recvfrom() + + bug: https://curl.haxx.se/docs/CVE-2019-5436.html + Reported-by: l00p3r on hackerone + CVE-2019-5436 + +Daniel Gustafsson (19 May 2019) +- version: make ssl_version buffer match for multi_ssl + + When running a multi TLS backend build the version string needs more + buffer space. Make the internal ssl_buffer stack buffer match the one + in Curl_multissl_version() to allow for the longer string. For single + TLS backend builds there is no use in extended to buffer. This is a + fallout from #3863 which fixes up the multi_ssl string generation to + avoid a buffer overflow when the buffer is too small. + + Closes #3875 + Reviewed-by: Daniel Stenberg + +Steve Holme (18 May 2019) +- http_ntlm_wb: Handle auth for only a single request + + Currently when the server responds with 401 on NTLM authenticated + connection (re-used) we consider it to have failed. However this is + legitimate and may happen when for example IIS is set configured to + 'authPersistSingleRequest' or when the request goes thru a proxy (with + 'via' header). + + Implemented by imploying an additional state once a connection is + re-used to indicate that if we receive 401 we need to restart + authentication. + + Missed in fe6049f0. + +- http_ntlm_wb: Cleanup handshake after clean NTLM failure + + Missed in 50b87c4e. + +- http_ntlm_wb: Return the correct error on receiving an empty auth message + + Missed in fe20826b as it wasn't implemented in http.c in b4d6db83. + + Closes #3894 + +Daniel Stenberg (18 May 2019) +- curl: make code work with protocol-disabled libcurl + + Closes #3844 + +- libcurl: #ifdef away more code for disabled features/protocols + +- progress: CURL_DISABLE_PROGRESS_METER + +- hostip: CURL_DISABLE_SHUFFLE_DNS + +- netrc: CURL_DISABLE_NETRC + +Viktor Szakats (16 May 2019) +- docs: Markdown and misc improvements [ci skip] + + Approved-by: Daniel Stenberg + Closes #3896 + +- docs/RELEASE-PROCEDURE: link to live iCalendar [ci skip] + + Ref: https://github.com/curl/curl/commit/0af41b40b2c7bd379b2251cbe7cd618e21fa0ea1#commitcomment-33563135 + Approved-by: Daniel Stenberg + Closes #3895 + +Daniel Stenberg (16 May 2019) +- travis: add an osx http-only build + + Closes #3887 + +- cleanup: remove FIXME and TODO comments + + They serve very little purpose and mostly just add noise. Most of them + have been around for a very long time. I read them all before removing + or rephrasing them. + + Ref: #3876 + Closes #3883 + +- curl: don't set FTP options for FTP-disabled builds + + ... since libcurl has started to be totally unaware of options for + disabled protocols they now return error. + + Bug: https://github.com/curl/curl/commit/c9c5304dd4747cbe75d2f24be85920d572fcb5b8#commitcomment-33533937 + + Reported-by: Marcel Raad + Closes #3886 + +Steve Holme (16 May 2019) +- http_ntlm_wb: Move the type-2 message processing into a dedicated function + + This brings the code inline with the other HTTP authentication mechanisms. + + Closes #3890 + +Daniel Stenberg (15 May 2019) +- RELEASE-NOTES: synced + +- docs/RELEASE-PROCEDURE: updated coming releases dates [ci skip] + +- CURLOPT_READFUNCTION.3: see also CURLOPT_UPLOAD_BUFFERSIZE [ci skip] + + Reported-by: Roy Bellingan + Bug: #3885 + +- parse_proxy: use the URL parser API + + As we treat a given proxy as a URL we should use the unified URL parser + to extract the parts out of it. + + Closes #3878 + +Steve Holme (15 May 2019) +- http_negotiate: Move the Negotiate state out of the negotiatedata structure + + Given that this member variable is not used by the SASL based protocols + there is no need to have it here. + + Closes #3882 + +- http_ntlm: Move the NTLM state out of the ntlmdata structure + + Given that this member variable is not used by the SASL based protocols + there is no need to have it here. + +- url: Move the negotiate state type into a dedicated enum + +- url: Remove duplicate clean up of the winbind variables in conn_shutdown() + + Given that Curl_disconnect() calls Curl_http_auth_cleanup_ntlm() prior + to calling conn_shutdown() and it in turn performs this, there is no + need to perform the same action in conn_shutdown(). + + Closes #3881 + +Daniel Stenberg (14 May 2019) +- urlapi: require a non-zero host name length when parsing URL + + Updated test 1560 to verify. + + Closes #3880 + +- configure: error out if OpenSSL wasn't detected when asked for + + If --with-ssl is used and configure still couldn't enable SSL this + creates an error instead of just silently ignoring the fact. + + Suggested-by: Isaiah Norton + Fixes #3824 + Closes #3830 + +Daniel Gustafsson (14 May 2019) +- imap: Fix typo in comment + +Steve Holme (14 May 2019) +- url: Remove unnecessary initialisation from allocate_conn() + + No need to set variables to zero as calloc() does this for us. + + Closes #3879 + +Daniel Stenberg (14 May 2019) +- CURLOPT_CAINFO.3: with Schannel, you want Windows 8 or later [ci skip] + + Clues-provided-by: Jay Satiro + Clues-provided-by: Jeroen Ooms + Fixes #3711 + Closes #3874 + +Daniel Gustafsson (13 May 2019) +- vtls: fix potential ssl_buffer stack overflow + + In Curl_multissl_version() it was possible to overflow the passed in + buffer if the generated version string exceeded the size of the buffer. + Fix by inverting the logic, and also make sure to not exceed the local + buffer during the string generation. + + Closes #3863 + Reported-by: nevv on HackerOne/curl + Reviewed-by: Jay Satiro + Reviewed-by: Daniel Stenberg + +Daniel Stenberg (13 May 2019) +- RELEASE-NOTES: synced + +- appveyor: also build "/ci" branches like travis + +- pingpong: disable more when no pingpong enabled + +- proxy: acknowledge DISABLE_PROXY more + +- parsedate: CURL_DISABLE_PARSEDATE + +- sasl: only enable if there's a protocol enabled using it + +- mime: acknowledge CURL_DISABLE_MIME + +- wildcard: disable from build when FTP isn't present + +- http: CURL_DISABLE_HTTP_AUTH + +- base64: build conditionally if there are users + +- doh: CURL_DISABLE_DOH + +Steve Holme (12 May 2019) +- auth: Rename the various authentication clean up functions + + For consistency and to a avoid confusion. + + Closes #3869 + +Daniel Stenberg (12 May 2019) +- [Jay Satiro brought this change] + + docs/INSTALL: fix broken link [ci skip] + + Reported-by: Joombalaya on github + Fixes #3818 + +Marcel Raad (12 May 2019) +- easy: fix another "clarify calculation precedence" warning + + I missed this one in commit 6b3dde7fe62ea5a557fd1fd323fac2bcd0c2e9be. + +- build: fix "clarify calculation precedence" warnings + + Codacy/CppCheck warns about this. Consistently use parentheses as we + already do in some places to silence the warning. + + Closes https://github.com/curl/curl/pull/3866 + +- cmake: restore C89 compatibility of CurlTests.c + + I broke it in d1b5cf830bfe169745721b21245d2217d2c2453e and + 97de97daefc2ed084c91eff34af2426f2e55e134. + + Reported-by: Viktor Szakats + Ref: https://github.com/curl/curl/commit/97de97daefc2ed084c91eff34af2426f2e55e134#commitcomment-33499044 + Closes https://github.com/curl/curl/pull/3868 + +Steve Holme (11 May 2019) +- http_ntlm: Corrected the name of the include guard + + Missed in f0bdd72c. + + Closes #3867 + +- http_digest: Don't expose functions when HTTP and Crypto Auth are disabled + + Closes #3861 + +- http_negotiate: Don't expose functions when HTTP is disabled + +Daniel Stenberg (11 May 2019) +- SECURITY-PROCESS: fix links [ci skip] + +Marcel Raad (11 May 2019) +- CMake: suppress unused variable warnings + + I missed these in commit d1b5cf830bfe169745721b21245d2217d2c2453e. + +Daniel Stenberg (11 May 2019) +- doh: disable DOH for the cases it doesn't work + + Due to limitations in Curl_resolver_wait_resolv(), it doesn't work for + DOH resolves. This fix disables DOH for those. + + Limitation added to KNOWN_BUGS. + + Fixes #3850 + Closes #3857 + +Jay Satiro (11 May 2019) +- checksrc.bat: Ignore snprintf warnings in docs/examples + + .. because we allow snprintf use in docs/examples. + + Closes https://github.com/curl/curl/pull/3862 + +Steve Holme (10 May 2019) +- vauth: Fix incorrect function description for Curl_auth_user_contains_domain() + + ...and misalignment of these comments. From a78c61a4. + + Closes #3860 + +Jay Satiro (10 May 2019) +- Revert "multi: support verbose conncache closure handle" + + This reverts commit b0972bc. + + - No longer show verbose output for the conncache closure handle. + + The offending commit was added so that the conncache closure handle + would inherit verbose mode from the user's easy handle. (Note there is + no way for the user to set options for the closure handle which is why + that was necessary.) Other debug settings such as the debug function + were not also inherited since we determined that could lead to crashes + if the user's per-handle private data was used on an unexpected handle. + + The reporter here says he has a debug function to capture the verbose + output, and does not expect or want any output to stderr; however + because the conncache closure handle does not inherit the debug function + the verbose output for that handle does go to stderr. + + There are other plausible scenarios as well such as the user redirects + stderr on their handle, which is also not inherited since it could lead + to crashes when used on an unexpected handle. + + Short of allowing the user to set options for the conncache closure + handle I don't think there's much we can safely do except no longer + inherit the verbose setting. + + Bug: https://curl.haxx.se/mail/lib-2019-05/0021.html + Reported-by: Kristoffer Gleditsch + + Ref: https://github.com/curl/curl/pull/3598 + Ref: https://github.com/curl/curl/pull/3618 + + Closes https://github.com/curl/curl/pull/3856 + +Steve Holme (10 May 2019) +- ntlm: Fix misaligned function comments for Curl_auth_ntlm_cleanup() + + From 6012fa5a. + + Closes #3858 + +Daniel Stenberg (9 May 2019) +- BUG-BOUNTY: minor formatting fixes [ci skip] + +- RELEASE-NOTES: synced + +- BUG-BOUNTY.md: add the Dropbox "bonus" extra payout ability [ci skip] + + Closes #3839 + +Kamil Dudka (9 May 2019) +- http_negotiate: do not treat failure of gss_init_sec_context() as fatal + + Fixes #3726 + Closes #3849 + +- spnego_gssapi: fix return code on gss_init_sec_context() failure + + Fixes #3726 + Closes #3849 + +Steve Holme (9 May 2019) +- gen_resp_file.bat: Removed unnecessary @ from all but the first command + + There is need to use @ on every command once echo has been turned off. + + Closes #3854 + +Jay Satiro (8 May 2019) +- http: Ignore HTTP/2 prior knowledge setting for HTTP proxies + + - Do not switch to HTTP/2 for an HTTP proxy that is not tunnelling to + the destination host. + + We already do something similar for HTTPS proxies by not sending h2. [1] + + Prior to this change setting CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE would + incorrectly use HTTP/2 to talk to the proxy, which is not something we + support (yet?). Also it's debatable whether or not that setting should + apply to HTTP/2 proxies. + + [1]: https://github.com/curl/curl/commit/17c5d05 + + Bug: https://github.com/curl/curl/issues/3570 + Bug: https://github.com/curl/curl/issues/3832 + + Closes https://github.com/curl/curl/pull/3853 + +Marcel Raad (8 May 2019) +- travis: update mesalink build to xenial + + Closes https://github.com/curl/curl/pull/3842 + +Daniel Stenberg (8 May 2019) +- [Ricky Leverence brought this change] + + OpenSSL: Report -fips in version if OpenSSL is built with FIPS + + Older versions of OpenSSL report FIPS availabilty via an OPENSSL_FIPS + define. It uses this define to determine whether to publish -fips at + the end of the version displayed. Applications that utilize the version + reported by OpenSSL will see a mismatch if they compare it to what curl + reports, as curl is not modifying the version in the same way. This + change simply adds a check to see if OPENSSL_FIPS is defined, and will + alter the reported version to match what OpenSSL itself provides. This + only appears to be applicable in versions of OpenSSL <1.1.1 + + Closes #3771 + +Kamil Dudka (7 May 2019) +- [Frank Gevaerts brought this change] + + nss: allow fifos and character devices for certificates. + + Currently you can do things like --cert <(cat ./cert.crt) with (at least) the + openssl backend, but that doesn't work for nss because is_file rejects fifos. + + I don't actually know if this is sufficient, nss might do things internally + (like seeking back) that make this not work, so actual testing is needed. + + Closes #3807 + +Daniel Gustafsson (6 May 2019) +- test2100: Fix typos in test description + +Daniel Stenberg (6 May 2019) +- ssh: define USE_SSH if SSH is enabled (any backend) + + Closes #3846 + +Steve Holme (5 May 2019) +- winbuild: Add our standard copyright header to the winbuild batch files + +- makedebug: Fix ERRORLEVEL detection after running where.exe + + Closes #3838 + +Daniel Stenberg (5 May 2019) +- urlapi: add CURLUPART_ZONEID to set and get + + The zoneid can be used with IPv6 numerical addresses. + + Updated test 1560 to verify. + + Closes #3834 + +- [Taiyu Len brought this change] + + WRITEFUNCTION: add missing set_in_callback around callback + + Closes #3837 + +- RELEASE-NOTES: synced + +- CURLMOPT_TIMERFUNCTION.3: warn about the recursive risk [ci skip] + + Reported-by: Ricardo Gomes + + Bug: #3537 + Closes #3836 + +- CURLOPT_CHUNK_BGN_FUNCTION.3: document the struct and time value + + The time field in the curl_fileinfo struct will always be zero. No code + was ever implemented to actually convert the date string to a time_t. + + Fixes #3829 + Closes #3835 + +- OS400/ccsidcurl.c: code style fixes + +- OS400/ccsidcurl: replace use of Curl_vsetopt + + (and make the code style comply) + + Fixes #3833 + +- urlapi: strip off scope id from numerical IPv6 addresses + + ... to make the host name "usable". Store the scope id and put it back + when extracting a URL out of it. + + Also makes curl_url_set() syntax check CURLUPART_HOST. + + Fixes #3817 + Closes #3822 + +- RELEASE-NOTES: synced + +- multiif.h: remove unused protos + + ... for functions related to pipelining. Those functions were removed in + 2f44e94efb3df. + + Closes #3828 + +- [Yiming Jing brought this change] + + travis: mesalink: temporarily disable test 3001 + + ... due to SHA-1 signatures in test certs + +- [Yiming Jing brought this change] + + travis: upgrade the MesaLink TLS backend to v1.0.0 + + Closes #3823 + Closes #3776 + +- ConnectionExists: improve non-multiplexing use case + + - better log output + + - make sure multiplex is enabled for it to be used + +- multi: provide Curl_multiuse_state to update information + + As soon as a TLS backend gets ALPN conformation about the specific HTTP + version it can now set the multiplex situation for the "bundle" and + trigger moving potentially queued up transfers to the CONNECT state. + +- process_pending_handles: mark queued transfers as previously pending + + With transfers being queued up, we only move one at a a time back to the + CONNECT state but now we mark moved transfers so that when a moved + transfer is confirmed "successful" (it connected) it will trigger the + move of another pending transfer. Previously, it would otherwise wait + until the transfer was done before doing this. This makes queued up + pending transfers get processed (much) faster. + +- http: mark bundle as not for multiuse on < HTTP/2 response + + Fixes #3813 + Closes #3815 + +Daniel Gustafsson (1 May 2019) +- cookie: Guard against possible NULL ptr deref + + In case the name pointer isn't set (due to memory pressure most likely) + we need to skip the prefix matching and reject with a badcookie to avoid + a possible NULL pointer dereference. + + Closes #3820 #3821 + Reported-by: Jonathan Moerman + Reviewed-by: Daniel Stenberg + +Patrick Monnerat (30 Apr 2019) +- os400: Add CURLOPT_MAXAGE_CONN to ILE/RPG bindings + +Kamil Dudka (29 Apr 2019) +- nss: provide more specific error messages on failed init + + Closes #3808 + +Daniel Stenberg (29 Apr 2019) +- [Reed Loden brought this change] + + docs: minor polish to the bug bounty / security docs + + Closes #3811 + +- CURL_MAX_INPUT_LENGTH: largest acceptable string input size + + This limits all accepted input strings passed to libcurl to be less than + CURL_MAX_INPUT_LENGTH (8000000) bytes, for these API calls: + curl_easy_setopt() and curl_url_set(). + + The 8000000 number is arbitrary picked and is meant to detect mistakes + or abuse, not to limit actual practical use cases. By limiting the + acceptable string lengths we also reduce the risk of integer overflows + all over. + + NOTE: This does not apply to `CURLOPT_POSTFIELDS`. + + Test 1559 verifies. + + Closes #3805 + +- [Tseng Jun brought this change] + + curlver.h: use parenthesis in CURL_VERSION_BITS macro + + Closes #3809 + +Marcel Raad (27 Apr 2019) +- [Simon Warta brought this change] + + cmake: rename CMAKE_USE_DARWINSSL to CMAKE_USE_SECTRANSP + + Closes https://github.com/curl/curl/pull/3769 + +Steve Holme (23 Apr 2019) +- ntlm: Missed pre-processor || (or) during rebase for cd15acd0 + +- ntlm: Support the NT response in the type-3 when OpenSSL doesn't include MD4 + + Just like we do for mbed TLS, use our local implementation of MD4 when + OpenSSL doesn't support it. This allows a type-3 message to include the + NT response. + +Daniel Gustafsson (23 Apr 2019) +- INTERNALS: fix misindentation of ToC item + + Kerberos was incorrectly indented as a subsection under FTP, which is + incorrect as they are both top level sections. A fix for this was first + attempted in commit fef38a0898322f285401c5ff2f5e7c90dbf3be63 but that + was a few paddles short of being complete. + +- [Aron Bergman brought this change] + + INTERNALS: Add structs to ToC + + Add the subsections under "Structs in libcurl" to the table of contents. + + Reviewed-by: Daniel Stenberg + Reviewed-by: Daniel Gustafsson + +- [Aron Bergman brought this change] + + INTERNALS: Add code highlighting + + Make all struct members under the Curl_handler section + print in monospace font. + + Closes #3801 + Reviewed-by: Daniel Stenberg + Reviewed-by: Daniel Gustafsson + +Daniel Stenberg (22 Apr 2019) +- docs/BUG-BOUNTY: bug bounty time [skip ci] + + Introducing the curl bug bounty program on hackerone. We now recommend + filing security issues directly in the hackerone ticket system which + only is readable to curl security team members. + + Assisted-by: Daniel Gustafsson + + Closes #3488 + +Steve Holme (22 Apr 2019) +- sasl: Don't send authcid as authzid for the PLAIN mechanism as per RFC 4616 + + RFC 4616 specifies the authzid is optional in the client authentication + message and that the server will derive the authorisation identity + (authzid) from the authentication identity (authcid) when not specified + by the client. + +Jay Satiro (22 Apr 2019) +- [Gisle Vanem brought this change] + + memdebug: fix variable name + + Follow-up to 76b6348 which renamed logfile as curl_dbg_logfile. + + Ref: https://github.com/curl/curl/commit/76b6348#r33259088 + +Steve Holme (21 Apr 2019) +- vauth/cleartext: Don't send the authzid if it is empty + + Follow up to 762a292f. + +Daniel Stenberg (21 Apr 2019) +- test 196,197,198: add 'retry' keyword [skip ci] + +- RELEASE-NOTES: synced + +- CURLOPT_MAXAGE_CONN: set the maximum allowed age for conn reuse + + ... and disconnect too old ones instead of trying to reuse. + + Default max age is set to 118 seconds. + + Ref: #3722 + Closes #3782 + +Daniel Gustafsson (20 Apr 2019) +- [Po-Chuan Hsieh brought this change] + + altsvc: Fix building with cookies disables + + ALTSVC requires Curl_get_line which is defined in lib/cookie.c inside a #if + check of HTTP and COOKIES. That makes Curl_get_line undefined if COOKIES is + disabled. Fix by splitting out the function into a separate file which can + be included where needed. + + Closes #3717 + Reviewed-by: Daniel Gustafsson + Reviewed-by: Marcel Raad + +Daniel Stenberg (20 Apr 2019) +- test1002: correct the name [skip ci] + +- test660: verify CONNECT_ONLY with IMAP + + which basically just makes sure LOGOUT is *not* issued on disconnect + +- Curl_disconnect: treat all CONNECT_ONLY connections as "dead" + + Since the connection has been used by the "outside" we don't know the + state of it anymore and curl should not use it anymore. + + Bug: https://curl.haxx.se/mail/lib-2019-04/0052.html + + Closes #3795 + +- multi: fix the statenames (follow-up fix from 2f44e94efb3df8e) + + The list of names must be in sync with the defined states in the header + file! + +Steve Holme (16 Apr 2019) +- openvms: Remove pre-processors for Windows as VMS cannot support them + +- openvms: Remove pre-processor for SecureTransport as VMS cannot support it + + Fixes #3768 + Closes #3785 + +Jay Satiro (16 Apr 2019) +- TODO: Add issue link to an existing entry + +Daniel Stenberg (16 Apr 2019) +- RELEASE-NOTES: synced + +Jay Satiro (16 Apr 2019) +- tool_help: Warn if curl and libcurl versions do not match + + .. because functionality may be affected if the versions differ. + + This commit implements TODO 18.7 "warning if curl version is not in sync + with libcurl version". + + Ref: https://github.com/curl/curl/blob/curl-7_64_1/docs/TODO#L1028-L1033 + + Closes https://github.com/curl/curl/pull/3774 + +Steve Holme (16 Apr 2019) +- md5: Update the function signature following d84da52d + +- md5: Forgot to update the code alignment in d84da52d + +- md5: Return CURLcode from the internally accessible functions + + Following 28f826b3 to return CURLE_OK instead of numeric 0. + +Daniel Gustafsson (15 Apr 2019) +- tests: Run global cleanup at end of tests + + Make sure to run curl_global_cleanup() when shutting down the test + suite to release any resources allocated in the SSL setup. This is + clearly visible when running tests with PolarSSL where the thread + lock calloc() memory which isn't released when not running cleanup. + Below is an excerpt from the autobuild logs: + + ==12368== 96 bytes in 1 blocks are possibly lost in loss record 1 of 2 + ==12368== at 0x4837B65: calloc (vg_replace_malloc.c:752) + ==12368== by 0x11A76E: curl_dbg_calloc (memdebug.c:205) + ==12368== by 0x145CDF: Curl_polarsslthreadlock_thread_setup + (polarssl_threadlock.c:54) + ==12368== by 0x145B37: Curl_polarssl_init (polarssl.c:865) + ==12368== by 0x14129D: Curl_ssl_init (vtls.c:171) + ==12368== by 0x118B4C: global_init (easy.c:158) + ==12368== by 0x118BF5: curl_global_init (easy.c:221) + ==12368== by 0x118D0B: curl_easy_init (easy.c:299) + ==12368== by 0x114E96: test (lib1906.c:32) + ==12368== by 0x115495: main (first.c:174) + + Closes #3783 + Reviewed-by: Marcel Raad + Reviewed-by: Daniel Stenberg + +Marcel Raad (15 Apr 2019) +- travis: use mbedtls from Xenial + + No need to build it from source anymore. + + Closes https://github.com/curl/curl/pull/3779 + +- travis: use libpsl from Xenial + + This makes building libpsl and libidn2 from source unnecessary and + removes the need for the autopoint and libunistring-dev packages. + + Closes https://github.com/curl/curl/pull/3779 + +Daniel Stenberg (15 Apr 2019) +- runtests: start socksd like other servers + + ... without a $srcdir prefix. Triggered by the failures in several + autobuilds. + + Closes #3781 + +Daniel Gustafsson (14 Apr 2019) +- socksd: Fix typos + + Reviewed-by: Daniel Stenberg + +- socksd: Properly decorate static variables + + Mark global variables static to avoid compiler warning in Clang when + using -Wmissing-variable-declarations. + + Closes #3778 + Reviewed-by: Daniel Stenberg + +Steve Holme (14 Apr 2019) +- md(4|5): Fixed indentation oddities with the importation of replacement code + + The indentation from 211d5329 and 57d6d253 was a little strange as + parts didn't align correctly, uses 4 spaces rather than 2. Checked + the indentation of the original source so it aligns, albeit, using + curl style. + +- md5: Code style to return CURLE_OK rather than numeric 0 + +- md5: Corrected code style for some pointer arguments + +Marcel Raad (13 Apr 2019) +- travis: update some builds to xenial + + Xenial comes with more up-to-date software versions and more available + packages, some of which we currently build from source. Unfortunately, + some builds would fail with Xenial because of assertion failures in + Valgrind when using OpenSSL, so leave these at Trusty. + + Closes https://github.com/curl/curl/pull/3777 + +Daniel Stenberg (13 Apr 2019) +- test: make tests and test scripts use socksd for SOCKS + + Make all SOCKS tests use socksd instead of ssh. + +- socksd: new SOCKS 4+5 server for tests + + Closes #3752 + +- singleipconnect: show port in the verbose "Trying ..." message + + To aid debugging better. + +- [tmilburn brought this change] + + CURLOPT_ADDRESS_SCOPE: fix range check and more + + Commit 9081014 fixed most of the confusing issues between scope id and + scope however 844896d added bad limits checking assuming that the scope + is being set and not the scope id. + + I have fixed the documentation so it all refers to scope ids. + + In addition Curl_if2ip refered to the scope id as remote_scope_id which + is incorrect, so I renamed it to local_scope_id. + + Adjusted-by: Daniel Stenberg + + Closes #3655 + Closes #3765 + Fixes #3713 + +- urlapi: stricter CURLUPART_PORT parsing + + Only allow well formed decimal numbers in the input. + + Document that the number MUST be between 1 and 65535. + + Add tests to test 1560 to verify the above. + + Ref: https://github.com/curl/curl/issues/3753 + Closes #3762 + +Jay Satiro (13 Apr 2019) +- [Jan Ehrhardt brought this change] + + winbuild: Support MultiSSL builds + + - Remove the lines in winbuild/Makefile.vc that generate an error with + multiple SSL backends. + + - Add /DCURL_WITH_MULTI_SSL in winbuild/MakefileBuild.vc if multiple SSL + backends are set. + + Closes https://github.com/curl/curl/pull/3772 + +Daniel Stenberg (12 Apr 2019) +- travis: remove mesalink builds (temporarily?) + + Since the mesalink build started to fail on travis, even though we build + a fixed release version, we disable it to prevent it from blocking + progress. + + Closes #3767 + +- openssl: mark connection for close on TLS close_notify + + Without this, detecting and avoid reusing a closed TLS connection + (without a previous GOAWAY) when doing HTTP/2 is tricky. + + Reported-by: Tom van der Woerdt + Fixes #3750 + Closes #3763 + +- RELEASE-NOTES: synced + +Steve Holme (11 Apr 2019) +- vauth/cleartext: Update the PLAIN login function signature to match RFC 4616 + + Functionally this doesn't change anything as we still use the username + for both the authorisation identity and the authentication identity. + + Closes #3757 + +Daniel Stenberg (11 Apr 2019) +- test1906: verify CURLOPT_CURLU + CURLOPT_PORT usage + + Based-on-code-by: Poul T Lomholt + +- url: always clone the CUROPT_CURLU handle + + Since a few code paths actually update that data. + + Fixes #3753 + Closes #3761 + + Reported-by: Poul T Lomholt + +- CURLOPT_DNS_USE_GLOBAL_CACHE: remove + + Remove the code too. The functionality has been disabled in code since + 7.62.0. Setting this option will from now on simply be ignored and have + no function. + + Closes #3654 + +Marcel Raad (11 Apr 2019) +- travis: install libgnutls28-dev only for --with-gnutls build + + Reduces the time needed for the other jobs a little. + + Closes https://github.com/curl/curl/pull/3721 + +- travis: install libnss3-dev only for --with-nss build + + Reduces the time needed for the other jobs a little. + + Closes https://github.com/curl/curl/pull/3721 + +- travis: install libssh2-dev only for --with-libssh2 build + + Reduces the time needed for the other jobs a little. + + Closes https://github.com/curl/curl/pull/3721 + +- travis: install libssh-dev only for --with-libssh build + + Reduces the time needed for the other jobs a little. + + Closes https://github.com/curl/curl/pull/3721 + +- travis: install krb5-user only for --with-gssapi build + + Reduces the time needed for the other jobs a little. + + Closes https://github.com/curl/curl/pull/3721 + +- travis: install lcov only for the coverage job + + Reduces the time needed for the other jobs a little. + + Closes https://github.com/curl/curl/pull/3721 + +- travis: install clang only when needed + + This reduces the GCC job runtimes a little and it's needed to + selectively update clang builds to xenial. + + Closes https://github.com/curl/curl/pull/3721 + +- AppVeyor: enable testing for WinSSL build + + Closes https://github.com/curl/curl/pull/3725 + +- build: fix Codacy/CppCheck warnings + + - remove unused variables + - declare conditionally used variables conditionally + - suppress unused variable warnings in the CMake tests + - remove dead variable stores + - consistently use WIN32 macro to detect Windows + + Closes https://github.com/curl/curl/pull/3739 + +- polarssl_threadlock: remove conditionally unused code + + Make functions no-ops if neither both USE_THREADS_POSIX and + HAVE_PTHREAD_H nor both USE_THREADS_WIN32 and HAVE_PROCESS_H are + defined. Previously, if only one of them was defined, there was either + code compiled that did nothing useful or the wrong header included for + the functions used. + + Also, move POLARSSL_MUTEX_T define to implementation file as it's not + used externally. + + Closes https://github.com/curl/curl/pull/3739 + +- lib557: initialize variables + + These variables are only conditionally initialized. + + Closes https://github.com/curl/curl/pull/3739 + +- lib509: add missing include for strdup + + Closes https://github.com/curl/curl/pull/3739 + +- README.md: fix no-consecutive-blank-lines Codacy warning + + Consistently use one blank line between blocks. + + Closes https://github.com/curl/curl/pull/3739 + +- tests/server/util: fix Windows Unicode build + + Always use the ANSI version of FormatMessage as we don't have the + curl_multibyte gear available here. + + Closes https://github.com/curl/curl/pull/3758 + +Daniel Stenberg (11 Apr 2019) +- curl_easy_getinfo.3: fix minor formatting mistake + +Daniel Gustafsson (11 Apr 2019) +- xattr: skip unittest on unsupported platforms + + The stripcredentials unittest fails to compile on platforms without + xattr support, for example the Solaris member in the buildfarm which + fails with the following: + + CC unit1621-unit1621.o + CC ../libtest/unit1621-first.o + CCLD unit1621 + Undefined first referenced + symbol in file + stripcredentials unit1621-unit1621.o + goto problem 2 + ld: fatal: symbol referencing errors. No output written to .libs/unit1621 + collect2: error: ld returned 1 exit status + gmake[2]: *** [Makefile:996: unit1621] Error 1 + + Fix by excluding the test on such platforms by using the reverse + logic from where stripcredentials() is defined. + + Closes #3759 + Reviewed-by: Daniel Stenberg + +Steve Holme (11 Apr 2019) +- emailL Added reference to RFC8314 for implicit TLS + +- README: Schannel, stop calling it "winssl" + + Stick to "Schannel" everywhere - follow up to 180501cb. + +Jakub Zakrzewski (10 Apr 2019) +- cmake: clear CMAKE_REQUIRED_LIBRARIES after each use + + This fixes GSSAPI builds with the libraries in a non-standard location. + The testing for recv() were failing because it failed to link + the Kerberos libraries, which are not needed for this or subsequent + tests. + + fixes #3743 + closes #3744 + +- cmake: avoid linking executable for some tests with cmake 3.6+ + + With CMAKE_TRY_COMPILE_TARGET_TYPE set to STATIC_LIBRARY, the try_compile() + (which is used by check_c_source_compiles()) will build static library + instead of executable. This avoids linking additional libraries in and thus + speeds up those checks a little. + + This commit also avoids #3743 (GSSAPI build errors) on itself with cmake + 3.6 or above. That issue was fixed separately for all versions. + + Ref: #3744 + +- cmake: minor cleanup + + - Remove nneeded include_regular_expression. + It was setting what is already a default. + + - Remove duplicated include. + + - Don't check for pre-3.0.0 CMake version. + We already require at least 3.0.0, so it's just clutter. + + Ref: #3744 + +Steve Holme (8 Apr 2019) +- build-openssl.bat: Fixed support for OpenSSL v1.1.0+ + +- build-openssl.bat: Perfer the use of if statements rather than goto (where possible) + +- build-openssl.bat: Perform the install for each build type directly after the build + +- build-openssl.bat: Split the install of static and shared build types + +- build-openssl.bat: Split the building of static and shared build types + +- build-openssl.bat: Move the installation into a separate function + +- build-openssl.bat: Move the build step into a separate function + +- build-openssl.bat: Move the OpenSSL configuration into a separate function + +- build-openssl.bat: Fixed the BUILD_CONFIG variable not being initialised + + Should the parent environment set this variable then the build might + not be performed as the user intended. + +Daniel Stenberg (8 Apr 2019) +- socks: fix error message + +- config.d: clarify that initial : and = might need quoting [skip ci] + + Fixes #3738 + Closes #3749 + +- RELEASE-NOTES: synced + + bumped to 7.65.0 for next release + +- socks5: user name and passwords must be shorter than 256 + + bytes... since the protocol needs to store the length in a single byte field. + + Reported-by: XmiliaH on github + Fixes #3737 + Closes #3740 + +- [Jakub Zakrzewski brought this change] + + test: urlapi: urlencode characters above 0x7f correctly + +- [Jakub Zakrzewski brought this change] + + urlapi: urlencode characters above 0x7f correctly + + fixes #3741 + Closes #3742 + +- [Even Rouault brought this change] + + multi_runsingle(): fix use-after-free + + Fixes #3745 + Closes #3746 + + The following snippet + ``` + + int main() + { + CURL* hCurlHandle = curl_easy_init(); + curl_easy_setopt(hCurlHandle, CURLOPT_URL, "http://example.com"); + curl_easy_setopt(hCurlHandle, CURLOPT_PROXY, "1"); + curl_easy_perform(hCurlHandle); + curl_easy_cleanup(hCurlHandle); + return 0; + } + ``` + triggers the following Valgrind warning + + ``` + ==4125== Invalid read of size 8 + ==4125== at 0x4E7D1EE: Curl_llist_remove (llist.c:97) + ==4125== by 0x4E7EF5C: detach_connnection (multi.c:798) + ==4125== by 0x4E80545: multi_runsingle (multi.c:1451) + ==4125== by 0x4E8197C: curl_multi_perform (multi.c:2072) + ==4125== by 0x4E766A0: easy_transfer (easy.c:625) + ==4125== by 0x4E76915: easy_perform (easy.c:719) + ==4125== by 0x4E7697C: curl_easy_perform (easy.c:738) + ==4125== by 0x4008BE: main (in /home/even/curl/test) + ==4125== Address 0x9b3d1d0 is 1,120 bytes inside a block of size 1,600 free'd + ==4125== at 0x4C2ECF0: free (vg_replace_malloc.c:530) + ==4125== by 0x4E62C36: conn_free (url.c:756) + ==4125== by 0x4E62D34: Curl_disconnect (url.c:818) + ==4125== by 0x4E48DF9: Curl_once_resolved (hostip.c:1097) + ==4125== by 0x4E8052D: multi_runsingle (multi.c:1446) + ==4125== by 0x4E8197C: curl_multi_perform (multi.c:2072) + ==4125== by 0x4E766A0: easy_transfer (easy.c:625) + ==4125== by 0x4E76915: easy_perform (easy.c:719) + ==4125== by 0x4E7697C: curl_easy_perform (easy.c:738) + ==4125== by 0x4008BE: main (in /home/even/curl/test) + ==4125== Block was alloc'd at + ==4125== at 0x4C2F988: calloc (vg_replace_malloc.c:711) + ==4125== by 0x4E6438E: allocate_conn (url.c:1654) + ==4125== by 0x4E685B4: create_conn (url.c:3496) + ==4125== by 0x4E6968F: Curl_connect (url.c:4023) + ==4125== by 0x4E802E7: multi_runsingle (multi.c:1368) + ==4125== by 0x4E8197C: curl_multi_perform (multi.c:2072) + ==4125== by 0x4E766A0: easy_transfer (easy.c:625) + ==4125== by 0x4E76915: easy_perform (easy.c:719) + ==4125== by 0x4E7697C: curl_easy_perform (easy.c:738) + ==4125== by 0x4008BE: main (in /home/even/curl/test) + ``` + + This has been bisected to commit 2f44e94 + + Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=14109 + Credit to OSS Fuzz + +- pipelining: removed + + As previously planned and documented in DEPRECATE.md, all pipelining + code is removed. + + Closes #3651 + +- [cclauss brought this change] + + tests: make Impacket (SMB server) Python 3 compatible + + Closes #3731 + Fixes #3289 + +Marcel Raad (6 Apr 2019) +- [Simon Warta brought this change] + + cmake: set SSL_BACKENDS + + This groups all SSL backends into the feature "SSL" and sets the + SSL_BACKENDS analogue to configure.ac + + Closes https://github.com/curl/curl/pull/3736 + +- [Simon Warta brought this change] + + cmake: don't run SORT on empty list + + In case of an empty list, SORTing leads to the cmake error "list + sub-command SORT requires list to be present." + + Closes https://github.com/curl/curl/pull/3736 + +Daniel Gustafsson (5 Apr 2019) +- [Eli Schwartz brought this change] + + configure: fix default location for fish completions + + Fish defines a vendor completions directory for completions that are not + installed as part of the fish project itself, and the vendor completions + are preferred if they exist. This prevents trying to overwrite the + builtin curl.fish completion (or creating file conflicts in distro + packaging). + + Prefer the pkg-config defined location exported by fish, if it can be + found, and fall back to the correct directory defined by most systems. + + Closes #3723 + Reviewed-by: Daniel Gustafsson + +Marcel Raad (5 Apr 2019) +- ftplistparser: fix LGTM alert "Empty block without comment" + + Removing the block is consistent with line 954/957. + + Closes https://github.com/curl/curl/pull/3732 + +- transfer: fix LGTM alert "Comparison is always true" + + Just remove the redundant condition, which also makes it clear that + k->buf is always 0-terminated if this break is not hit. + + Closes https://github.com/curl/curl/pull/3732 + +Jay Satiro (4 Apr 2019) +- [Rikard Falkeborn brought this change] + + smtp: fix compiler warning + + - Fix clang string-plus-int warning. + + Clang 8 warns about adding a string to an int does not append to the + string. Indeed it doesn't, but that was not the intention either. Use + array indexing as suggested to silence the warning. There should be no + functional changes. + + (In other words clang warns about "foo"+2 but not &"foo"[2] so use the + latter.) + + smtp.c:1221:29: warning: adding 'int' to a string does not append to the + string [-Wstring-plus-int] + eob = strdup(SMTP_EOB + 2); + ~~~~~~~~~~~~~~~~^~~~ + + Closes https://github.com/curl/curl/pull/3729 + +Marcel Raad (4 Apr 2019) +- VS projects: use Unicode for VC10+ + + All Windows APIs have been natively UTF-16 since Windows 2000 and the + non-Unicode variants are just wrappers around them. Only Windows 9x + doesn't understand Unicode without the UnicoWS DLL. As later Visual + Studio versions cannot target Windows 9x anyway, using the ANSI API + doesn't really have any benefit there. + + This avoids issues like KNOWN_BUGS 6.5. + + Ref: https://github.com/curl/curl/issues/2120 + Closes https://github.com/curl/curl/pull/3720 + +Daniel Gustafsson (3 Apr 2019) +- RELEASE-NOTES: synced + + Bump the version in progress to 7.64.2, if we merge any "change" + before the cut-off date we can update the version. + +- [Tim Rühsen brought this change] + + documentation: Fix several typos + + Closes #3724 + Reviewed-by: Jakub Zakrzewski + Reviewed-by: Daniel Gustafsson + +Jay Satiro (2 Apr 2019) +- [Mert Yazıcıoğlu brought this change] + + vauth/oauth2: Fix OAUTHBEARER token generation + + OAUTHBEARER tokens were incorrectly generated in a format similar to + XOAUTH2 tokens. These changes make OAUTHBEARER tokens conform to the + RFC7628. + + Fixes: #2487 + Reported-by: Paolo Mossino + + Closes https://github.com/curl/curl/pull/3377 + +Marcel Raad (2 Apr 2019) +- tool_cb_wrt: fix bad-function-cast warning + + Commit f5bc578f4cdfdc6c708211dfc2962a0e9d79352d reintroduced the + warning fixed in commit 2f5f31bb57d68b54e03bffcd9648aece1fe564f8. + Extend fhnd's scope and reuse that variable instead of calling + _get_osfhandle a second time to fix the warning again. + + Closes https://github.com/curl/curl/pull/3718 + +- VC15 project: remove MinimalRebuild + + Already done in commit d5cfefd0ea8e331b884186bff484210fad36e345 for the + library project, but I forgot the tool project template. Now also + removed for that. + +Dan Fandrich (1 Apr 2019) +- cirrus: Customize the disabled tests per FreeBSD version + + Try to run as many test cases as possible on each OS version. + 12.0 passes 13 more tests than the older versions, so we might as well + run them. + +Daniel Stenberg (1 Apr 2019) +- tool_help: include for strcasecmp + + Reported-by: Wyatt O'Day + Fixes #3715 + Closes #3716 + +Daniel Gustafsson (31 Mar 2019) +- scripts: fix typos + +Dan Fandrich (28 Mar 2019) +- travis: allow builds on branches named "ci" + + This allows a way to test changes other than through PRs. + +Daniel Stenberg (27 Mar 2019) +- [Brad Spencer brought this change] + + resolve: apply Happy Eyeballs philosophy to parallel c-ares queries + + Closes #3699 + +- multi: improved HTTP_1_1_REQUIRED handling + + Make sure to downgrade to 1.1 even when we get this HTTP/2 stream error + on first flight. + + Reported-by: niner on github + Fixes #3696 + Closes #3707 + +- [Leonardo Taccari brought this change] + + configure: avoid unportable `==' test(1) operator + + Closes #3709 + +Version 7.64.1 (27 Mar 2019) + +Daniel Stenberg (27 Mar 2019) +- RELEASE: 7.64.1 + +- Revert "ntlm: remove USE_WIN32_CRYPTO check to get USE_NTLM2SESSION set" + + This reverts commit 9130ead9fcabdb6b8fbdb37c0b38be2d326adb00. + + Fixes #3708 + +- [Christian Schmitz brought this change] + + ntlm: remove USE_WIN32_CRYPTO check to get USE_NTLM2SESSION set + + Closes #3704 + +Jay Satiro (26 Mar 2019) +- tool_cb_wrt: fix writing to Windows null device NUL + + - Improve console detection. + + Prior to this change WriteConsole could be called to write to a handle + that may not be a console, which would cause an error. This issue is + limited to character devices that are not also consoles such as the null + device NUL. + + Bug: https://github.com/curl/curl/issues/3175#issuecomment-439068724 + Reported-by: Gisle Vanem + +- CURLMOPT_PIPELINING.3: fix typo + +Daniel Stenberg (25 Mar 2019) +- TODO: config file parsing + + Closes #3698 + +Jay Satiro (24 Mar 2019) +- os400: Disable Alt-Svc by default since it's experimental + + Follow-up to 520f0b4 which added Alt-Svc support and enabled it by + default for OS400. Since the feature is experimental, it should be + disabled by default. + + Ref: https://github.com/curl/curl/commit/520f0b4#commitcomment-32792332 + Ref: https://curl.haxx.se/mail/lib-2019-02/0008.html + + Closes https://github.com/curl/curl/pull/3688 + +Dan Fandrich (24 Mar 2019) +- tests: Fixed XML validation errors in some test files. + +- tests: Fix some incorrect precheck error messages. + + [ci skip] + +Daniel Stenberg (22 Mar 2019) +- curl_url.3: this is not experimental anymore + +- travis: bump the used wolfSSL version to 4.0.0 + + Test 311 is now fine, leaving only 313 (CRL) disabled. + + Test 313 details can be found here: + https://github.com/wolfSSL/wolfssl/issues/1546 + + Closes #3697 + +Daniel Gustafsson (22 Mar 2019) +- lib: Fix typos in comments + +David Woodhouse (20 Mar 2019) +- openssl: if cert type is ENG and no key specified, key is ENG too + + Fixes #3692 + Closes #3692 + +Daniel Stenberg (20 Mar 2019) +- sectransp: tvOS 11 is required for ALPN support + + Reported-by: nianxuejie on github + Assisted-by: Nick Zitzmann + Assisted-by: Jay Satiro + Fixes #3689 + Closes #3690 + +- test1541: threaded connection sharing + + The threaded-shared-conn.c example turned into test case. Only works if + pthread was detected. + + An attempt to detect future regressions such as e3a53e3efb942a5 + + Closes #3687 + +Patrick Monnerat (17 Mar 2019) +- os400: alt-svc support. + + Although experimental, enable it in the platform config file. + Upgrade ILE/RPG binding. + +Daniel Stenberg (17 Mar 2019) +- conncache: use conn->data to know if a transfer owns it + + - make sure an already "owned" connection isn't returned unless + multiplexed. + + - clear ->data when returning the connection to the cache again + + Regression since 7.62.0 (probably in commit 1b76c38904f0) + + Bug: https://curl.haxx.se/mail/lib-2019-03/0064.html + + Closes #3686 + +- RELEASE-NOTES: synced + +- [Chris Young brought this change] + + configure: add --with-amissl + + AmiSSL is an Amiga native library which provides a wrapper over OpenSSL. + It also requires all programs using it to use bsdsocket.library + directly, rather than accessing socket functions through clib, which + libcurl was not necessarily doing previously. Configure will now check + for the headers and ensure they are included if found. + + Closes #3677 + +- [Chris Young brought this change] + + vtls: rename some of the SSL functions + + ... in the SSL structure as AmiSSL is using macros for the socket API + functions. + +- [Chris Young brought this change] + + tool_getpass: termios.h is present on AmigaOS 3, but no tcgetattr/tcsetattr + +- [Chris Young brought this change] + + tool_operate: build on AmigaOS + +- makefile: make checksrc and hugefile commands "silent" + + ... to match the style already used for compiling, linking + etc. Acknowledges 'make V=1' to enable verbose. + + Closes #3681 + +- curl.1: --user and --proxy-user are hidden from ps output + + Suggested-by: Eric Curtin + Improved-by: Dan Fandrich + Ref: #3680 + + Closes #3683 + +- curl.1: mark the argument to --cookie as + + From a discussion in #3676 + + Suggested-by: Tim Rühsen + + Closes #3682 + +Dan Fandrich (14 Mar 2019) +- fuzzer: Only clone the latest fuzzer code, for speed. + +Daniel Stenberg (14 Mar 2019) +- [Dominik Hölzl brought this change] + + Negotiate: fix for HTTP POST with Negotiate + + * Adjusted unit tests 2056, 2057 + * do not generally close connections with CURLAUTH_NEGOTIATE after every request + * moved negotiatedata from UrlState to connectdata + * Added stream rewind logic for CURLAUTH_NEGOTIATE + * introduced negotiatedata::GSS_AUTHDONE and negotiatedata::GSS_AUTHSUCC + * Consider authproblem state for CURLAUTH_NEGOTIATE + * Consider reuse_forbid for CURLAUTH_NEGOTIATE + * moved and adjusted negotiate authentication state handling from + output_auth_headers into Curl_output_negotiate + * Curl_output_negotiate: ensure auth done is always set + * Curl_output_negotiate: Set auth done also if result code is + GSS_S_CONTINUE_NEEDED/SEC_I_CONTINUE_NEEDED as this result code may + also indicate the last challenge request (only works with disabled + Expect: 100-continue and CURLOPT_KEEP_SENDING_ON_ERROR -> 1) + * Consider "Persistent-Auth" header, detect if not present; + Reset/Cleanup negotiate after authentication if no persistent + authentication + * apply changes introduced with #2546 for negotiate rewind logic + + Fixes #1261 + Closes #1975 + +- [Marc Schlatter brought this change] + + http: send payload when (proxy) authentication is done + + The check that prevents payload from sending in case of authentication + doesn't check properly if the authentication is done or not. + + They're cases where the proxy respond "200 OK" before sending + authentication challenge. This change takes care of that. + + Fixes #2431 + Closes #3669 + +- file: fix "Checking if unsigned variable 'readcount' is less than zero." + + Pointed out by codacy + + Closes #3672 + +- memdebug: log pointer before freeing its data + + Coverity warned for two potentional "Use after free" cases. Both are false + positives because the memory wasn't used, it was only the actual pointer + value that was logged. + + The fix still changes the order of execution to avoid the warnings. + + Coverity CID 1443033 and 1443034 + + Closes #3671 + +- RELEASE-NOTES: synced + +Marcel Raad (12 Mar 2019) +- travis: actually use updated compiler versions + + For the Linux builds, GCC 8 and 7 and clang 7 were installed, but the + new GCC versions were only used for the coverage build and for building + nghttp2, while the new clang version was not used at all. + + BoringSSL needs to use the default GCC as it respects CC, but not CXX, + so it would otherwise pass gcc 8 options to g++ 4.8 and fail. + + Also remove GCC 7, it's not needed anymore. + + Ref: https://docs.travis-ci.com/user/languages/c/#c11c11-and-beyond-and-toolchain-versioning + + Closes https://github.com/curl/curl/pull/3670 + +- travis: update clang to version 7 + + Closes https://github.com/curl/curl/pull/3670 + +Jay Satiro (11 Mar 2019) +- [Andre Guibert de Bruet brought this change] + + examples/externalsocket: add missing close socket calls + + .. and for Windows also call WSACleanup since we call WSAStartup. + + The example is to demonstrate handling the socket independently of + libcurl. In this case libcurl is not responsible for creating, opening + or closing the socket, it is handled by the application (our example). + + Fixes https://github.com/curl/curl/pull/3663 + +Daniel Stenberg (11 Mar 2019) +- multi: removed unused code for request retries + + This code was once used for the non multi-interface using code path, but + ever since easy_perform was turned into a wrapper around the multi + interface, this code path never runs. + + Closes #3666 + +Jay Satiro (11 Mar 2019) +- doh: inherit some SSL options from user's easy handle + + - Inherit SSL options for the doh handle but not SSL client certs, + SSL ALPN/NPN, SSL engine, SSL version, SSL issuer cert, + SSL pinned public key, SSL ciphers, SSL id cache setting, + SSL kerberos or SSL gss-api settings. + + - Fix inheritance of verbose setting. + + - Inherit NOSIGNAL. + + There is no way for the user to set options for the doh (DNS-over-HTTPS) + handles and instead we inherit some options from the user's easy handle. + + My thinking for the SSL options not inherited is they are most likely + not intended by the user for the DOH transfer. I did inherit insecure + because I think that should still be in control of the user. + + Prior to this change doh did not work for me because CAINFO was not + inherited. Also verbose was set always which AFAICT was a bug (#3660). + + Fixes https://github.com/curl/curl/issues/3660 + Closes https://github.com/curl/curl/pull/3661 + +Daniel Stenberg (9 Mar 2019) +- test331: verify set-cookie for dotless host name + + Reproduced bug #3649 + Closes #3659 + +- Revert "cookies: extend domain checks to non psl builds" + + This reverts commit 3773de378d48b06c09931e44dca4d274d0bfdce0. + + Regression shipped in 7.64.0 + Fixes #3649 + +- memdebug: make debug-specific functions use curl_dbg_ prefix + + To not "collide" or use up the regular curl_ name space. Also makes them + easier to detect in helper scripts. + + Closes #3656 + +- cmdline-opts/proxytunnel.d: the option tunnnels all protocols + + Clarify the language and simplify. + + Reported-by: Daniel Lublin + Closes #3658 + +- KNOWN_BUGS: Client cert (MTLS) issues with Schannel + + Closes #3145 + +- ROADMAP: updated to some more current things to work on + +- tests: fix multiple may be used uninitialized warnings + +- RELEASE-NOTES: synced + +- source: fix two 'nread' may be used uninitialized warnings + + Both seem to be false positives but we don't like warnings. + + Closes #3646 + +- gopher: remove check for path == NULL + + Since it can't be NULL and it makes Coverity believe we lack proper NULL + checks. Verified by test 659, landed in commit 15401fa886b. + + Pointed out by Coverity CID 1442746. + + Assisted-by: Dan Fandrich + Fixes #3617 + Closes #3642 + +- examples: only include + + That's the only public curl header we should encourage use of. + + Reviewed-by: Marcel Raad + Closes #3645 + +- ssh: loop the state machine if not done and not blocking + + If the state machine isn't complete, didn't fail and it didn't return + due to blocking it can just as well loop again. + + This addresses the problem with SFTP directory listings where we would + otherwise return back to the parent and as the multi state machine + doesn't have any code for using CURLM_CALL_MULTI_PERFORM for as long the + doing phase isn't complete, it would return out when in reality there + was more data to deal with. + + Fixes #3506 + Closes #3644 + +Jay Satiro (5 Mar 2019) +- multi: support verbose conncache closure handle + + - Change closure handle to receive verbose setting from the easy handle + most recently added via curl_multi_add_handle. + + The closure handle is a special easy handle used for closing cached + connections. It receives limited settings from the easy handle most + recently added to the multi handle. Prior to this change that did not + include verbose which was a problem because on connection shutdown + verbose mode was not acknowledged. + + Ref: https://github.com/curl/curl/pull/3598 + + Co-authored-by: Daniel Stenberg + + Closes https://github.com/curl/curl/pull/3618 + +Daniel Stenberg (4 Mar 2019) +- CURLU: fix NULL dereference when used over proxy + + Test 659 verifies + + Also fixed the test 658 name + + Closes #3641 + +- altsvc_out: check the return code from Curl_gmtime + + Pointed out by Coverity, CID 1442956. + + Closes #3640 + +- docs/ALTSVC.md: docs describing the approach + + Closes #3498 + +- alt-svc: add a travis build + +- alt-svc: add test 355 and 356 to verify with command line curl + +- alt-svc: the curl command line bits + +- alt-svc: the libcurl bits + +- travis: add build using gnutls + + Closes #3637 + +- RELEASE-NOTES: synced + +- [Simon Legner brought this change] + + scripts/completion.pl: also generate fish completion file + + This is the renamed script formerly known as zsh.pl + + Closes #3545 + +- gnutls: remove call to deprecated gnutls_compression_get_name + + It has been deprecated by GnuTLS since a year ago and now causes build + warnings. + + Ref: https://gitlab.com/gnutls/gnutls/commit/b0041897d2846737f5fb0f + Docs: https://www.gnutls.org/manual/html_node/Compatibility-API.html + + Closes #3636 + +Jay Satiro (2 Mar 2019) +- system_win32: move win32_init here from easy.c + + .. since system_win32 is a more appropriate location for the functions + and to extern the globals. + + Ref: https://github.com/curl/curl/commit/ca597ad#r32446578 + Reported-by: Gisle Vanem + + Closes https://github.com/curl/curl/pull/3625 + +Daniel Stenberg (1 Mar 2019) +- curl_easy_duphandle.3: clarify that a duped handle has no shares + + Reported-by: Sara Golemon + + Fixes #3592 + Closes #3634 + +- 10-at-a-time.c: fix too long line + +- [Arnaud Rebillout brought this change] + + examples: various fixes in ephiperfifo.c + + The main change here is the timer value that was wrong, it was given in + usecs (ms * 1000), while the itimerspec struct wants nsecs (ms * 1000 * + 1000). This resulted in the callback being invoked WAY TOO OFTEN. + + As a quick check you can run this command before and after applying this + commit: + + # shell 1 + ./ephiperfifo 2>&1 | tee ephiperfifo.log + # shell 2 + echo http://hacking.elboulangero.com > hiper.fifo + + Then just compare the size of the logs files. + + Closes #3633 + Fixes #3632 + Signed-off-by: Arnaud Rebillout + +- urldata: simplify bytecounters + + - no need to have them protocol specific + + - no need to set pointers to them with the Curl_setup_transfer() call + + - make Curl_setup_transfer() operate on a transfer pointer, not + connection + + - switch some counters from long to the more proper curl_off_t type + + Closes #3627 + +- examples/10-at-a-time.c: improve readability and simplify + + - use better variable names to explain their purposes + - convert logic to curl_multi_wait() + +- threaded-resolver: shutdown the resolver thread without error message + + When a transfer is done, the resolver thread will be brought down. That + could accidentally generate an error message in the error buffer even + though this is not an error situationand the transfer would still return + OK. An application that still reads the error buffer could find a + "Could not resolve host: [host name]" message there and get confused. + + Reported-by: Michael Schmid + Fixes #3629 + Closes #3630 + +- [Ԝеѕ brought this change] + + docs: update max-redirs.d phrasing + + clarify redir - "in absurdum" doesn't seem to make sense in this context + + Closes #3631 + +- ssh: fix Condition '!status' is always true + + in the same sftp_done function in both SSH backends. Simplify them + somewhat. + + Pointed out by Codacy. + + Closes #3628 + +- test578: make it read data from the correct test + +- Curl_easy: remove req.maxfd - never used! + + Introduced in 8b6314ccfb, but not used anymore in current code. Unclear + since when. + + Closes #3626 + +- http: set state.infilesize when sending formposts + + Without it set, we would unwillingly triger the "HTTP error before end + of send, stop sending" condition even if the entire POST body had been + sent (since it wouldn't know the expected size) which would + unnecessarily log that message and close the connection when it didn't + have to. + + Reported-by: Matt McClure + Bug: https://curl.haxx.se/mail/archive-2019-02/0023.html + Closes #3624 + +- INSTALL: refer to the current TLS library names and configure options + +- FAQ: minor updates and spelling fixes + +- GOVERNANCE.md: minor spelling fixes + +- Secure Transport: no more "darwinssl" + + Everyone calls it Secure Transport, now we do too. + + Reviewed-by: Nick Zitzmann + + Closes #3619 + +Marcel Raad (27 Feb 2019) +- AppVeyor: add classic MinGW build + + But use the MSYS2 shell rather than the default MSYS shell because of + POSIX path conversion issues. Classic MinGW is only available on the + Visual Studio 2015 image. + + Closes https://github.com/curl/curl/pull/3623 + +- AppVeyor: add MinGW-w64 build + + Add a MinGW-w64 build using CMake's MSYS Makefiles generator. + Use the Visual Studio 2015 image as it has GCC 8, while the + Visual Studio 2017 image only has GCC 7.2. + + Closes https://github.com/curl/curl/pull/3623 + +Daniel Stenberg (27 Feb 2019) +- cookies: only save the cookie file if the engine is enabled + + Follow-up to 8eddb8f4259. + + If the cookieinfo pointer is NULL there really is nothing to save. + + Without this fix, we got a problem when a handle was using shared object + with cookies and is told to "FLUSH" it to file (which worked) and then + the share object was removed and when the easy handle was closed just + afterwards it has no cookieinfo and no cookies so it decided to save an + empty jar (overwriting the file just flushed). + + Test 1905 now verifies that this works. + + Assisted-by: Michael Wallner + Assisted-by: Marcel Raad + + Closes #3621 + +- [DaVieS brought this change] + + cacertinmem.c: use multiple certificates for loading CA-chain + + Closes #3421 + +- urldata: convert bools to bitfields and move to end + + This allows the compiler to pack and align the structs better in + memory. For a rather feature-complete build on x86_64 Linux, gcc 8.1.2 + makes the Curl_easy struct 4.9% smaller. From 6312 bytes to 6000. + + Removed an unused struct field. + + No functionality changes. + + Closes #3610 + +- [Don J Olmstead brought this change] + + curl.h: use __has_declspec_attribute for shared builds + + Closes #3616 + +- curl: display --version features sorted alphabetically + + Closes #3611 + +- runtests: detect "schannel" as an alias for "winssl" + + Follow-up to 180501cb02 + + Reported-by: Marcel Raad + Fixes #3609 + Closes #3620 + +Marcel Raad (26 Feb 2019) +- AppVeyor: update to Visual Studio 2017 + + Switch all Visual Studio 2015 builds to Visual Studio 2017. It's not a + moving target anymore as the last update, Update 9, has been released. + + Closes https://github.com/curl/curl/pull/3606 + +- AppVeyor: switch VS 2015 builds to VS 2017 image + + The Visual Studio 2017 image has Visual Studio 2015 and 2017 installed. + + Closes https://github.com/curl/curl/pull/3606 + +- AppVeyor: explicitly select worker image + + Currently, we're using the default Visual Studio 2015 image for + everything. + + Closes https://github.com/curl/curl/pull/3606 + +Daniel Stenberg (26 Feb 2019) +- strerror: make the strerror function use local buffers + + Instead of using a fixed 256 byte buffer in the connectdata struct. + + In my build, this reduces the size of the connectdata struct by 11.8%, + from 2160 to 1904 bytes with no functionality or performance loss. + + This also fixes a bug in schannel's Curl_verify_certificate where it + called Curl_sspi_strerror when it should have called Curl_strerror for + string from GetLastError. the only effect would have been no text or the + wrong text being shown for the error. + + Co-authored-by: Jay Satiro + + Closes #3612 + +- [Michael Wallner brought this change] + + cookies: fix NULL dereference if flushing cookies with no CookieInfo set + + Regression brought by a52e46f3900fb0 (shipped in 7.63.0) + + Closes #3613 + +Marcel Raad (26 Feb 2019) +- AppVeyor: re-enable test 500 + + It's passing now. + + Closes https://github.com/curl/curl/pull/3615 + +- AppVeyor: remove redundant builds + + Remove the Visual Studio 2012 and 2013 builds as they add little value. + + Ref: https://github.com/curl/curl/pull/3606 + Closes https://github.com/curl/curl/pull/3614 + +Daniel Stenberg (25 Feb 2019) +- RELEASE-NOTES: synced + +- [Bernd Mueller brought this change] + + OpenSSL: add support for TLS ASYNC state + + Closes #3591 + +Jay Satiro (25 Feb 2019) +- [Michael Felt brought this change] + + acinclude: add additional libraries to check for LDAP support + + - Add an additional check for LDAP that also checks for OpenSSL since + on AIX those libraries may be required to link LDAP properly. + + Fixes https://github.com/curl/curl/issues/3595 + Closes https://github.com/curl/curl/pull/3596 + +- [georgeok brought this change] + + schannel: support CALG_ECDH_EPHEM algorithm + + Add support for Ephemeral elliptic curve Diffie-Hellman key exchange + algorithm option when selecting ciphers. This became available on the + Win10 SDK. + + Closes https://github.com/curl/curl/pull/3608 + +Daniel Stenberg (24 Feb 2019) +- multi: call multi_done on connect timeouts + + Failing to do so would make the CURLINFO_TOTAL_TIME timeout to not get + updated correctly and could end up getting reported to the application + completely wrong (way too small). + + Reported-by: accountantM on github + Fixes #3602 + Closes #3605 + +- examples: remove recursive calls to curl_multi_socket_action + + From within the timer callbacks. Recursive is problematic for several + reasons. They should still work, but this way the examples and the + documentation becomes simpler. I don't think we need to encourage + recursive calls. + + Discussed in #3537 + Closes #3601 + +Marcel Raad (23 Feb 2019) +- configure: remove CURL_CHECK_FUNC_FDOPEN call + + The macro itself has been removed in commit + 11974ac859c5d82def59e837e0db56fef7f6794e. + + Closes https://github.com/curl/curl/pull/3604 + +Daniel Stenberg (23 Feb 2019) +- wolfssl: stop custom-adding curves + + since wolfSSL PR https://github.com/wolfSSL/wolfssl/pull/717 (shipped in + wolfSSL 3.10.2 and later) it sends these curves by default already. + + Pointed-out-by: David Garske + + Closes #3599 + +- configure: remove the unused fdopen macro + + and the two remaining #ifdefs for it + + Closes #3600 + +Jay Satiro (22 Feb 2019) +- url: change conn shutdown order to unlink data as last step + + - Split off connection shutdown procedure from Curl_disconnect into new + function conn_shutdown. + + - Change the shutdown procedure to close the sockets before + disassociating the transfer. + + Prior to this change the sockets were closed after disassociating the + transfer so SOCKETFUNCTION wasn't called since the transfer was already + disassociated. That likely came about from recent work started in + Jan 2019 (#3442) to separate transfers from connections. + + Bug: https://curl.haxx.se/mail/lib-2019-02/0101.html + Reported-by: Pavel Löbl + + Closes https://github.com/curl/curl/issues/3597 + Closes https://github.com/curl/curl/pull/3598 + +Marcel Raad (22 Feb 2019) +- Fix strict-prototypes GCC warning + + As seen in the MinGW autobuilds. Caused by commit + f26bc29cfec0be84c67cf74065cf8e5e78fd68b7. + +Dan Fandrich (21 Feb 2019) +- tests: Fixed XML validation errors in some test files. + +Daniel Stenberg (20 Feb 2019) +- TODO: Allow SAN names in HTTP/2 server push + + Suggested-by: Nicolas Grekas + +- RELEASE-NOTES: synced + +- curl: remove MANUAL from -M output + + ... and remove it from the dist tarball. It has served its time, it + barely gets updated anymore and "everything curl" is now convering all + this document once tried to include, and does it more and better. + + In the compressed scenario, this removes ~15K data from the binary, + which is 25% of the -M output. + + It remains in the git repo for now for as long as the web site builds a + page using that as source. It renders poorly on the site (especially for + mobile users) so its not even good there. + + Closes #3587 + +- http2: verify :athority in push promise requests + + RFC 7540 says we should verify that the push is for an "authoritative" + server. We make sure of this by only allowing push with an :athority + header that matches the host that was asked for in the URL. + + Fixes #3577 + Reported-by: Nicolas Grekas + Bug: https://curl.haxx.se/mail/lib-2019-02/0057.html + Closes #3581 + +- singlesocket: fix the 'sincebefore' placement + + The variable wasn't properly reset within the loop and thus could remain + set for sockets that hadn't been set before and miss notifying the app. + + This is a follow-up to 4c35574 (shipped in curl 7.64.0) + + Reported-by: buzo-ffm on github + Detected-by: Jan Alexander Steffens + Fixes #3585 + Closes #3589 + +- connection: never reuse CONNECT_ONLY conections + + and make CONNECT_ONLY conections never reuse any existing ones either. + + Reported-by: Pavel Löbl + Bug: https://curl.haxx.se/mail/lib-2019-02/0064.html + Closes #3586 + +Patrick Monnerat (19 Feb 2019) +- cli tool: fix mime post with --disable-libcurl-option configure option + + Reported-by: Marcel Raad + Fixes #3576 + Closes #3583 + +Daniel Stenberg (19 Feb 2019) +- x509asn1: cleanup and unify code layout + + - rename 'n' to buflen in functions, and use size_t for them. Don't pass + in negative buffer lengths. + + - move most function comments to above the function starts like we use + to + + - remove several unnecessary typecasts (especially of NULL) + + Reviewed-by: Patrick Monnerat + Closes #3582 + +- curl_multi_remove_handle.3: use at any time, just not from within callbacks + + [ci skip] + +- http: make adding a blank header thread-safe + + Previously the function would edit the provided header in-place when a + semicolon is used to signify an empty header. This made it impossible to + use the same set of custom headers in multiple threads simultaneously. + + This approach now makes a local copy when it needs to edit the string. + + Reported-by: d912e3 on github + Fixes #3578 + Closes #3579 + +- unit1651: survive curl_easy_init() fails + +- [Frank Gevaerts brought this change] + + rand: Fix a mismatch between comments in source and header. + + Reported-by: Björn Stenberg + Closes #3584 + +Patrick Monnerat (18 Feb 2019) +- x509asn1: replace single char with an array + + Although safe in this context, using a single char as an array may + cause invalid accesses to adjacent memory locations. + + Detected by Coverity. + +Daniel Stenberg (18 Feb 2019) +- examples/http2-serverpush: add some sensible error checks + + To avoid NULL pointer dereferences etc in the case of problems. + + Closes #3580 + +Jay Satiro (18 Feb 2019) +- easy: fix win32 init to work without CURL_GLOBAL_WIN32 + + - Change the behavior of win32_init so that the required initialization + procedures are not affected by CURL_GLOBAL_WIN32 flag. + + libcurl via curl_global_init supports initializing for win32 with an + optional flag CURL_GLOBAL_WIN32, which if omitted was meant to stop + Winsock initialization. It did so internally by skipping win32_init() + when that flag was set. Since then win32_init() has been expanded to + include required initialization routines that are separate from + Winsock and therefore must be called in all cases. This commit fixes + it so that CURL_GLOBAL_WIN32 only controls the optional win32 + initialization (which is Winsock initialization, according to our doc). + + The only users affected by this change are those that don't pass + CURL_GLOBAL_WIN32 to curl_global_init. For them this commit removes the + risk of a potential crash. + + Ref: https://github.com/curl/curl/pull/3573 + + Fixes https://github.com/curl/curl/issues/3313 + Closes https://github.com/curl/curl/pull/3575 + +Daniel Gustafsson (17 Feb 2019) +- cookie: Add support for cookie prefixes + + The draft-ietf-httpbis-rfc6265bis-02 draft, specify a set of prefixes + and how they should affect cookie initialization, which has been + adopted by the major browsers. This adds support for the two prefixes + defined, __Host- and __Secure, and updates the testcase with the + supplied examples from the draft. + + Closes #3554 + Reviewed-by: Daniel Stenberg + +- mbedtls: release sessionid resources on error + + If mbedtls_ssl_get_session() fails, it may still have allocated + memory that needs to be freed to avoid leaking. Call the library + API function to release session resources on this errorpath as + well as on Curl_ssl_addsessionid() errors. + + Closes: #3574 + Reported-by: Michał Antoniak + Reviewed-by: Daniel Stenberg + +Patrick Monnerat (16 Feb 2019) +- cli tool: refactor encoding conversion sequence for switch case fallthrough. + +- version.c: silent scan-build even when librtmp is not enabled + +Daniel Stenberg (15 Feb 2019) +- RELEASE-NOTES: synced + +- Curl_now: figure out windows version in win32_init + + ... and avoid use of static variables that aren't thread safe. + + Fixes regression from e9ababd4f5a (present in the 7.64.0 release) + + Reported-by: Paul Groke + Fixes #3572 + Closes #3573 + +Marcel Raad (15 Feb 2019) +- unit1307: just fail without FTP support + + I missed to check this in with commit + 71786c0505926aaf7e9b2477b2fb7ee16a915ec6, which only disabled the test. + This fixes the actual linker error. + + Closes https://github.com/curl/curl/pull/3568 + +Daniel Stenberg (15 Feb 2019) +- travis: enable valgrind for the iconv tests too + + Closes #3571 + +- travis: add scan-build + + Closes #3564 + +- examples/sftpuploadresume: Value stored to 'result' is never read + + Detected by scan-build + +- examples/http2-upload: cleaned up + + Fix scan-build warnings, no globals, no silly handle scan. Also remove + handles from the multi before cleaning up. + +- examples/http2-download: cleaned up + + To avoid scan-build warnings and global variables. + +- examples/postinmemory: Potential leak of memory pointed to by 'chunk.memory' + + Detected by scan-build + +- examples/httpcustomheader: Value stored to 'res' is never read + + Detected by scan-build + +- examples: remove superfluous null-pointer checks + + in ftpget, ftpsget and sftpget, so that scan-build stops warning for + potential NULL pointer dereference below! + + Detected by scan-build + +- strip_trailing_dot: make sure NULL is never used for strlen + + scan-build warning: Null pointer passed as an argument to a 'nonnull' + parameter + +- [Jay Satiro brought this change] + + connection_check: restore original conn->data after the check + + - Save the original conn->data before it's changed to the specified + data transfer for the connection check and then restore it afterwards. + + This is a follow-up to 38d8e1b 2019-02-11. + + History: + + It was discovered a month ago that before checking whether to extract a + dead connection that that connection should be associated with a "live" + transfer for the check (ie original conn->data ignored and set to the + passed in data). A fix was landed in 54b201b which did that and also + cleared conn->data after the check. The original conn->data was not + restored, so presumably it was thought that a valid conn->data was no + longer needed. + + Several days later it was discovered that a valid conn->data was needed + after the check and follow-up fix was landed in bbae24c which partially + reverted the original fix and attempted to limit the scope of when + conn->data was changed to only when pruning dead connections. In that + case conn->data was not cleared and the original conn->data not + restored. + + A month later it was discovered that the original fix was somewhat + correct; a "live" transfer is needed for the check in all cases + because original conn->data could be null which could cause a bad deref + at arbitrary points in the check. A fix was landed in 38d8e1b which + expanded the scope to all cases. conn->data was not cleared and the + original conn->data not restored. + + A day later it was discovered that not restoring the original conn->data + may lead to busy loops in applications that use the event interface, and + given this observation it's a pretty safe assumption that there is some + code path that still needs the original conn->data. This commit is the + follow-up fix for that, it restores the original conn->data after the + connection check. + + Assisted-by: tholin@users.noreply.github.com + Reported-by: tholin@users.noreply.github.com + + Fixes https://github.com/curl/curl/issues/3542 + Closes #3559 + +- memdebug: bring back curl_mark_sclose + + Used by debug builds with NSS. + + Reverted from 05b100aee247bb + +Patrick Monnerat (14 Feb 2019) +- transfer.c: do not compute length of undefined hex buffer. + + On non-ascii platforms, the chunked hex header was measured for char code + conversion length, even for chunked trailers that do not have an hex header. + In addition, the efective length is already known: use it. + Since the hex length can be zero, only convert if needed. + + Reported by valgrind. + +Daniel Stenberg (14 Feb 2019) +- KNOWN_BUGS: Cannot compile against a static build of OpenLDAP + + Closes #2367 + +Patrick Monnerat (14 Feb 2019) +- x509asn1: "Dereference of null pointer" + + Detected by scan-build (false positive). + +Daniel Stenberg (14 Feb 2019) +- configure: show features as well in the final summary + + Closes #3569 + +- KNOWN_BUGS: curl compiled on OSX 10.13 failed to run on OSX 10.10 + + Closes #2905 + +- KNOWN_BUGS: Deflate error after all content was received + + Closes #2719 + +- gssapi: fix deprecated header warnings + + Heimdal includes on FreeBSD spewed out lots of them. Less so now. + + Closes #3566 + +- TODO: Upgrade to websockets + + Closes #3523 + +- TODO: cmake test suite improvements + + Closes #3109 + +Patrick Monnerat (13 Feb 2019) +- curl: "Dereference of null pointer" + + Rephrase to satisfy scan-build. + +Marcel Raad (13 Feb 2019) +- unit1307: require FTP support + + This test doesn't link without FTP support after + fc7ab4835b5fd09d0a6f57000633bb6bb6edfda1, which made Curl_fnmatch + unavailable without FTP support. + + Closes https://github.com/curl/curl/pull/3565 + +Daniel Stenberg (13 Feb 2019) +- TODO: TFO support on Windows + + Nobody works on this now. + + Closes #3378 + +- multi: Dereference of null pointer + + Mostly a false positive, but this makes the code easier to read anyway. + + Detected by scan-build. + + Closes #3563 + +- urlglob: Argument with 'nonnull' attribute passed null + + Detected by scan-build. + +Jay Satiro (12 Feb 2019) +- schannel: restore some debug output but only for debug builds + + Follow-up to 84c10dc from earlier today which wrapped a lot of the noisy + debug output in DEBUGF but omitted a few lines. + + Ref: https://github.com/curl/curl/commit/84c10dc#r32292900 + +- examples/crawler: Fix the Accept-Encoding setting + + - Pass an empty string to CURLOPT_ACCEPT_ENCODING to use the default + supported encodings. + + Prior to this change the specific encodings of gzip and deflate were set + but there's no guarantee they'd be supported by the user's libcurl. + +Daniel Stenberg (12 Feb 2019) +- mime: put the boundary buffer into the curl_mime struct + + ... instead of allocating it separately and point to it. It is + fixed-size and always used for each part. + + Closes #3561 + +- schannel: be quiet + + Convert numerous infof() calls into debug-build only messages since they + are annoyingly verbose for regular applications. Removed a few. + + Bug: https://curl.haxx.se/mail/lib-2019-02/0027.html + Reported-by: Volker Schmid + Closes #3552 + +- [Romain Geissler brought this change] + + Curl_resolv: fix a gcc -Werror=maybe-uninitialized warning + + Closes #3562 + +- http2: multi_connchanged() moved from multi.c, only used for h2 + + Closes #3557 + +- curl: "Function call argument is an uninitialized value" + + Follow-up to cac0e4a6ad14b42471eb + + Detected by scan-build + Closes #3560 + +- pretransfer: don't strlen() POSTFIELDS set for GET requests + + ... since that data won't be used in the request anyway. + + Fixes #3548 + Reported-by: Renaud Allard + Close #3549 + +- multi: remove verbose "Expire in" ... messages + + Reported-by: James Brown + Bug: https://curl.haxx.se/mail/archive-2019-02/0013.html + Closes #3558 + +- mbedtls: make it build even if MBEDTLS_VERSION_C isn't set + + Reported-by: MAntoniak on github + Fixes #3553 + Closes #3556 + +Daniel Gustafsson (12 Feb 2019) +- non-ascii.c: fix typos in comments + + Fix two occurrences of s/convers/converts/ spotted while reading code. + +Daniel Stenberg (12 Feb 2019) +- fnmatch: disable if FTP is disabled + + Closes #3551 + +- curl_path: only enabled for SSH builds + +- [Frank Gevaerts brought this change] + + tests: add stderr comparison to the test suite + + The code is more or less copied from the stdout comparison code, maybe + some better reuse is possible. + + test 1457 is adjusted to make the output actually match (by using --silent) + test 506 used without actually needing it, so that block is removed + + Closes #3536 + +Patrick Monnerat (11 Feb 2019) +- cli tool: do not use mime.h private structures. + + Option -F generates an intermediate representation of the mime structure + that is used later to create the libcurl mime structure and generate + the --libcurl statements. + + Reported-by: Daniel Stenberg + Fixes #3532 + Closes #3546 + +Daniel Stenberg (11 Feb 2019) +- curlver: bump to 7.64.1-dev + +- RELEASE-NOTES: synced + + and bump the version in progress to 7.64.1. If we merge any "change" + before the cut-off date, we update again. + +Daniel Gustafsson (11 Feb 2019) +- curl: follow-up to 3f16990ec84 + + Commit 3f16990ec84cc4b followed-up a bug in b49652ac66cc0 but was + inadvertently introducing a new bug in the ternary expression. + + Close #3555 + Reviewed-by: Daniel Stenberg + +- dns: release sharelock as soon as possible + + There is no benefit to holding the data sharelock when freeing the + addrinfo in case it fails, so ensure releaseing it as soon as we can + rather than holding on to it. This also aligns the code with other + consumers of sharelocks. + + Closes #3516 + Reviewed-by: Daniel Stenberg + +Daniel Stenberg (11 Feb 2019) +- curl: follow-up to b49652ac66cc0 + + On FreeBSD, return non-zero on error otherwise zero. + + Reported-by: Marcel Raad + +- multi: (void)-prefix when ignoring return values + + ... and added braces to two function calls which fixes warnings if they + are replace by empty macros at build-time. + +- curl: fix FreeBSD compiler warning in the --xattr code + + Closes #3550 + +- connection_check: set ->data to the transfer doing the check + + The http2 code for connection checking needs a transfer to use. Make + sure a working one is set before handler->connection_check() is called. + + Reported-by: jnbr on github + Fixes #3541 + Closes #3547 + +- hostip: make create_hostcache_id avoid alloc + free + + Closes #3544 + +- scripts/singleuse: script to use to track single-use functions + + That is functions that are declared global but are not used from outside + of the file in which it is declared. Such functions should be made + static or even at times be removed. + + It also verifies that all used curl_ prefixed functions are "blessed" + + Closes #3538 + +- cleanup: make local functions static + + urlapi: turn three local-only functions into statics + + conncache: make conncache_find_first_connection static + + multi: make detach_connnection static + + connect: make getaddressinfo static + + curl_ntlm_core: make hmac_md5 static + + http2: make two functions static + + http: make http_setup_conn static + + connect: make tcpnodelay static + + tests: make UNITTEST a thing to mark functions with, so they can be static for + normal builds and non-static for unit test builds + + ... and mark Curl_shuffle_addr accordingly. + + url: make up_free static + + setopt: make vsetopt static + + curl_endian: make write32_le static + + rtsp: make rtsp_connisdead static + + warnless: remove unused functions + + memdebug: remove one unused function, made another static + +Dan Fandrich (10 Feb 2019) +- cirrus: Added FreeBSD builds using Cirrus CI. + + The build logs will be at https://cirrus-ci.com/github/curl/curl + + Some tests are currently failing and so disabled for now. The SSH server + isn't starting for the SSH tests due to unsupported options used in its + config file. The DICT server also is failing on startup. + +Daniel Stenberg (9 Feb 2019) +- url/idnconvert: remove scan for <= 32 ascii values + + The check was added back in fa939220df before the URL parser would catch + these problems and therefore these will never trigger now. + + Closes #3539 + +- urlapi: reduce variable scope, remove unreachable 'break' + + Both nits pointed out by codacy.com + + Closes #3540 + +Alessandro Ghedini (7 Feb 2019) +- zsh.pl: escape ':' character + + ':' is interpreted as separator by zsh, so if used as part of the argument + or option's description it needs to be escaped. + + The problem can be reproduced as follows: + + % curl --reso + % curl -E + + Bug: https://bugs.debian.org/921452 + +- zsh.pl: update regex to better match curl -h output + + The current regex fails to match '<...>' arguments properly (e.g. those + with spaces in them), which causes an completion script with wrong + descriptions for some options. + + Here's a diff of the generated completion script, comparing the previous + version to the one with this fix: + + --- /usr/share/zsh/vendor-completions/_curl 2019-01-15 20:47:40.000000000 +0000 + +++ _curl 2019-02-05 20:57:29.453349040 +0000 + @@ -9,48 +9,48 @@ + + _arguments -C -S \ + --happy-eyeballs-timeout-ms'[How long to wait in milliseconds for IPv6 before trying IPv4]':'' \ + + --resolve'[Resolve the host+port to this address]':'' \ + {-c,--cookie-jar}'[Write cookies to after operation]':'':_files \ + {-D,--dump-header}'[Write the received headers to ]':'':_files \ + {-y,--speed-time}'[Trigger '\''speed-limit'\'' abort after this time]':'' \ + --proxy-cacert'[CA certificate to verify peer against for proxy]':'':_files \ + - --tls13-ciphers'[of TLS 1.3 ciphersuites> TLS 1.3 cipher suites to use]':'' \ + {-E,--cert}'[Client certificate file and password]':'' \ + --libcurl'[Dump libcurl equivalent code of this command line]':'':_files \ + --proxy-capath'[CA directory to verify peer against for proxy]':'':_files \ + - --proxy-negotiate'[HTTP Negotiate (SPNEGO) authentication on the proxy]':'Use' \ + --proxy-pinnedpubkey'[FILE/HASHES public key to verify proxy with]':'' \ + --crlfile'[Get a CRL list in PEM format from the given file]':'':_files \ + - --proxy-insecure'[HTTPS proxy connections without verifying the proxy]':'Do' \ + - --proxy-ssl-allow-beast'[security flaw for interop for HTTPS proxy]':'Allow' \ + + --proxy-negotiate'[Use HTTP Negotiate (SPNEGO) authentication on the proxy]' \ + --abstract-unix-socket'[Connect via abstract Unix domain socket]':'' \ + --pinnedpubkey'[FILE/HASHES Public key to verify peer against]':'' \ + + --proxy-insecure'[Do HTTPS proxy connections without verifying the proxy]' \ + --proxy-pass'[Pass phrase for the private key for HTTPS proxy]':'' \ + + --proxy-ssl-allow-beast'[Allow security flaw for interop for HTTPS proxy]' \ + {-p,--proxytunnel}'[Operate through an HTTP proxy tunnel (using CONNECT)]' \ + --socks5-hostname'[SOCKS5 proxy, pass host name to proxy]':'' \ + --proto-default'[Use PROTOCOL for any URL missing a scheme]':'' \ + - --proxy-tls13-ciphers'[list> TLS 1.3 proxy cipher suites]':'' \ + --socks5-gssapi-service'[SOCKS5 proxy service name for GSS-API]':'' \ + --ftp-alternative-to-user'[String to replace USER \[name\]]':'' \ + - --ftp-ssl-control'[SSL/TLS for FTP login, clear for transfer]':'Require' \ + {-T,--upload-file}'[Transfer local FILE to destination]':'':_files \ + --local-port'[Force use of RANGE for local port numbers]':'' \ + --proxy-tlsauthtype'[TLS authentication type for HTTPS proxy]':'' \ + {-R,--remote-time}'[Set the remote file'\''s time on the local output]' \ + - --retry-connrefused'[on connection refused (use with --retry)]':'Retry' \ + - --suppress-connect-headers'[proxy CONNECT response headers]':'Suppress' \ + - {-j,--junk-session-cookies}'[session cookies read from file]':'Ignore' \ + - --location-trusted'[--location, and send auth to other hosts]':'Like' \ + + --ftp-ssl-control'[Require SSL/TLS for FTP login, clear for transfer]' \ + --proxy-cert-type'[Client certificate type for HTTPS proxy]':'' \ + {-O,--remote-name}'[Write output to a file named as the remote file]' \ + + --retry-connrefused'[Retry on connection refused (use with --retry)]' \ + + --suppress-connect-headers'[Suppress proxy CONNECT response headers]' \ + --trace-ascii'[Like --trace, but without hex output]':'':_files \ + --connect-timeout'[Maximum time allowed for connection]':'' \ + --expect100-timeout'[How long to wait for 100-continue]':'' \ + {-g,--globoff}'[Disable URL sequences and ranges using {} and \[\]]' \ + + {-j,--junk-session-cookies}'[Ignore session cookies read from file]' \ + {-m,--max-time}'[Maximum time allowed for the transfer]':'' \ + --dns-ipv4-addr'[IPv4 address to use for DNS requests]':'
' \ + --dns-ipv6-addr'[IPv6 address to use for DNS requests]':'
' \ + - --ignore-content-length'[the size of the remote resource]':'Ignore' \ + {-k,--insecure}'[Allow insecure server connections when using SSL]' \ + + --location-trusted'[Like --location, and send auth to other hosts]' \ + --mail-auth'[Originator address of the original email]':'
' \ + --noproxy'[List of hosts which do not use proxy]':'' \ + --proto-redir'[Enable/disable PROTOCOLS on redirect]':'' \ + @@ -62,18 +62,19 @@ + --socks5-basic'[Enable username/password auth for SOCKS5 proxies]' \ + --cacert'[CA certificate to verify peer against]':'':_files \ + {-H,--header}'[Pass custom header(s) to server]':'
' \ + + --ignore-content-length'[Ignore the size of the remote resource]' \ + {-i,--include}'[Include protocol response headers in the output]' \ + --proxy-header'[Pass custom header(s) to proxy]':'
' \ + --unix-socket'[Connect through this Unix domain socket]':'' \ + {-w,--write-out}'[Use output FORMAT after completion]':'' \ + - --http2-prior-knowledge'[HTTP 2 without HTTP/1.1 Upgrade]':'Use' \ + {-o,--output}'[Write to file instead of stdout]':'':_files \ + - {-J,--remote-header-name}'[the header-provided filename]':'Use' \ + + --preproxy'[\[protocol://\]host\[:port\] Use this proxy first]' \ + --socks4a'[SOCKS4a proxy on given host + port]':'' \ + {-Y,--speed-limit}'[Stop transfers slower than this]':'' \ + {-z,--time-cond}'[Transfer based on a time condition]':'