├── .all-contributorsrc ├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ ├── build_pull_request.yml │ ├── build_push.yml │ ├── msvc-analysis.yml │ ├── sonarcloud.yml │ └── test_cpp_versions.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── appveyor.yml ├── cmake ├── CMakeCompiler.cmake ├── CMakePlatforms.cmake ├── CMakeUtils.cmake ├── Toolchain-Android.cmake └── Toolchain-iOS.cmake ├── external └── README.md ├── includes └── sjson │ ├── error.h │ ├── fwd.h │ ├── impl │ ├── bit_cast.impl.h │ └── cstdlib.impl.h │ ├── parser.h │ ├── parser_error.h │ ├── parser_state.h │ ├── string_view.h │ ├── version.h │ └── writer.h ├── make.py ├── sonar-project.properties ├── tests ├── CMakeLists.txt ├── main_android │ ├── CMakeLists.txt │ ├── app │ │ ├── build.gradle.in │ │ ├── proguard-rules.pro │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── cpp │ │ │ ├── CMakeLists.txt │ │ │ └── main.cpp │ │ │ ├── java │ │ │ └── com │ │ │ │ └── sjson │ │ │ │ └── unit_tests │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ └── values │ │ │ └── strings.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── main_emscripten │ ├── CMakeLists.txt │ └── main.cpp ├── main_generic │ ├── CMakeLists.txt │ └── main.cpp ├── main_ios │ ├── CMakeLists.txt │ └── main.cpp ├── sources │ ├── catch2.impl.h │ ├── test_header_fwd.cpp │ ├── test_parser.cpp │ ├── test_string_view.cpp │ └── test_writer.cpp └── validate_includes │ ├── CMakeLists.txt │ ├── dummy.cpp │ └── single_include.cpp.in └── tools ├── appveyor_ci.bat ├── release_scripts └── test_everything.py ├── setup_linux_compiler.sh ├── setup_osx_compiler.sh └── vs_visualizers └── sjson-cpp.natvis /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "CodyDWJones", 10 | "name": "CodyDWJones", 11 | "avatar_url": "https://avatars.githubusercontent.com/u/28773740?v=4", 12 | "profile": "https://github.com/CodyDWJones", 13 | "contributions": [ 14 | "code", 15 | "maintenance" 16 | ] 17 | }, 18 | { 19 | "login": "janisozaur", 20 | "name": "Michał Janiszewski", 21 | "avatar_url": "https://avatars.githubusercontent.com/u/550290?v=4", 22 | "profile": "https://github.com/janisozaur", 23 | "contributions": [ 24 | "code", 25 | "maintenance" 26 | ] 27 | }, 28 | { 29 | "login": "tirpidz", 30 | "name": "Martin Turcotte", 31 | "avatar_url": "https://avatars.githubusercontent.com/u/9991876?v=4", 32 | "profile": "https://github.com/tirpidz", 33 | "contributions": [ 34 | "maintenance" 35 | ] 36 | }, 37 | { 38 | "login": "Meradrin", 39 | "name": "Meradrin", 40 | "avatar_url": "https://avatars.githubusercontent.com/u/7066278?v=4", 41 | "profile": "https://github.com/Meradrin", 42 | "contributions": [ 43 | "bug" 44 | ] 45 | } 46 | ], 47 | "contributorsPerLine": 7, 48 | "projectName": "sjson-cpp", 49 | "projectOwner": "nfrechette", 50 | "repoType": "github", 51 | "repoHost": "https://github.com", 52 | "skipCi": true 53 | } 54 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | # Matches multiple files with brace expansion notation 8 | [*.{cpp,h,py,cmake,txt,java}] 9 | charset = utf-8 10 | indent_style = tab 11 | indent_size = 4 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | 17 | [*.{gradle,gradle.in}] 18 | indent_style = space 19 | indent_size = 4 20 | trim_trailing_whitespace = true 21 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | #github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | #patreon: # Replace with a single Patreon username 5 | #open_collective: # Replace with a single Open Collective username 6 | #ko_fi: # Replace with a single Ko-fi username 7 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | #liberapay: # Replace with a single Liberapay username 10 | #issuehunt: # Replace with a single IssueHunt username 11 | #otechie: # Replace with a single Otechie username 12 | #custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | 14 | github: [nfrechette] 15 | -------------------------------------------------------------------------------- /.github/workflows/build_pull_request.yml: -------------------------------------------------------------------------------- 1 | name: build pull request 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '**' 7 | paths-ignore: 8 | - '**/*.md' 9 | 10 | jobs: 11 | linux-xenial: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | compiler: [clang4] 16 | steps: 17 | - name: Git checkout 18 | uses: actions/checkout@v4 19 | with: 20 | submodules: 'recursive' 21 | - name: Building (debug-x86) 22 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-xenial:v1 23 | with: 24 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x86 -build' 25 | - name: Running unit tests (debug-x86) 26 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-xenial:v1 27 | with: 28 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x86 -unit_test' 29 | - name: Clean 30 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-xenial:v1 31 | with: 32 | args: 'python3 make.py -clean_only' 33 | - name: Building (debug-x64) 34 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-xenial:v1 35 | with: 36 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x64 -build' 37 | - name: Running unit tests (debug-x64) 38 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-xenial:v1 39 | with: 40 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x64 -unit_test' 41 | - name: Clean 42 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-xenial:v1 43 | with: 44 | args: 'python3 make.py -clean_only' 45 | - name: Building (release-x86) 46 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-xenial:v1 47 | with: 48 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x86 -build' 49 | - name: Running unit tests (release-x86) 50 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-xenial:v1 51 | with: 52 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x86 -unit_test' 53 | - name: Clean 54 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-xenial:v1 55 | with: 56 | args: 'python3 make.py -clean_only' 57 | - name: Building (release-x64) 58 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-xenial:v1 59 | with: 60 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x64 -build' 61 | - name: Running unit tests (release-x64) 62 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-xenial:v1 63 | with: 64 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x64 -unit_test' 65 | 66 | linux-bionic: 67 | runs-on: ubuntu-latest 68 | strategy: 69 | matrix: 70 | compiler: [gcc5, gcc6, gcc7, gcc8, clang5, clang6, clang7, clang8, clang9, clang10] 71 | steps: 72 | - name: Git checkout 73 | uses: actions/checkout@v4 74 | with: 75 | submodules: 'recursive' 76 | - name: Building (debug-x86) 77 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-bionic:v1 78 | with: 79 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x86 -build' 80 | - name: Running unit tests (debug-x86) 81 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-bionic:v1 82 | with: 83 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x86 -unit_test' 84 | - name: Clean 85 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-bionic:v1 86 | with: 87 | args: 'python3 make.py -clean_only' 88 | - name: Building (debug-x64) 89 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-bionic:v1 90 | with: 91 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x64 -build' 92 | - name: Running unit tests (debug-x64) 93 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-bionic:v1 94 | with: 95 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x64 -unit_test' 96 | - name: Clean 97 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-bionic:v1 98 | with: 99 | args: 'python3 make.py -clean_only' 100 | - name: Building (release-x86) 101 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-bionic:v1 102 | with: 103 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x86 -build' 104 | - name: Running unit tests (release-x86) 105 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-bionic:v1 106 | with: 107 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x86 -unit_test' 108 | - name: Clean 109 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-bionic:v1 110 | with: 111 | args: 'python3 make.py -clean_only' 112 | - name: Building (release-x64) 113 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-bionic:v1 114 | with: 115 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x64 -build' 116 | - name: Running unit tests (release-x64) 117 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-bionic:v1 118 | with: 119 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x64 -unit_test' 120 | 121 | linux-focal: 122 | runs-on: ubuntu-latest 123 | strategy: 124 | matrix: 125 | compiler: [gcc9, gcc10, gcc11, clang11, clang12, clang13, clang14] 126 | steps: 127 | - name: Git checkout 128 | uses: actions/checkout@v4 129 | with: 130 | submodules: 'recursive' 131 | - name: Building (debug-x86) 132 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-focal:v1 133 | with: 134 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x86 -build' 135 | - name: Running unit tests (debug-x86) 136 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-focal:v1 137 | with: 138 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x86 -unit_test' 139 | - name: Clean 140 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-focal:v1 141 | with: 142 | args: 'python3 make.py -clean_only' 143 | - name: Building (debug-x64) 144 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-focal:v1 145 | with: 146 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x64 -build' 147 | - name: Running unit tests (debug-x64) 148 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-focal:v1 149 | with: 150 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x64 -unit_test' 151 | - name: Clean 152 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-focal:v1 153 | with: 154 | args: 'python3 make.py -clean_only' 155 | - name: Building (release-x86) 156 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-focal:v1 157 | with: 158 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x86 -build' 159 | - name: Running unit tests (release-x86) 160 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-focal:v1 161 | with: 162 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x86 -unit_test' 163 | - name: Clean 164 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-focal:v1 165 | with: 166 | args: 'python3 make.py -clean_only' 167 | - name: Building (release-x64) 168 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-focal:v1 169 | with: 170 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x64 -build' 171 | - name: Running unit tests (release-x64) 172 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-focal:v1 173 | with: 174 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x64 -unit_test' 175 | 176 | linux-lunar: 177 | runs-on: ubuntu-latest 178 | strategy: 179 | matrix: 180 | compiler: [gcc12, gcc13, clang15, clang16, clang17, clang18] 181 | steps: 182 | - name: Git checkout 183 | uses: actions/checkout@v4 184 | with: 185 | submodules: 'recursive' 186 | - name: Building (debug-x86) 187 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 188 | with: 189 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x86 -build' 190 | - name: Running unit tests (debug-x86) 191 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 192 | with: 193 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x86 -unit_test' 194 | - name: Clean 195 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 196 | with: 197 | args: 'python3 make.py -clean_only' 198 | - name: Building (debug-x64) 199 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 200 | with: 201 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x64 -build' 202 | - name: Running unit tests (debug-x64) 203 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 204 | with: 205 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x64 -unit_test' 206 | - name: Clean 207 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 208 | with: 209 | args: 'python3 make.py -clean_only' 210 | - name: Building (release-x86) 211 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 212 | with: 213 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x86 -build' 214 | - name: Running unit tests (release-x86) 215 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 216 | with: 217 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x86 -unit_test' 218 | - name: Clean 219 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 220 | with: 221 | args: 'python3 make.py -clean_only' 222 | - name: Building (release-x64) 223 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 224 | with: 225 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x64 -build' 226 | - name: Running unit tests (release-x64) 227 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 228 | with: 229 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x64 -unit_test' 230 | 231 | osx-12: 232 | runs-on: macos-12 233 | strategy: 234 | matrix: 235 | compiler: [xcode13] 236 | steps: 237 | - name: Git checkout 238 | uses: actions/checkout@v4 239 | with: 240 | submodules: 'recursive' 241 | - name: Setup ${{ matrix.compiler }} compiler 242 | run: ./tools/setup_osx_compiler.sh ${{ matrix.compiler }} 243 | - name: Building (debug-x64) 244 | run: python3 make.py -ci -compiler osx -config Debug -cpu x64 -build 245 | - name: Running unit tests (debug-x64) 246 | run: python3 make.py -ci -compiler osx -config Debug -cpu x64 -unit_test 247 | - name: Clean 248 | run: python3 make.py -clean_only 249 | - name: Building (release-x64) 250 | run: python3 make.py -ci -compiler osx -config Release -cpu x64 -build 251 | - name: Running unit tests (release-x64) 252 | run: python3 make.py -ci -compiler osx -config Release -cpu x64 -unit_test 253 | - name: Clean 254 | run: python3 make.py -clean_only 255 | - name: Building for iOS (debug-arm64) 256 | run: python3 make.py -ci -compiler ios -config Debug -build 257 | - name: Clean 258 | run: python3 make.py -clean_only 259 | - name: Building for iOS (release-arm64) 260 | run: python3 make.py -ci -compiler ios -config Release -build 261 | 262 | osx-14: 263 | runs-on: macos-14 264 | strategy: 265 | matrix: 266 | compiler: [xcode14, xcode15] 267 | steps: 268 | - name: Git checkout 269 | uses: actions/checkout@v4 270 | with: 271 | submodules: 'recursive' 272 | - name: Setup ${{ matrix.compiler }} compiler 273 | run: ./tools/setup_osx_compiler.sh ${{ matrix.compiler }} 274 | - name: Building (debug-arm64) 275 | run: python3 make.py -ci -compiler osx -config Debug -build 276 | - name: Running unit tests (debug-arm64) 277 | run: python3 make.py -ci -compiler osx -config Debug -unit_test 278 | - name: Clean 279 | run: python3 make.py -clean_only 280 | - name: Building (release-arm64) 281 | run: python3 make.py -ci -compiler osx -config Release -build 282 | - name: Running unit tests (release-arm64) 283 | run: python3 make.py -ci -compiler osx -config Release -unit_test 284 | - name: Clean 285 | run: python3 make.py -clean_only 286 | - name: Building for iOS (debug-arm64) 287 | run: python3 make.py -ci -compiler ios -config Debug -build 288 | - name: Clean 289 | run: python3 make.py -clean_only 290 | - name: Building for iOS (release-arm64) 291 | run: python3 make.py -ci -compiler ios -config Release -build 292 | 293 | emscripten: 294 | runs-on: ubuntu-latest 295 | steps: 296 | - name: Git checkout 297 | uses: actions/checkout@v4 298 | with: 299 | submodules: 'recursive' 300 | - name: Building (debug) 301 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-linux-emscripten:v1 302 | with: 303 | args: 'python3 make.py -ci -compiler emscripten -config debug -build' 304 | - name: Running unit tests (debug) 305 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-linux-emscripten:v1 306 | with: 307 | args: 'python3 make.py -ci -compiler emscripten -config debug -unit_test' 308 | - name: Clean 309 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-linux-emscripten:v1 310 | with: 311 | args: 'python3 make.py -clean_only' 312 | - name: Building (release) 313 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-linux-emscripten:v1 314 | with: 315 | args: 'python3 make.py -ci -compiler emscripten -config release -build' 316 | - name: Running unit tests (release) 317 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-linux-emscripten:v1 318 | with: 319 | args: 'python3 make.py -ci -compiler emscripten -config release -unit_test' 320 | 321 | vs2019: 322 | runs-on: windows-2019 323 | steps: 324 | - name: Git checkout 325 | uses: actions/checkout@v4 326 | with: 327 | submodules: 'recursive' 328 | - name: Building (debug-x64) 329 | run: python3 make.py -ci -compiler vs2019 -config Debug -cpu x64 -build 330 | - name: Running unit tests (debug-x64) 331 | run: python3 make.py -ci -compiler vs2019 -config Debug -cpu x64 -unit_test 332 | - name: Clean 333 | run: python3 make.py -clean_only 334 | - name: Building (release-x64) 335 | run: python3 make.py -ci -compiler vs2019 -config Release -cpu x64 -build 336 | - name: Running unit tests (release-x64) 337 | run: python3 make.py -ci -compiler vs2019 -config Release -cpu x64 -unit_test 338 | - name: Clean 339 | run: python3 make.py -clean_only 340 | - name: Building (debug-arm64) 341 | run: python3 make.py -ci -compiler vs2019 -config Debug -cpu arm64 -build 342 | - name: Clean 343 | run: python3 make.py -clean_only 344 | - name: Building (release-arm64) 345 | run: python3 make.py -ci -compiler vs2019 -config Release -cpu arm64 -build 346 | - name: Clean 347 | run: python3 make.py -clean_only 348 | - name: Building (debug-x64) with Clang 349 | run: python3 make.py -ci -compiler vs2019-clang -config Debug -cpu x64 -build 350 | - name: Running unit tests (debug-x64) with Clang 351 | run: python3 make.py -ci -compiler vs2019-clang -config Debug -cpu x64 -unit_test 352 | - name: Clean 353 | run: python3 make.py -clean_only 354 | - name: Building (release-x64) with Clang 355 | run: python3 make.py -ci -compiler vs2019-clang -config Release -cpu x64 -build 356 | - name: Running unit tests (release-x64) with Clang 357 | run: python3 make.py -ci -compiler vs2019-clang -config Release -cpu x64 -unit_test 358 | 359 | vs2022: 360 | runs-on: windows-2022 361 | steps: 362 | - name: Git checkout 363 | uses: actions/checkout@v4 364 | with: 365 | submodules: 'recursive' 366 | - name: Building (debug-x64) 367 | run: python3 make.py -ci -compiler vs2022 -config Debug -cpu x64 -build 368 | - name: Running unit tests (debug-x64) 369 | run: python3 make.py -ci -compiler vs2022 -config Debug -cpu x64 -unit_test 370 | - name: Clean 371 | run: python3 make.py -clean_only 372 | - name: Building (release-x64) 373 | run: python3 make.py -ci -compiler vs2022 -config Release -cpu x64 -build 374 | - name: Running unit tests (release-x64) 375 | run: python3 make.py -ci -compiler vs2022 -config Release -cpu x64 -unit_test 376 | - name: Clean 377 | run: python3 make.py -clean_only 378 | - name: Building (debug-arm64) 379 | run: python3 make.py -ci -compiler vs2022 -config Debug -cpu arm64 -build 380 | - name: Clean 381 | run: python3 make.py -clean_only 382 | - name: Building (release-arm64) 383 | run: python3 make.py -ci -compiler vs2022 -config Release -cpu arm64 -build 384 | - name: Clean 385 | run: python3 make.py -clean_only 386 | - name: Building (debug-x64) with Clang 387 | run: python3 make.py -ci -compiler vs2022-clang -config Debug -cpu x64 -build 388 | - name: Running unit tests (debug-x64) with Clang 389 | run: python3 make.py -ci -compiler vs2022-clang -config Debug -cpu x64 -unit_test 390 | - name: Clean 391 | run: python3 make.py -clean_only 392 | - name: Building (release-x64) with Clang 393 | run: python3 make.py -ci -compiler vs2022-clang -config Release -cpu x64 -build 394 | - name: Running unit tests (release-x64) with Clang 395 | run: python3 make.py -ci -compiler vs2022-clang -config Release -cpu x64 -unit_test 396 | -------------------------------------------------------------------------------- /.github/workflows/build_push.yml: -------------------------------------------------------------------------------- 1 | name: build push 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | paths-ignore: 8 | - '**/*.md' 9 | 10 | jobs: 11 | linux: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | compiler: [gcc13, clang18] 16 | steps: 17 | - name: Git checkout 18 | uses: actions/checkout@v4 19 | with: 20 | submodules: 'recursive' 21 | - name: Building (debug-x86) 22 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 23 | with: 24 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x86 -build' 25 | - name: Running unit tests (debug-x86) 26 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 27 | with: 28 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x86 -unit_test' 29 | - name: Clean 30 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 31 | with: 32 | args: 'python3 make.py -clean_only' 33 | - name: Building (debug-x64) 34 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 35 | with: 36 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x64 -build' 37 | - name: Running unit tests (debug-x64) 38 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 39 | with: 40 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Debug -cpu x64 -unit_test' 41 | - name: Clean 42 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 43 | with: 44 | args: 'python3 make.py -clean_only' 45 | - name: Building (release-x86) 46 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 47 | with: 48 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x86 -build' 49 | - name: Running unit tests (release-x86) 50 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 51 | with: 52 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x86 -unit_test' 53 | - name: Clean 54 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 55 | with: 56 | args: 'python3 make.py -clean_only' 57 | - name: Building (release-x64) 58 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 59 | with: 60 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x64 -build' 61 | - name: Running unit tests (release-x64) 62 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 63 | with: 64 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x64 -unit_test' 65 | 66 | osx-14: 67 | runs-on: macos-14 68 | strategy: 69 | matrix: 70 | compiler: [xcode15] 71 | steps: 72 | - name: Git checkout 73 | uses: actions/checkout@v4 74 | with: 75 | submodules: 'recursive' 76 | - name: Setup ${{ matrix.compiler }} compiler 77 | run: ./tools/setup_osx_compiler.sh ${{ matrix.compiler }} 78 | - name: Building (debug-arm64) 79 | run: python3 make.py -ci -compiler osx -config Debug -build 80 | - name: Running unit tests (debug-arm64) 81 | run: python3 make.py -ci -compiler osx -config Debug -unit_test 82 | - name: Clean 83 | run: python3 make.py -clean_only 84 | - name: Building (release-arm64) 85 | run: python3 make.py -ci -compiler osx -config Release -build 86 | - name: Running unit tests (release-arm64) 87 | run: python3 make.py -ci -compiler osx -config Release -unit_test 88 | - name: Clean 89 | run: python3 make.py -clean_only 90 | - name: Building for iOS (debug-arm64) 91 | run: python3 make.py -ci -compiler ios -config Debug -build 92 | - name: Clean 93 | run: python3 make.py -clean_only 94 | - name: Building for iOS (release-arm64) 95 | run: python3 make.py -ci -compiler ios -config Release -build 96 | 97 | emscripten: 98 | runs-on: ubuntu-latest 99 | steps: 100 | - name: Git checkout 101 | uses: actions/checkout@v4 102 | with: 103 | submodules: 'recursive' 104 | - name: Building (debug) 105 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-linux-emscripten:v1 106 | with: 107 | args: 'python3 make.py -ci -compiler emscripten -config debug -build' 108 | - name: Running unit tests (debug) 109 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-linux-emscripten:v1 110 | with: 111 | args: 'python3 make.py -ci -compiler emscripten -config debug -unit_test' 112 | - name: Clean 113 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-linux-emscripten:v1 114 | with: 115 | args: 'python3 make.py -clean_only' 116 | - name: Building (release) 117 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-linux-emscripten:v1 118 | with: 119 | args: 'python3 make.py -ci -compiler emscripten -config release -build' 120 | - name: Running unit tests (release) 121 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-linux-emscripten:v1 122 | with: 123 | args: 'python3 make.py -ci -compiler emscripten -config release -unit_test' 124 | 125 | vs2022: 126 | runs-on: windows-2022 127 | steps: 128 | - name: Git checkout 129 | uses: actions/checkout@v4 130 | with: 131 | submodules: 'recursive' 132 | - name: Building (debug-x64) 133 | run: python3 make.py -ci -compiler vs2022 -config Debug -cpu x64 -build 134 | - name: Running unit tests (debug-x64) 135 | run: python3 make.py -ci -compiler vs2022 -config Debug -cpu x64 -unit_test 136 | - name: Clean 137 | run: python3 make.py -clean_only 138 | - name: Building (release-x64) 139 | run: python3 make.py -ci -compiler vs2022 -config Release -cpu x64 -build 140 | - name: Running unit tests (release-x64) 141 | run: python3 make.py -ci -compiler vs2022 -config Release -cpu x64 -unit_test 142 | - name: Clean 143 | run: python3 make.py -clean_only 144 | - name: Building (debug-arm64) 145 | run: python3 make.py -ci -compiler vs2022 -config Debug -cpu arm64 -build 146 | - name: Clean 147 | run: python3 make.py -clean_only 148 | - name: Building (release-arm64) 149 | run: python3 make.py -ci -compiler vs2022 -config Release -cpu arm64 -build 150 | - name: Clean 151 | run: python3 make.py -clean_only 152 | - name: Building (debug-x64) with Clang 153 | run: python3 make.py -ci -compiler vs2022-clang -config Debug -cpu x64 -build 154 | - name: Running unit tests (debug-x64) with Clang 155 | run: python3 make.py -ci -compiler vs2022-clang -config Debug -cpu x64 -unit_test 156 | - name: Clean 157 | run: python3 make.py -clean_only 158 | - name: Building (release-x64) with Clang 159 | run: python3 make.py -ci -compiler vs2022-clang -config Release -cpu x64 -build 160 | - name: Running unit tests (release-x64) with Clang 161 | run: python3 make.py -ci -compiler vs2022-clang -config Release -cpu x64 -unit_test 162 | -------------------------------------------------------------------------------- /.github/workflows/msvc-analysis.yml: -------------------------------------------------------------------------------- 1 | name: msvc analysis 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '**' 7 | paths-ignore: 8 | - '**/*.md' 9 | 10 | jobs: 11 | # We use the presence of the sonar token to detect if the PR comes from a fork 12 | condition-check: 13 | runs-on: ubuntu-latest 14 | outputs: 15 | ok: ${{ steps.check-secrets.outputs.ok }} 16 | steps: 17 | - name: Secret guard 18 | id: check-secrets 19 | run: | 20 | if [ ! -z "${{ secrets.SONAR_TOKEN }}" ]; then 21 | echo "ok=true" >> $GITHUB_OUTPUT 22 | fi 23 | vs2022: 24 | needs: 25 | - condition-check 26 | runs-on: windows-2022 27 | steps: 28 | - name: Git checkout 29 | uses: actions/checkout@v3 30 | with: 31 | submodules: 'recursive' 32 | - name: Generate solution (release-x64) 33 | run: python3 make.py -ci -compiler vs2022 -config Release -cpu x64 34 | - name: Run MSVC Code Analysis 35 | uses: microsoft/msvc-code-analysis-action@v0.1.1 36 | id: run-analysis 37 | with: 38 | cmakeBuildDirectory: ${{ github.workspace }}/build 39 | buildConfiguration: Release 40 | ruleset: NativeRecommendedRules.ruleset 41 | ignoredPaths: ${{ github.workspace }}/external 42 | - name: Upload SARIF to GitHub 43 | # Can only upload SARIF if we don't come from a fork 44 | if: ${{ needs.condition-check.outputs.ok == 'true' }} 45 | uses: github/codeql-action/upload-sarif@v2 46 | with: 47 | sarif_file: ${{ steps.run-analysis.outputs.sarif }} 48 | - name: Upload SARIF as an Artifact 49 | uses: actions/upload-artifact@v2 50 | with: 51 | name: sarif-file 52 | path: ${{ steps.run-analysis.outputs.sarif }} 53 | -------------------------------------------------------------------------------- /.github/workflows/sonarcloud.yml: -------------------------------------------------------------------------------- 1 | name: sonarcloud 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | paths-ignore: 8 | - '**/*.md' 9 | pull_request: 10 | branches: 11 | - '**' 12 | paths-ignore: 13 | - '**/*.md' 14 | 15 | jobs: 16 | condition-check: 17 | runs-on: ubuntu-latest 18 | outputs: 19 | ok: ${{ steps.check-secrets.outputs.ok }} 20 | steps: 21 | - name: Secret guard 22 | id: check-secrets 23 | run: | 24 | if [ ! -z "${{ secrets.SONAR_TOKEN }}" ]; then 25 | echo "ok=true" >> $GITHUB_OUTPUT 26 | fi 27 | sonarcloud: 28 | needs: 29 | - condition-check 30 | if: ${{ needs.condition-check.outputs.ok == 'true' }} 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Git checkout 34 | uses: actions/checkout@v4 35 | with: 36 | submodules: 'recursive' 37 | - name: Build with wrapper 38 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-linux-sonarcloud:v3 39 | with: 40 | args: '/build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir bw_output python3 make.py -compiler clang15 -config Release -cpu x64 -build' 41 | - name: Run Sonar Scanner and upload to Sonarcloud 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 45 | SONAR_HOST_URL: https://sonarcloud.io 46 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-linux-sonarcloud:v3 47 | with: 48 | args: '/sonar-scanner-5.0.1.3006-linux/bin/sonar-scanner' 49 | -------------------------------------------------------------------------------- /.github/workflows/test_cpp_versions.yml: -------------------------------------------------------------------------------- 1 | name: test cpp versions 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '**' 7 | paths-ignore: 8 | - '**/*.md' 9 | 10 | jobs: 11 | linux: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | compiler: [gcc13, clang18] 16 | cpp_version: [14, 17, 20] 17 | steps: 18 | - name: Git checkout 19 | uses: actions/checkout@v3 20 | with: 21 | submodules: 'recursive' 22 | - name: Building (release-x86) 23 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 24 | with: 25 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x86 -cpp_version ${{ matrix.cpp_version }} -build' 26 | - name: Clean 27 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 28 | with: 29 | args: 'python3 make.py -clean_only' 30 | - name: Building (release-x64) 31 | uses: docker://ghcr.io/nfrechette/toolchain-amd64-lunar:v2 32 | with: 33 | args: 'python3 make.py -ci -compiler ${{ matrix.compiler }} -config Release -cpu x64 -cpp_version ${{ matrix.cpp_version }} -build' 34 | 35 | vs2022: 36 | runs-on: windows-2022 37 | strategy: 38 | matrix: 39 | cpp_version: [14, 17, 20] 40 | steps: 41 | - name: Git checkout 42 | uses: actions/checkout@v3 43 | with: 44 | submodules: 'recursive' 45 | - name: Building (release-x86) 46 | run: python3 make.py -ci -compiler vs2022 -config Release -cpu x86 -cpp_version ${{ matrix.cpp_version }} -build 47 | - name: Clean 48 | run: python3 make.py -clean_only 49 | - name: Building (release-x64) 50 | run: python3 make.py -ci -compiler vs2022 -config Release -cpu x64 -cpp_version ${{ matrix.cpp_version }} -build 51 | - name: Clean 52 | run: python3 make.py -clean_only 53 | - name: Building (release-arm64) 54 | run: python3 make.py -ci -compiler vs2022 -config Release -cpu arm64 -cpp_version ${{ matrix.cpp_version }} -build 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary data rules 2 | build/ 3 | *.suo 4 | **/__pycache__/ 5 | .vscode/ 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/catch2"] 2 | path = external/catch2 3 | url = https://github.com/catchorg/Catch2 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Significant changes per release 2 | 3 | ## 0.9.0 4 | 5 | * Added support for Clang 12, 13, 14, and 15 6 | * Added support for GCC 11, 12, and 13 7 | * Added support for XCode 14 8 | * Added support for VS2022 9 | * Added support for MSYS2 10 | * Added support for C++14, C++17, and C++20 11 | * CI now uses docker where possible 12 | * Lots of static analysis fixes and improvements 13 | * Misc minor improvements 14 | 15 | ## 0.8.3 16 | 17 | * Force macro expansion in version namespace identifier 18 | 19 | ## 0.8.2 20 | 21 | * Add versioned namespace to allow multiple versions to coexist within a binary 22 | * Add OS X/Linux ARM development support 23 | * Other minor changes 24 | 25 | ## 0.8.1 26 | 27 | * Minor static analysis fixes 28 | 29 | ## 0.8.0 30 | 31 | * Added support for clang 8, 9, and 10 32 | * Added support for GCC 10 33 | * Added support for clang with VS 2019 34 | * Added support for emscripten 35 | * Converted android to use gradle instead of NVIDIA CodeWorks 36 | * Updated Catch2 to 2.11.0 37 | * Migrated from Travis to GitHub Actions 38 | * Cleanup and minor misc changes 39 | 40 | ## 0.7.0 41 | 42 | * Added support for VS 2019, GCC 9, clang 7, and Xcode 11 43 | * Added custom support for NaN/Inf handling 44 | * Cleanup and minor misc changes 45 | 46 | ## 0.6.0 47 | 48 | * Added support for Windows ARM64, GCC 8, clang 6, and Xcode 10 49 | * Integrated SonarCloud 50 | * Cleanup and minor changes & fixes 51 | 52 | ## 0.5.0 53 | 54 | * Cleaned up error handling 55 | * Enabled as many warnings as possible 56 | * Other minor misc changes 57 | 58 | ## 0.4.0 59 | 60 | * Added iOS support 61 | * Other misc additions 62 | 63 | ## 0.3.0 64 | 65 | * Added Android support through NVIDIA CodeWorks 66 | * Added cmake utilities 67 | 68 | ## 0.2.0 69 | 70 | * New SJSON file writer added 71 | * Added unit tests 72 | * Added contributing guidelines, setup instructions, code of conduct 73 | * Added CI builds with AppVeyor and Travis 74 | * Added support for Linux and OS X 75 | * Various other fixes and additions 76 | 77 | ## 0.1.0 78 | 79 | Initial release! 80 | 81 | * Reads SJSON files in ANSI or UTF-8 82 | * Nothing fancy, works as advertised 83 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.2) 2 | project(sjson-cpp CXX) 3 | 4 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") 5 | 6 | include(CMakeUtils) 7 | include(CMakeCompiler) 8 | include(CMakePlatforms) 9 | 10 | set(CPU_INSTRUCTION_SET false CACHE STRING "CPU instruction set") 11 | 12 | if(CMAKE_CONFIGURATION_TYPES) 13 | set(CMAKE_CONFIGURATION_TYPES Debug Release) 14 | set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING "Reset the configurations to what we need" FORCE) 15 | endif() 16 | 17 | # Grab all of our include files 18 | file(GLOB_RECURSE SJSON_CPP_INCLUDE_FILES LIST_DIRECTORIES false 19 | ${PROJECT_SOURCE_DIR}/includes/*.h 20 | ${PROJECT_SOURCE_DIR}/*.md 21 | ${PROJECT_SOURCE_DIR}/cmake/*.cmake 22 | ${PROJECT_SOURCE_DIR}/tools/release_scripts/*.py 23 | ${PROJECT_SOURCE_DIR}/tools/vs_visualizers/*.natvis 24 | ) 25 | 26 | create_source_groups("${SJSON_CPP_INCLUDE_FILES}" ${PROJECT_SOURCE_DIR}) 27 | 28 | file(GLOB SJSON_CPP_ROOT_FILES LIST_DIRECTORIES false 29 | ${PROJECT_SOURCE_DIR}/*.md 30 | ${PROJECT_SOURCE_DIR}/*.py) 31 | 32 | # Create a dummy target so they show up in the IDE 33 | add_custom_target(${PROJECT_NAME} SOURCES ${SJSON_CPP_INCLUDE_FILES} ${SJSON_CPP_ROOT_FILES}) 34 | 35 | # Enable CTest 36 | enable_testing() 37 | 38 | # Add other projects 39 | add_subdirectory("${PROJECT_SOURCE_DIR}/tests") 40 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | 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, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | 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. 28 | 29 | 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. 30 | 31 | ## Scope 32 | 33 | 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. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at zeno490@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems 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. 38 | 39 | 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. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | Thank you for your interest in the sjson-cpp! All contributions that follow our guidelines below and abide by our [code of conduct](CODE_OF_CONDUCT.md) are welcome. 4 | 5 | In this document you will find relevant reading material, what contributions we are looking for, and what we are *not* looking for. 6 | 7 | Project contact email: zeno490@gmail.com 8 | 9 | # Getting set up 10 | 11 | See the [readme](README.md) file for details on how to generate project solutions, build, and run the unit tests. Every pull request should run with continuous integration on every platform we support and the unit tests will also execute as well. 12 | 13 | The project uses [GitHub issues](https://github.com/nfrechette/sjson-cpp/issues) to track bugs and feature requests. 14 | 15 | Whether you create an issue or a pull request, I will do my best to comment or reply within 48 hours. 16 | 17 | # Contributions we are looking for 18 | 19 | Several issues already have a [help wanted](https://github.com/nfrechette/sjson-cpp/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label. Those tasks should be either reasonably simple or tasks that I do not think I will get the chance to get to very soon. 20 | 21 | Feature requests are welcome providing that they fit within the project scope. For smaller features, you are welcome to create an issue with as much relevant information as you can. If it makes sense, I will prioritize it or add it to the backlog, and if I feel it is beyond the scope of the project, I will tell you why and close the issue. For larger topics, the best way to move forward is to reach out by email. 22 | 23 | # Contributions we are *not* looking for 24 | 25 | A lot of older compilers do not properly support C++11 and there is no plan to support them. This also applies to platforms that are either not mainstream or that we cannot easily test with continuous integration. If you would like to support such an exotic environment, reach out by email first so we can discuss this. 26 | 27 | If you aren't sure, don't be afraid to reach out by email! 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CLA assistant](https://cla-assistant.io/readme/badge/nfrechette/sjson-cpp)](https://cla-assistant.io/nfrechette/sjson-cpp) 2 | [![All Contributors](https://img.shields.io/github/all-contributors/nfrechette/sjson-cpp)](#contributors-) 3 | [![Build status](https://ci.appveyor.com/api/projects/status/oynd3x3d9umjaruf/branch/develop?svg=true)](https://ci.appveyor.com/project/nfrechette/sjson-cpp) 4 | [![Build status](https://github.com/nfrechette/sjson-cpp/workflows/build/badge.svg)](https://github.com/nfrechette/sjson-cpp/actions) 5 | [![Sonar Status](https://sonarcloud.io/api/project_badges/measure?project=nfrechette_sjson-cpp&metric=alert_status)](https://sonarcloud.io/dashboard?id=nfrechette_sjson-cpp) 6 | [![GitHub (pre-)release](https://img.shields.io/github/release/nfrechette/sjson-cpp/all.svg)](https://github.com/nfrechette/sjson-cpp/releases) 7 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nfrechette/sjson-cpp/master/LICENSE) 8 | 9 | # sjson-cpp 10 | 11 | `sjson-cpp` is a C++ library to read and write [Simplified JSON](http://help.autodesk.com/view/Stingray/ENU/?guid=__stingray_help_managing_content_sjson_html) files. 12 | It aims to be minimal, fast, and get out of the way of the programmer. 13 | 14 | By design, the library does no memory allocations. This is in contrast to the [nflibs C parser](https://github.com/niklasfrykholm/nflibs). 15 | 16 | Everything is **100% C++11** header based for easy and trivial integration. 17 | 18 | This parser is intended to accept only pure SJSON, and it will fail if given a JSON file, unlike the [Autodesk JS Stingray parser](https://github.com/Autodesk/sjson). 19 | 20 | ## The SJSON format 21 | 22 | The data format is described [here](http://help.autodesk.com/view/Stingray/ENU/?guid=__stingray_help_managing_content_sjson_html) in the Stingray documentation. 23 | 24 | TODO: Add a reference sjson file showing the format as a form of loose specification 25 | 26 | ## Unicode support 27 | 28 | UTF-8 support is as follow: 29 | 30 | * String values return a raw `StringView` into the SJSON buffer. It is the responsability of the caller to interpret it as ANSI or UTF-8. 31 | * String values properly support escaped unicode sequences in that they are returned raw in the `StringView`. 32 | * Keys do not support UTF-8, they must be ANSI. 33 | * The BOM is properly skipped if present 34 | 35 | Unicode formats other than UTF-8 aren't supported. 36 | 37 | ## Supported platforms 38 | 39 | * Windows VS2015 x86 and x64 40 | * Windows (VS2017 to VS2022) x86, x64, and ARM64 41 | * Windows (VS2017 to VS2022) with clang x86 and x64 42 | * Linux (gcc 5 to 13) x86 and x64 43 | * Linux (clang 4 to 15) x86 and x64 44 | * OS X (XCode 12.5, 13.2, 14.2) x64 and ARM64 45 | * Android (NDK 21) ARMv7-A and ARM64 46 | * iOS (Xcode 10.3, 11.7, 12.5, 13.2, 14.2) ARM64 47 | * Emscripten (1.39.11) WASM 48 | * MSYS2 x64 49 | 50 | The above supported platform list is only what is tested every release but if it compiles, it should run just fine. 51 | 52 | ## External dependencies 53 | 54 | There are none! You don't need anything else to get started: everything is self contained. 55 | See [here](./external) for details. 56 | 57 | ## Getting up and running 58 | 59 | This library is **100%** headers as such you just need to include them in your own project to start using it. However, if you wish to run the unit tests you will need a few things, see below. 60 | 61 | ### Windows, Linux, and OS X for x86 and x64 62 | 63 | 1. Install *CMake 3.2* or higher (*3.14* for Visual Studio 2019, or *3.10* on OS X with *Xcode 10*), *Python 2.7 or 3*, and the proper compiler for your platform. 64 | 2. Execute `git submodule update --init` to get the files of external submodules (e.g. Catch2). 65 | 3. Generate the IDE solution with: `python make.py` 66 | The solution is generated under `./build` 67 | 4. Build the IDE solution with: `python make.py -build` 68 | 5. Run the unit tests with: `python make.py -unit_test` 69 | 70 | ### Windows ARM64 71 | 72 | For *Windows on ARM64*, the steps are identical to *x86 and x64* but you will need *CMake 3.13 or higher* and you must provide the architecture on the command line: `python make.py -compiler vs2017 -cpu arm64` 73 | 74 | ### Android 75 | 76 | For *Android*, the steps are identical to *Windows, Linux, and OS X* but you also need to install *Android NDK 21* (or higher). The build uses `gradle` and `-unit_test` will deploy and run on the device when executed (make sure that the `adb` executable is in your `PATH` for this to work). 77 | 78 | *Android Studio v3.5* can be used to launch and debug. After running *CMake* to build and generate everything, the *Android Studio* projects can be found under the `./build` directory. 79 | 80 | ### iOS 81 | 82 | For *iOS*, the steps are identical to the other platforms but due to code signing, you will need to perform the builds from *Xcode* manually. Note that this is only an issue if you attempt to use the tools or run the unit tests locally. 83 | 84 | ### Emscripten 85 | 86 | Emscripten support currently only has been tested on OS X and Linux. To use it, make sure to install a recent version of Emscripten SDK 1.39.11+. 87 | 88 | ## Commit message format 89 | 90 | This library uses the [angular.js message format](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits) and it is enforced with commit linting through every pull request. 91 | 92 | ## Authors 93 | 94 | * [Nicholas Frechette](https://github.com/nfrechette) 95 | * [Cody Jones](https://github.com/CodyDWJones) 96 | 97 | ## License, copyright, and code of conduct 98 | 99 | This project uses the [MIT license](LICENSE). 100 | 101 | Copyright (c) 2017 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 102 | 103 | Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. 104 | 105 | ## Contributors ✨ 106 | 107 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |

CodyDWJones

💻 🚧

Michał Janiszewski

💻 🚧

Martin Turcotte

🚧

Meradrin

🐛
120 | 121 | 122 | 123 | 124 | 125 | 126 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 127 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.9.99.{build} 2 | 3 | environment: 4 | PYTHON: "C:\\Python36-x64\\python.exe" 5 | matrix: 6 | - toolchain: msvc 7 | - toolchain: clang 8 | 9 | image: 10 | - Visual Studio 2015 11 | - Visual Studio 2017 12 | 13 | configuration: 14 | - Debug 15 | - Release 16 | 17 | platform: 18 | - x86 19 | - x64 20 | - arm64 21 | 22 | matrix: 23 | exclude: 24 | - image: Visual Studio 2015 25 | platform: arm64 26 | - image: Visual Studio 2015 27 | toolchain: clang 28 | - image: Visual Studio 2017 29 | toolchain: clang 30 | 31 | 32 | init: 33 | # Only run latest compiler on push, run everything on pull request 34 | - ps: if (!$env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_WORKER_IMAGE -ne "Visual Studio 2019") {Exit-AppveyorBuild} 35 | 36 | install: 37 | - cmd: >- 38 | git submodule update --init --recursive 39 | 40 | build_script: 41 | - cmd: >- 42 | .\tools\appveyor_ci.bat "%APPVEYOR_BUILD_WORKER_IMAGE%" %platform% %configuration% %toolchain% "%PYTHON%" 43 | -------------------------------------------------------------------------------- /cmake/CMakeCompiler.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.2) 2 | 3 | macro(setup_default_compiler_flags _project_name) 4 | if(MSVC) 5 | # Replace some default compiler switches and add new ones 6 | STRING(REPLACE "/GR" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) # Disable RTTI 7 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 8 | STRING(REPLACE "/W3" "/W4" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) # Enable level 4 warnings 9 | else() 10 | if(MSVC_VERSION GREATER 1920) 11 | # VS2019 and above 12 | STRING(REPLACE "/W3" "/Wall" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) # Enable all warnings 13 | else() 14 | STRING(REPLACE "/W3" "/W4" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) # Enable level 4 warnings 15 | endif() 16 | endif() 17 | target_compile_options(${_project_name} PRIVATE /Zi) # Add debug info 18 | target_compile_options(${_project_name} PRIVATE /Oi) # Generate intrinsic functions 19 | target_compile_options(${_project_name} PRIVATE /WX) # Treat warnings as errors 20 | target_compile_options(${_project_name} PRIVATE /MP) # Enable parallel compilation 21 | 22 | if(MSVC_VERSION GREATER 1900) 23 | # VS2017 and above 24 | target_compile_options(${_project_name} PRIVATE /permissive-) 25 | endif() 26 | 27 | # Disable various warnings that are harmless 28 | target_compile_options(${_project_name} PRIVATE /wd4514) # Unreferenced inline function removed 29 | target_compile_options(${_project_name} PRIVATE /wd4619) # No warning with specified number 30 | target_compile_options(${_project_name} PRIVATE /wd4820) # Padding added after data member 31 | target_compile_options(${_project_name} PRIVATE /wd4710) # Function not inlined 32 | target_compile_options(${_project_name} PRIVATE /wd4711) # Function selected for automatic inlining 33 | target_compile_options(${_project_name} PRIVATE /wd4738) # Storing 32-bit float in memory leads to rounding (x86) 34 | target_compile_options(${_project_name} PRIVATE /wd5045) # Spectre mitigation for memory load 35 | 36 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 37 | target_compile_options(${_project_name} PRIVATE -Wno-c++98-compat) # No need to support C++98 38 | target_compile_options(${_project_name} PRIVATE -Wno-c++98-compat-pedantic) # No need to support C++98 39 | endif() 40 | 41 | # Add linker flags 42 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG") 43 | else() 44 | # TODO: Handle OS X properly: https://stackoverflow.com/questions/5334095/cmake-multiarchitecture-compilation 45 | if(CPU_INSTRUCTION_SET MATCHES "x86") 46 | target_compile_options(${_project_name} PRIVATE "-m32") 47 | target_link_libraries(${_project_name} PRIVATE "-m32") 48 | elseif(CPU_INSTRUCTION_SET MATCHES "x64") 49 | target_compile_options(${_project_name} PRIVATE "-m64") 50 | target_link_libraries(${_project_name} PRIVATE "-m64") 51 | endif() 52 | 53 | target_compile_options(${_project_name} PRIVATE -Wall -Wextra) # Enable all warnings 54 | target_compile_options(${_project_name} PRIVATE -Wshadow) # Enable shadowing warnings 55 | target_compile_options(${_project_name} PRIVATE -Werror) # Treat warnings as errors 56 | 57 | # Disable various warnings that are harmless 58 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 59 | target_compile_options(${_project_name} PRIVATE -Wno-c++98-compat) # No need to support C++98 60 | endif() 61 | 62 | target_compile_options(${_project_name} PRIVATE -g) # Enable debug symbols 63 | endif() 64 | endmacro() 65 | -------------------------------------------------------------------------------- /cmake/CMakePlatforms.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | 3 | if(PLATFORM_NAME) 4 | return() # Already set 5 | endif() 6 | 7 | # Detect which platform we have 8 | if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") 9 | set(PLATFORM_WINDOWS 1) 10 | set(PLATFORM_NAME "Windows") 11 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") 12 | if(PLATFORM_IOS) 13 | set(PLATFORM_NAME "iOS") 14 | else() 15 | set(PLATFORM_OSX 1) 16 | set(PLATFORM_NAME "OS X") 17 | endif() 18 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 19 | set(PLATFORM_LINUX 1) 20 | set(PLATFORM_NAME "Linux") 21 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Android") 22 | set(PLATFORM_ANDROID 1) 23 | set(PLATFORM_NAME "Android") 24 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten") 25 | set(PLATFORM_EMSCRIPTEN 1) 26 | set(PLATFORM_NAME "Emscripten") 27 | else() 28 | message(FATAL_ERROR "Unknown platform ${CMAKE_SYSTEM_NAME}!") 29 | endif() 30 | 31 | message(STATUS "Detected platform: ${PLATFORM_NAME}") 32 | -------------------------------------------------------------------------------- /cmake/CMakeUtils.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.2) 2 | 3 | # Create groups for the test source files, this creates the proper directory structure under the 4 | # Visual Studio filters 5 | macro(create_source_groups _source_files _relative_directory) 6 | foreach(_file IN ITEMS ${_source_files}) 7 | get_filename_component(_file_path "${_file}" PATH) 8 | file(RELATIVE_PATH _file_path_rel "${_relative_directory}" "${_file_path}") 9 | string(REPLACE "/" "\\" _group_path "${_file_path_rel}") 10 | source_group("${_group_path}" FILES "${_file}") 11 | endforeach() 12 | endmacro() 13 | -------------------------------------------------------------------------------- /cmake/Toolchain-Android.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | 3 | # For Android, we just set the platform name as we won't be using CMake to build anything. 4 | # Instead Gradle is used through CMake. 5 | 6 | set(PLATFORM_ANDROID 1) 7 | set(PLATFORM_NAME "Android") 8 | 9 | # Remap our CPU instruction set 10 | if(CPU_INSTRUCTION_SET MATCHES "armv7") 11 | set(CPU_INSTRUCTION_SET "armeabi-v7a") 12 | elseif(CPU_INSTRUCTION_SET MATCHES "arm64") 13 | set(CPU_INSTRUCTION_SET "arm64-v8a") 14 | endif() 15 | -------------------------------------------------------------------------------- /cmake/Toolchain-iOS.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.2) 2 | 3 | set(CMAKE_SYSTEM_NAME Darwin) 4 | 5 | # Set here instead of CMakePlatforms.cmake since we can't distinguis otherwise 6 | set(PLATFORM_IOS 1) 7 | 8 | # Find and set the C/C++ compiler paths, cmake doesn't seem to do this properly on its own 9 | execute_process(COMMAND xcrun --sdk iphoneos --find clang OUTPUT_VARIABLE CMAKE_C_COMPILER OUTPUT_STRIP_TRAILING_WHITESPACE) 10 | execute_process(COMMAND xcrun --sdk iphoneos --find clang++ OUTPUT_VARIABLE CMAKE_CXX_COMPILER OUTPUT_STRIP_TRAILING_WHITESPACE) 11 | 12 | set(CMAKE_MACOSX_BUNDLE YES) 13 | set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "") 14 | set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "") 15 | set(CMAKE_OSX_SYSROOT iphoneos CACHE STRING "") 16 | set(CMAKE_OSX_ARCHITECTURES arm64 CACHE STRING "") 17 | set(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET 17.0) 18 | -------------------------------------------------------------------------------- /external/README.md: -------------------------------------------------------------------------------- 1 | # External dependencies 2 | 3 | Good news! There are no external dependencies needed by this library at runtime! 4 | 5 | ## Catch 6 | 7 | [Catch2 v2.13.7](https://github.com/catchorg/Catch2/releases/tag/v2.13.7) is used by our [unit tests](../tests). You will only need it if you run the unit tests and it is included as-is without modifications. 8 | -------------------------------------------------------------------------------- /includes/sjson/error.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2018 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | ////////////////////////////////////////////////////////////////////////// 28 | // This library uses a simple system to handle asserts. Asserts are fatal and must terminate 29 | // otherwise the behavior is undefined if execution continues. 30 | // 31 | // A total of 4 behaviors are supported: 32 | // - We can print to stderr and abort 33 | // - We can throw and exception 34 | // - We can call a custom function 35 | // - Do nothing and strip the check at compile time (default behavior) 36 | // 37 | // Aborting: 38 | // In order to enable the aborting behavior, simply define the macro SJSON_CPP_ON_ASSERT_ABORT: 39 | // #define SJSON_CPP_ON_ASSERT_ABORT 40 | // 41 | // Throwing: 42 | // In order to enable the throwing behavior, simply define the macro SJSON_CPP_ON_ASSERT_THROW: 43 | // #define SJSON_CPP_ON_ASSERT_THROW 44 | // Note that the type of the exception thrown is sjson::runtime_assert. 45 | // 46 | // Custom function: 47 | // In order to enable the custom function calling behavior, define the macro SJSON_CPP_ON_ASSERT_CUSTOM 48 | // with the name of the function to call: 49 | // #define SJSON_CPP_ON_ASSERT_CUSTOM on_custom_assert_impl 50 | // Note that the function signature is as follow: 51 | // void on_custom_assert_impl(const char* expression, int line, const char* file, const char* format, ...) {} 52 | // 53 | // You can also define your own assert implementation by defining the SJSON_CPP_ASSERT macro as well: 54 | // #define SJSON_CPP_ON_ASSERT_CUSTOM 55 | // #define SJSON_CPP_ASSERT(expression, format, ...) checkf(expression, ANSI_TO_TCHAR(format), #__VA_ARGS__) 56 | // 57 | // [Custom String Format Specifier] 58 | // Note that if you use a custom function, you may need to override the SJSON_ASSERT_STRING_FORMAT_SPECIFIER 59 | // to properly handle ANSI/Unicode support. The C++11 standard does not support a way to say that '%s' 60 | // always means an ANSI string (with 'const char*' as type). MSVC does support '%hs' but other compilers 61 | // do not. 62 | // 63 | // No checks: 64 | // By default if no macro mentioned above is defined, all asserts will be stripped 65 | // at compile time. 66 | ////////////////////////////////////////////////////////////////////////// 67 | 68 | // See [Custom String Format Specifier] for details 69 | #if !defined(SJSON_ASSERT_STRING_FORMAT_SPECIFIER) 70 | #define SJSON_ASSERT_STRING_FORMAT_SPECIFIER "%s" 71 | #endif 72 | 73 | #if defined(SJSON_CPP_ON_ASSERT_ABORT) 74 | 75 | #include "sjson/version.h" 76 | 77 | #include 78 | #include 79 | #include 80 | 81 | namespace sjson 82 | { 83 | SJSON_CPP_IMPL_VERSION_NAMESPACE_BEGIN 84 | 85 | namespace error_impl 86 | { 87 | [[noreturn]] inline void on_assert_abort(const char* expression, int line, const char* file, const char* format, ...) 88 | { 89 | (void)expression; 90 | (void)line; 91 | (void)file; 92 | 93 | va_list args; 94 | va_start(args, format); 95 | 96 | std::vfprintf(stderr, format, args); 97 | std::fprintf(stderr, "\n"); 98 | 99 | va_end(args); 100 | 101 | std::abort(); 102 | } 103 | } 104 | 105 | SJSON_CPP_IMPL_VERSION_NAMESPACE_END 106 | } 107 | 108 | #define SJSON_CPP_ASSERT(expression, format, ...) do { if (!(expression)) SJSON_CPP_IMPL_NAMESPACE::error_impl::on_assert_abort(#expression, __LINE__, __FILE__, (format), ## __VA_ARGS__); } while (false) 109 | #define SJSON_CPP_HAS_ASSERT_CHECKS 110 | 111 | #elif defined(SJSON_CPP_ON_ASSERT_THROW) 112 | 113 | #include "sjson/version.h" 114 | 115 | #include 116 | #include 117 | #include 118 | #include 119 | 120 | namespace sjson 121 | { 122 | SJSON_CPP_IMPL_VERSION_NAMESPACE_BEGIN 123 | 124 | class runtime_assert final : public std::runtime_error 125 | { 126 | runtime_assert() = delete; // Default constructor not needed 127 | 128 | using std::runtime_error::runtime_error; // Inherit constructors 129 | }; 130 | 131 | namespace error_impl 132 | { 133 | [[noreturn]] inline void on_assert_throw(const char* expression, int line, const char* file, const char* format, ...) 134 | { 135 | (void)expression; 136 | (void)line; 137 | (void)file; 138 | 139 | constexpr int buffer_size = 1 * 1024; 140 | char buffer[buffer_size]; 141 | 142 | va_list args; 143 | va_start(args, format); 144 | 145 | const int count = vsnprintf(buffer, buffer_size, format, args); 146 | 147 | va_end(args); 148 | 149 | if (count >= 0 && count < buffer_size) 150 | throw runtime_assert(std::string(&buffer[0], static_cast(count))); 151 | else 152 | throw runtime_assert("Failed to format assert message!\n"); 153 | } 154 | } 155 | 156 | SJSON_CPP_IMPL_VERSION_NAMESPACE_END 157 | } 158 | 159 | #define SJSON_CPP_ASSERT(expression, format, ...) do { if (!(expression)) SJSON_CPP_IMPL_NAMESPACE::error_impl::on_assert_throw(#expression, __LINE__, __FILE__, (format), ## __VA_ARGS__); } while(false) 160 | #define SJSON_CPP_HAS_ASSERT_CHECKS 161 | 162 | #elif defined(SJSON_CPP_ON_ASSERT_CUSTOM) 163 | 164 | #if !defined(SJSON_CPP_ASSERT) 165 | #define SJSON_CPP_ASSERT(expression, format, ...) do { if (!(expression)) SJSON_CPP_ON_ASSERT_CUSTOM(#expression, __LINE__, __FILE__, (format), ## __VA_ARGS__); } while(false) 166 | #endif 167 | 168 | #define SJSON_CPP_HAS_ASSERT_CHECKS 169 | 170 | #else 171 | 172 | #define SJSON_CPP_ASSERT(expression, format, ...) ((void)0) 173 | 174 | #endif 175 | -------------------------------------------------------------------------------- /includes/sjson/fwd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2022 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | #include "sjson/version.h" 28 | 29 | //////////////////////////////////////////////////////////////////////////////// 30 | // This header provides forward declarations for all public sjson-cpp types. 31 | // Forward declaring symbols from a 3rd party library is a bad idea, use this 32 | // header instead. 33 | // See also: https://blog.libtorrent.org/2017/12/forward-declarations-and-abi/ 34 | //////////////////////////////////////////////////////////////////////////////// 35 | 36 | namespace sjson 37 | { 38 | SJSON_CPP_IMPL_VERSION_NAMESPACE_BEGIN 39 | 40 | // Core 41 | class runtime_assert; 42 | class StringView; 43 | 44 | // Parser 45 | struct ParserError; 46 | struct ParserState; 47 | class Parser; 48 | 49 | // Writer 50 | class StreamWriter; 51 | class FileStreamWriter; 52 | class ArrayWriter; 53 | class ObjectWriter; 54 | class Writer; 55 | 56 | SJSON_CPP_IMPL_VERSION_NAMESPACE_END 57 | } 58 | -------------------------------------------------------------------------------- /includes/sjson/impl/bit_cast.impl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2023 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | #include "sjson/version.h" 28 | 29 | #if defined(__cplusplus) && __cplusplus >= 202002L 30 | #include 31 | #elif defined(_MSVC_LANG) && _MSVC_LANG >= 202002L 32 | #include 33 | #endif 34 | 35 | namespace sjson 36 | { 37 | SJSON_CPP_IMPL_VERSION_NAMESPACE_BEGIN 38 | 39 | namespace sjson_impl 40 | { 41 | ////////////////////////////////////////////////////////////////////////// 42 | // C++20 introduced std::bit_cast which is safer than reinterpret_cast 43 | ////////////////////////////////////////////////////////////////////////// 44 | 45 | #if defined(__cplusplus) && __cplusplus >= 202002L 46 | using std::bit_cast; 47 | #elif defined(_MSVC_LANG) && _MSVC_LANG >= 202002L 48 | using std::bit_cast; 49 | #else 50 | template 51 | constexpr dest_type_t bit_cast(src_type_t input) noexcept 52 | { 53 | return reinterpret_cast(input); 54 | } 55 | #endif 56 | } 57 | 58 | SJSON_CPP_IMPL_VERSION_NAMESPACE_END 59 | } 60 | -------------------------------------------------------------------------------- /includes/sjson/impl/cstdlib.impl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2018 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | #include "sjson/version.h" 28 | 29 | ////////////////////////////////////////////////////////////////////////// 30 | // The Android NDK r10 and possibly earlier used a STD lib that poorly supported C++11. 31 | // As such, it is necessary to polyfill a few missing things. 32 | ////////////////////////////////////////////////////////////////////////// 33 | #if defined(__GNUG__) && defined(__ANDROID__) && __GNUC__ < 5 34 | #define SJSON_CPP_IMPL_POLYFILL_STD 35 | #endif 36 | 37 | #if defined(SJSON_CPP_IMPL_POLYFILL_STD) 38 | #include 39 | #else 40 | #include 41 | #endif 42 | 43 | namespace sjson 44 | { 45 | SJSON_CPP_IMPL_VERSION_NAMESPACE_BEGIN 46 | 47 | namespace sjson_impl 48 | { 49 | #if defined(SJSON_CPP_IMPL_POLYFILL_STD) 50 | // We need to polyfill these, bring the C version into our namespace 51 | using ::strtoull; 52 | using ::strtoll; 53 | using ::strtof; 54 | #else 55 | // These properly exist in the C++ version, use them 56 | using std::strtoull; 57 | using std::strtoll; 58 | using std::strtof; 59 | #endif 60 | } 61 | 62 | SJSON_CPP_IMPL_VERSION_NAMESPACE_END 63 | } 64 | -------------------------------------------------------------------------------- /includes/sjson/parser_error.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2017 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | #include "sjson/version.h" 28 | 29 | #include 30 | 31 | namespace sjson 32 | { 33 | SJSON_CPP_IMPL_VERSION_NAMESPACE_BEGIN 34 | 35 | struct ParserError 36 | { 37 | enum : uint32_t 38 | { 39 | None, 40 | InputTruncated, 41 | OpeningBraceExpected, 42 | ClosingBraceExpected, 43 | EqualSignExpected, 44 | OpeningBracketExpected, 45 | ClosingBracketExpected, 46 | CommaExpected, 47 | CommentBeginsIncorrectly, 48 | CannotUseQuotationMarkInUnquotedString, 49 | KeyExpected, 50 | IncorrectKey, 51 | TrueOrFalseExpected, 52 | QuotationMarkExpected, 53 | NumberExpected, 54 | NumberIsTooLong, 55 | InvalidNumber, 56 | NumberCouldNotBeConverted, 57 | UnexpectedContentAtEnd, 58 | 59 | Last 60 | }; 61 | 62 | uint32_t error = None; 63 | uint32_t line = 0; 64 | uint32_t column = 0; 65 | 66 | ParserError() = default; 67 | ParserError(const ParserError&) = default; 68 | ParserError& operator=(const ParserError&) = default; 69 | 70 | virtual ~ParserError() = default; 71 | 72 | virtual const char* get_description() const 73 | { 74 | switch (error) 75 | { 76 | case None: 77 | return "None"; 78 | case InputTruncated: 79 | return "The file ended sooner than expected"; 80 | case OpeningBraceExpected: 81 | return "An opening { is expected here"; 82 | case ClosingBraceExpected: 83 | return "A closing } is expected here"; 84 | case EqualSignExpected: 85 | return "An equal sign is expected here"; 86 | case OpeningBracketExpected: 87 | return "An opening [ is expected here"; 88 | case ClosingBracketExpected: 89 | return "A closing ] is expected here"; 90 | case CommaExpected: 91 | return "A comma is expected here"; 92 | case CommentBeginsIncorrectly: 93 | return "Comments must start with either // or /*"; 94 | case CannotUseQuotationMarkInUnquotedString: 95 | return "Quotation marks cannot be used in unquoted strings"; 96 | case KeyExpected: 97 | return "The name of a key is expected here"; 98 | case IncorrectKey: 99 | return "A different key is expected here"; 100 | case TrueOrFalseExpected: 101 | return "Only the literals true or false are allowed here"; 102 | case QuotationMarkExpected: 103 | return "A quotation mark is expected here"; 104 | case NumberExpected: 105 | return "A number is expected here"; 106 | case NumberIsTooLong: 107 | return "The number is too long; increase Parser::MAX_NUMBER_LENGTH"; 108 | case InvalidNumber: 109 | return "This number has an invalid format"; 110 | case NumberCouldNotBeConverted: 111 | return "This number could not be converted"; 112 | case UnexpectedContentAtEnd: 113 | return "There should not be any more content in this file"; 114 | default: 115 | return "Unknown error"; 116 | } 117 | } 118 | }; 119 | 120 | SJSON_CPP_IMPL_VERSION_NAMESPACE_END 121 | } 122 | -------------------------------------------------------------------------------- /includes/sjson/parser_state.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2017 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | #include "sjson/parser_error.h" 28 | #include "sjson/version.h" 29 | 30 | #include 31 | #include 32 | 33 | namespace sjson 34 | { 35 | SJSON_CPP_IMPL_VERSION_NAMESPACE_BEGIN 36 | 37 | struct ParserState 38 | { 39 | ParserState(const char* input, size_t input_length) 40 | : offset(0) 41 | , line(1) 42 | , column(1) 43 | , symbol(input_length > 0 ? input[0] : '\0') 44 | { 45 | } 46 | 47 | size_t offset; 48 | uint32_t line; 49 | uint32_t column; 50 | char symbol; 51 | 52 | ParserError error; 53 | }; 54 | 55 | SJSON_CPP_IMPL_VERSION_NAMESPACE_END 56 | } 57 | -------------------------------------------------------------------------------- /includes/sjson/string_view.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2017 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | #include "sjson/error.h" 28 | #include "sjson/version.h" 29 | 30 | #include 31 | #include 32 | 33 | namespace sjson 34 | { 35 | SJSON_CPP_IMPL_VERSION_NAMESPACE_BEGIN 36 | 37 | ////////////////////////////////////////////////////////////////////////// 38 | // A StringView is just a pointer to a string and an associated length. 39 | // It does NOT own the memory and no allocation or deallocation ever takes place. 40 | // An empty StringView() is equal to a StringView("") of the empty string. 41 | // It is not legal to create a StringView that contains multiple NULL terminators 42 | // and if you do so, the behavior is undefined. 43 | // 44 | // Two different StringViews are equal if the strings pointed to are equal. 45 | // They do not need to be pointing to the same physical string. 46 | // StringView("this") == StringView("this is fun", 4) 47 | // 48 | // The string pointed to is immutable. 49 | ////////////////////////////////////////////////////////////////////////// 50 | class StringView 51 | { 52 | public: 53 | constexpr StringView() 54 | : m_c_str(nullptr) 55 | , m_length(0) 56 | {} 57 | 58 | StringView(const char* str, size_t length) 59 | : m_c_str(str) 60 | , m_length(length) 61 | { 62 | #if defined(SJSON_CPP_USE_ERROR_CHECKS) && !defined(NDEBUG) 63 | for (size_t i = 0; i < length; ++i) 64 | SJSON_CPP_ASSERT(str[i] != '\0', "StringView cannot contain NULL terminators"); 65 | #endif 66 | } 67 | 68 | StringView(const char* str) 69 | : m_c_str(str) 70 | , m_length(str == nullptr ? 0 : std::strlen(str)) 71 | {} 72 | 73 | StringView& operator=(const char* str) 74 | { 75 | m_c_str = str; 76 | m_length = str == nullptr ? 0 : std::strlen(str); 77 | return *this; 78 | } 79 | 80 | constexpr const char* c_str() const { return m_length == 0 ? "" : m_c_str; } 81 | constexpr size_t size() const { return m_length; } 82 | constexpr bool empty() const { return m_length == 0; } 83 | 84 | bool operator==(const char* str) const 85 | { 86 | const size_t length = str == nullptr ? 0 : std::strlen(str); 87 | if (m_length != length) 88 | return false; 89 | 90 | if (m_c_str == str || length == 0) 91 | return true; 92 | 93 | return std::memcmp(m_c_str, str, length) == 0; 94 | } 95 | 96 | bool operator!=(const char* str) const { return !(*this == str); } 97 | 98 | bool operator==(const StringView& other) const 99 | { 100 | if (m_length != other.m_length) 101 | return false; 102 | 103 | if (m_c_str == other.m_c_str || m_length == 0) 104 | return true; 105 | 106 | return std::memcmp(m_c_str, other.m_c_str, m_length) == 0; 107 | } 108 | 109 | bool operator !=(const StringView& other) const { return !(*this == other); } 110 | 111 | char operator[](size_t offset) const 112 | { 113 | SJSON_CPP_ASSERT(offset < m_length, "Invalid offset"); 114 | return m_c_str[offset]; 115 | } 116 | 117 | private: 118 | const char* m_c_str; 119 | size_t m_length; 120 | }; 121 | 122 | SJSON_CPP_IMPL_VERSION_NAMESPACE_END 123 | } 124 | -------------------------------------------------------------------------------- /includes/sjson/version.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | // The MIT License (MIT) 5 | // 6 | // Copyright (c) 2022 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in all 16 | // copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | // SOFTWARE. 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | //////////////////////////////////////////////////////////////////////////////// 28 | // Macros to detect the sjson-cpp version 29 | //////////////////////////////////////////////////////////////////////////////// 30 | 31 | #define SJSON_CPP_VERSION_MAJOR 0 32 | #define SJSON_CPP_VERSION_MINOR 9 33 | #define SJSON_CPP_VERSION_PATCH 99 34 | 35 | //////////////////////////////////////////////////////////////////////////////// 36 | // In order to allow multiple versions of this library to coexist side by side 37 | // within the same executable/library, the symbols have to be unique per version. 38 | // We achieve this by using a versioned namespace that we optionally inline. 39 | // To disable namespace inlining, define SJSON_CPP_NO_INLINE_NAMESPACE before including 40 | // any sjson-cpp header. To disable the versioned namespace altogether, 41 | // define SJSON_CPP_NO_VERSION_NAMESPACE before including any sjson-cpp header. 42 | //////////////////////////////////////////////////////////////////////////////// 43 | 44 | #if !defined(SJSON_CPP_NO_VERSION_NAMESPACE) 45 | #if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER == 1900 46 | // VS2015 struggles with type resolution when inline namespaces are used 47 | // For that reason, we disable it explicitly 48 | #define SJSON_CPP_NO_VERSION_NAMESPACE 49 | #endif 50 | #endif 51 | 52 | // Force macro expansion to concatenate namespace identifier 53 | #define SJSON_CPP_IMPL_VERSION_CONCAT_IMPL(prefix, major, minor, patch) prefix ## major ## minor ## patch 54 | #define SJSON_CPP_IMPL_VERSION_CONCAT(prefix, major, minor, patch) SJSON_CPP_IMPL_VERSION_CONCAT_IMPL(prefix, major, minor, patch) 55 | 56 | // Name of the namespace, e.g. v082 57 | #define SJSON_CPP_IMPL_VERSION_NAMESPACE_NAME SJSON_CPP_IMPL_VERSION_CONCAT(v, SJSON_CPP_VERSION_MAJOR, SJSON_CPP_VERSION_MINOR, SJSON_CPP_VERSION_PATCH) 58 | 59 | // Because this is being introduced in a patch release, as caution, it is disabled 60 | // by default. It does break ABI if host runtimes forward declare types but that 61 | // is something they shouldn't do with a 3rd party library. Now, we offer forward 62 | // declaration headers to help prepare the migration in the next minor release. 63 | #if defined(SJSON_CPP_NO_VERSION_NAMESPACE) || !defined(SJSON_CPP_ENABLE_VERSION_NAMESPACE) 64 | // Namespace is inlined, its usage does not need to be qualified with the 65 | // full version everywhere 66 | #define SJSON_CPP_IMPL_NAMESPACE sjson 67 | 68 | #define SJSON_CPP_IMPL_VERSION_NAMESPACE_BEGIN 69 | #define SJSON_CPP_IMPL_VERSION_NAMESPACE_END 70 | #elif defined(SJSON_CPP_NO_INLINE_NAMESPACE) 71 | // Namespace won't be inlined, its usage will have to be qualified with the 72 | // full version everywhere 73 | #define SJSON_CPP_IMPL_NAMESPACE sjson::SJSON_CPP_IMPL_VERSION_NAMESPACE_NAME 74 | 75 | #define SJSON_CPP_IMPL_VERSION_NAMESPACE_BEGIN \ 76 | namespace SJSON_CPP_IMPL_VERSION_NAMESPACE_NAME \ 77 | { 78 | 79 | #define SJSON_CPP_IMPL_VERSION_NAMESPACE_END \ 80 | } 81 | #else 82 | // Namespace is inlined, its usage does not need to be qualified with the 83 | // full version everywhere 84 | #define SJSON_CPP_IMPL_NAMESPACE sjson 85 | 86 | #define SJSON_CPP_IMPL_VERSION_NAMESPACE_BEGIN \ 87 | inline namespace SJSON_CPP_IMPL_VERSION_NAMESPACE_NAME \ 88 | { 89 | 90 | #define SJSON_CPP_IMPL_VERSION_NAMESPACE_END \ 91 | } 92 | #endif 93 | -------------------------------------------------------------------------------- /make.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import multiprocessing 3 | import os 4 | import platform 5 | import shutil 6 | import subprocess 7 | import sys 8 | 9 | def parse_argv(): 10 | parser = argparse.ArgumentParser(add_help=False) 11 | 12 | actions = parser.add_argument_group(title='Actions', description='If no action is specified, on Windows, OS X, and Linux the solution/make files are generated. Multiple actions can be used simultaneously.') 13 | actions.add_argument('-build', action='store_true') 14 | actions.add_argument('-clean', action='store_true') 15 | actions.add_argument('-clean_only', action='store_true') 16 | actions.add_argument('-unit_test', action='store_true') 17 | 18 | target = parser.add_argument_group(title='Target') 19 | target.add_argument('-compiler', choices=['vs2015', 'vs2017', 'vs2019', 'vs2019-clang', 'vs2022', 'vs2022-clang', 'android', 'clang4', 'clang5', 'clang6', 'clang7', 'clang8', 'clang9', 'clang10', 'clang11', 'clang12', 'clang13', 'clang14', 'clang15', 'clang16', 'clang17', 'clang18', 'gcc4.8', 'gcc4.9', 'gcc5', 'gcc6', 'gcc7', 'gcc8', 'gcc9', 'gcc10', 'gcc11', 'gcc12', 'gcc13', 'osx', 'ios', 'emscripten'], help='Defaults to the host system\'s default compiler') 20 | target.add_argument('-config', choices=['Debug', 'Release'], type=str.capitalize) 21 | target.add_argument('-cpu', choices=['x86', 'x64', 'armv7', 'arm64', 'wasm'], help='Defaults to the host system\'s architecture') 22 | target.add_argument('-cpp_version', choices=['11', '14', '17', '20'], help='Defaults to C++11') 23 | 24 | misc = parser.add_argument_group(title='Miscellaneous') 25 | misc.add_argument('-num_threads', help='No. to use while compiling') 26 | misc.add_argument('-tests_matching', help='Only run tests whose names match this regex') 27 | misc.add_argument('-ci', action='store_true', help='Whether or not this is a Continuous Integration build') 28 | misc.add_argument('-help', action='help', help='Display this usage information') 29 | 30 | num_threads = multiprocessing.cpu_count() 31 | if platform.system() == 'Linux' and sys.version_info >= (3, 4): 32 | num_threads = len(os.sched_getaffinity(0)) 33 | if not num_threads or num_threads == 0: 34 | num_threads = 4 35 | 36 | parser.set_defaults(build=False, clean=False, clean_only=False, unit_test=False, compiler=None, config='Release', cpu=None, cpp_version='11', num_threads=num_threads, tests_matching='') 37 | 38 | args = parser.parse_args() 39 | 40 | is_arm64_cpu = False 41 | if platform.machine() == 'arm64' or platform.machine() == 'aarch64': 42 | is_arm64_cpu = True 43 | 44 | # Sanitize and validate our options 45 | if args.compiler == 'android': 46 | if not args.cpu: 47 | args.cpu = 'arm64' 48 | 49 | if not platform.system() == 'Windows': 50 | print('Android is only supported on Windows') 51 | sys.exit(1) 52 | 53 | if not args.cpu in ['armv7', 'arm64']: 54 | print('{} cpu architecture not in supported list [armv7, arm64] for Android'.format(args.cpu)) 55 | sys.exit(1) 56 | elif args.compiler == 'ios': 57 | if not args.cpu: 58 | args.cpu = 'arm64' 59 | 60 | if not platform.system() == 'Darwin': 61 | print('iOS is only supported on OS X') 62 | sys.exit(1) 63 | 64 | if args.unit_test: 65 | print('Unit tests cannot run from the command line on iOS') 66 | sys.exit(1) 67 | 68 | if not args.cpu in ['arm64']: 69 | print('{} cpu architecture not in supported list [arm64] for iOS'.format(args.cpu)) 70 | sys.exit(1) 71 | elif args.compiler == 'emscripten': 72 | if not args.cpu: 73 | args.cpu = 'wasm' 74 | 75 | if not platform.system() == 'Darwin' and not platform.system() == 'Linux': 76 | print('Emscripten is only supported on OS X and Linux') 77 | sys.exit(1) 78 | 79 | if not args.cpu in ['wasm']: 80 | print('{} cpu architecture not in supported list [wasm] for Emscripten'.format(args.cpu)) 81 | sys.exit(1) 82 | else: 83 | if not args.cpu: 84 | if is_arm64_cpu: 85 | args.cpu = 'arm64' 86 | else: 87 | args.cpu = 'x64' 88 | 89 | if args.cpu == 'arm64': 90 | is_arm_supported = False 91 | 92 | # Cross compilation 93 | if args.compiler in ['vs2017', 'vs2019', 'vs2022', 'ios', 'android']: 94 | is_arm_supported = True 95 | 96 | # Native compilation 97 | if platform.system() == 'Darwin' and is_arm64_cpu: 98 | is_arm_supported = True 99 | elif platform.system() == 'Linux' and is_arm64_cpu: 100 | is_arm_supported = True 101 | 102 | if not is_arm_supported: 103 | print('arm64 is only supported with VS2017, VS2019, OS X (M1 processors), Linux, Android, and iOS') 104 | sys.exit(1) 105 | elif args.cpu == 'armv7': 106 | if not args.compiler == 'android': 107 | print('armv7 is only supported with Android') 108 | sys.exit(1) 109 | elif args.cpu == 'wasm': 110 | if not args.compiler == 'emscripten': 111 | print('wasm is only supported with Emscripten') 112 | sys.exit(1) 113 | 114 | if platform.system() == 'Darwin' and args.cpu == 'x86': 115 | result = subprocess.check_output(['xcodebuild', '-version']).decode("utf-8") 116 | if 'Xcode 11' in result: 117 | print('Versions of Xcode 11 and up no longer support x86') 118 | sys.exit(1) 119 | 120 | return args 121 | 122 | def get_generator(compiler, cpu): 123 | if compiler == None: 124 | return None 125 | 126 | if platform.system() == 'Windows': 127 | if compiler == 'vs2015': 128 | if cpu == 'x86': 129 | return 'Visual Studio 14' 130 | elif cpu == 'x64': 131 | return 'Visual Studio 14 Win64' 132 | elif compiler == 'vs2017': 133 | if cpu == 'x86': 134 | return 'Visual Studio 15' 135 | elif cpu == 'x64': 136 | return 'Visual Studio 15 Win64' 137 | elif cpu == 'arm64': 138 | # VS2017 ARM/ARM64 support only works with cmake 3.13 and up and the architecture must be specified with 139 | # the -A cmake switch 140 | return 'Visual Studio 15 2017' 141 | elif compiler == 'vs2019' or compiler == 'vs2019-clang': 142 | return 'Visual Studio 16 2019' 143 | elif compiler == 'vs2022' or compiler == 'vs2022-clang': 144 | return 'Visual Studio 17 2022' 145 | elif compiler == 'android': 146 | # For Android, we use the default generator since we don't build with CMake 147 | return None 148 | elif platform.system() == 'Darwin': 149 | if compiler == 'osx' or compiler == 'ios': 150 | return 'Xcode' 151 | elif compiler == 'emscripten': 152 | # Emscripten uses the default generator 153 | return None 154 | elif platform.system() == 'Linux': 155 | if compiler == 'emscripten': 156 | # Emscripten uses the default generator 157 | return None 158 | 159 | return 'Unix Makefiles' 160 | 161 | print('Unknown compiler: {}'.format(compiler)) 162 | print('See help with: python make.py -help') 163 | sys.exit(1) 164 | 165 | def get_architecture(compiler, cpu): 166 | if compiler == None: 167 | return None 168 | 169 | if platform.system() == 'Windows': 170 | if compiler == 'vs2017': 171 | if cpu == 'arm64': 172 | return 'ARM64' 173 | 174 | is_modern_vs = False 175 | if compiler == 'vs2019' or compiler == 'vs2019-clang': 176 | is_modern_vs = True 177 | elif compiler == 'vs2022' or compiler == 'vs2022-clang': 178 | is_modern_vs = True 179 | 180 | if is_modern_vs: 181 | if cpu == 'x86': 182 | return 'Win32' 183 | else: 184 | return cpu 185 | 186 | # This compiler/cpu pair does not need the architecture switch 187 | return None 188 | 189 | def get_toolchain(compiler, cmake_script_dir): 190 | if platform.system() == 'Windows' and compiler == 'android': 191 | return os.path.join(cmake_script_dir, 'Toolchain-Android.cmake') 192 | elif platform.system() == 'Darwin' and compiler == 'ios': 193 | return os.path.join(cmake_script_dir, 'Toolchain-iOS.cmake') 194 | 195 | # No toolchain 196 | return None 197 | 198 | def set_compiler_env(compiler, args): 199 | if platform.system() == 'Linux': 200 | if compiler == 'clang4': 201 | os.environ['CC'] = 'clang-4.0' 202 | os.environ['CXX'] = 'clang++-4.0' 203 | elif compiler == 'clang5': 204 | os.environ['CC'] = 'clang-5.0' 205 | os.environ['CXX'] = 'clang++-5.0' 206 | elif compiler == 'clang6': 207 | os.environ['CC'] = 'clang-6.0' 208 | os.environ['CXX'] = 'clang++-6.0' 209 | elif compiler == 'clang7': 210 | os.environ['CC'] = 'clang-7' 211 | os.environ['CXX'] = 'clang++-7' 212 | elif compiler == 'clang8': 213 | os.environ['CC'] = 'clang-8' 214 | os.environ['CXX'] = 'clang++-8' 215 | elif compiler == 'clang9': 216 | os.environ['CC'] = 'clang-9' 217 | os.environ['CXX'] = 'clang++-9' 218 | elif compiler == 'clang10': 219 | os.environ['CC'] = 'clang-10' 220 | os.environ['CXX'] = 'clang++-10' 221 | elif compiler == 'clang11': 222 | os.environ['CC'] = 'clang-11' 223 | os.environ['CXX'] = 'clang++-11' 224 | elif compiler == 'clang12': 225 | os.environ['CC'] = 'clang-12' 226 | os.environ['CXX'] = 'clang++-12' 227 | elif compiler == 'clang13': 228 | os.environ['CC'] = 'clang-13' 229 | os.environ['CXX'] = 'clang++-13' 230 | elif compiler == 'clang14': 231 | os.environ['CC'] = 'clang-14' 232 | os.environ['CXX'] = 'clang++-14' 233 | elif compiler == 'clang15': 234 | os.environ['CC'] = 'clang-15' 235 | os.environ['CXX'] = 'clang++-15' 236 | elif compiler == 'clang16': 237 | os.environ['CC'] = 'clang-16' 238 | os.environ['CXX'] = 'clang++-16' 239 | elif compiler == 'clang17': 240 | os.environ['CC'] = 'clang-17' 241 | os.environ['CXX'] = 'clang++-17' 242 | elif compiler == 'clang18': 243 | os.environ['CC'] = 'clang-18' 244 | os.environ['CXX'] = 'clang++-18' 245 | elif compiler == 'gcc4.8': 246 | os.environ['CC'] = 'gcc-4.8' 247 | os.environ['CXX'] = 'g++-4.8' 248 | elif compiler == 'gcc4.9': 249 | os.environ['CC'] = 'gcc-4.9' 250 | os.environ['CXX'] = 'g++-4.9' 251 | elif compiler == 'gcc5': 252 | os.environ['CC'] = 'gcc-5' 253 | os.environ['CXX'] = 'g++-5' 254 | elif compiler == 'gcc6': 255 | os.environ['CC'] = 'gcc-6' 256 | os.environ['CXX'] = 'g++-6' 257 | elif compiler == 'gcc7': 258 | os.environ['CC'] = 'gcc-7' 259 | os.environ['CXX'] = 'g++-7' 260 | elif compiler == 'gcc8': 261 | os.environ['CC'] = 'gcc-8' 262 | os.environ['CXX'] = 'g++-8' 263 | elif compiler == 'gcc9': 264 | os.environ['CC'] = 'gcc-9' 265 | os.environ['CXX'] = 'g++-9' 266 | elif compiler == 'gcc10': 267 | os.environ['CC'] = 'gcc-10' 268 | os.environ['CXX'] = 'g++-10' 269 | elif compiler == 'gcc11': 270 | os.environ['CC'] = 'gcc-11' 271 | os.environ['CXX'] = 'g++-11' 272 | elif compiler == 'gcc12': 273 | os.environ['CC'] = 'gcc-12' 274 | os.environ['CXX'] = 'g++-12' 275 | elif compiler == 'gcc13': 276 | os.environ['CC'] = 'gcc-13' 277 | os.environ['CXX'] = 'g++-13' 278 | elif compiler == 'emscripten': 279 | # Nothing to do for Emscripten 280 | return 281 | else: 282 | print('Unknown compiler: {}'.format(compiler)) 283 | print('See help with: python make.py -help') 284 | sys.exit(1) 285 | 286 | def do_generate_solution(build_dir, cmake_script_dir, args): 287 | compiler = args.compiler 288 | cpu = args.cpu 289 | config = args.config 290 | 291 | if compiler: 292 | set_compiler_env(compiler, args) 293 | 294 | extra_switches = ['--no-warn-unused-cli'] 295 | extra_switches.append('-DCPU_INSTRUCTION_SET:STRING={}'.format(cpu)) 296 | extra_switches.append('-DCMAKE_CXX_STANDARD:STRING={}'.format(args.cpp_version)) 297 | 298 | if platform.system() == 'Windows': 299 | if os.path.sep == '\\': 300 | # Native Windows 301 | extra_switches 302 | else: 303 | # MSYS2 or Cygwin 304 | extra_switches.append('-DCMAKE_BUILD_TYPE={}'.format(config.upper())) 305 | else: 306 | extra_switches.append('-DCMAKE_BUILD_TYPE={}'.format(config.upper())) 307 | 308 | if platform.system() == 'Darwin' and compiler == 'ios' and args.ci: 309 | # Disable code signing for CI iOS builds since we just test compilation 310 | extra_switches.append('-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=NO') 311 | extra_switches.append('-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=NO') 312 | 313 | toolchain = get_toolchain(compiler, cmake_script_dir) 314 | if toolchain: 315 | extra_switches.append('-DCMAKE_TOOLCHAIN_FILE={}'.format(toolchain)) 316 | 317 | # Generate IDE solution 318 | print('Generating build files ...') 319 | if compiler == 'emscripten': 320 | cmake_cmd = 'emcmake cmake .. -DCMAKE_INSTALL_PREFIX="{}" {}'.format(build_dir, ' '.join(extra_switches)) 321 | else: 322 | cmake_generator = get_generator(compiler, cpu) 323 | if not cmake_generator: 324 | print('Using default generator') 325 | else: 326 | generator_suffix = '' 327 | if compiler == 'vs2019-clang' or compiler == 'vs2022-clang': 328 | extra_switches.append('-T ClangCL') 329 | generator_suffix = 'Clang CL' 330 | 331 | print('Using generator: {} {}'.format(cmake_generator, generator_suffix)) 332 | extra_switches.append('-G "{}"'.format(cmake_generator)) 333 | 334 | cmake_arch = get_architecture(compiler, cpu) 335 | if cmake_arch: 336 | print('Using architecture: {}'.format(cmake_arch)) 337 | extra_switches.append('-A {}'.format(cmake_arch)) 338 | 339 | cmake_cmd = 'cmake .. -DCMAKE_INSTALL_PREFIX="{}" {}'.format(build_dir, ' '.join(extra_switches)) 340 | 341 | result = subprocess.call(cmake_cmd, shell=True) 342 | if result != 0: 343 | sys.exit(result) 344 | 345 | def do_build(args): 346 | config = args.config 347 | 348 | print('Building ...') 349 | cmake_cmd = 'cmake --build .' 350 | if platform.system() == 'Windows': 351 | if args.compiler == 'android': 352 | cmake_cmd += ' --config {}'.format(config) 353 | else: 354 | if os.path.sep == '\\': 355 | # Native Windows 356 | cmake_cmd += ' --config {} --target INSTALL'.format(config) 357 | else: 358 | # MSYS2 or Cygwin 359 | cmake_cmd += ' --config {} --target install'.format(config) 360 | elif platform.system() == 'Darwin': 361 | if args.compiler == 'ios': 362 | cmake_cmd += ' --config {}'.format(config) 363 | else: 364 | cmake_cmd += ' --config {} --target install'.format(config) 365 | else: 366 | cmake_cmd += ' --target install' 367 | 368 | result = subprocess.call(cmake_cmd, shell=True) 369 | if result != 0: 370 | sys.exit(result) 371 | 372 | def do_tests_android(build_dir, args): 373 | # Switch our working directory to where we built everything 374 | working_dir = os.path.join(build_dir, 'tests', 'main_android') 375 | os.chdir(working_dir) 376 | 377 | gradlew_exe = os.path.join(build_dir, '..', 'tests', 'main_android', 'gradlew.bat') 378 | 379 | # We uninstall first and then install 380 | if args.config == 'Debug': 381 | install_cmd = 'uninstallAll installDebug' 382 | elif args.config == 'Release': 383 | install_cmd = 'uninstallAll installRelease' 384 | 385 | # Install our app 386 | test_cmd = '"{}" {}'.format(gradlew_exe, install_cmd) 387 | result = subprocess.call(test_cmd, shell=True) 388 | if result != 0: 389 | sys.exit(result) 390 | 391 | # Execute through ADB 392 | run_cmd = 'adb shell am start -n "com.sjson.unit_tests/com.sjson.unit_tests.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER' 393 | result = subprocess.call(run_cmd, shell=True) 394 | if result != 0: 395 | sys.exit(result) 396 | 397 | # Restore working directory 398 | os.chdir(build_dir) 399 | 400 | def do_tests_cmake(args): 401 | ctest_cmd = 'ctest --output-on-failure --parallel {}'.format(args.num_threads) 402 | 403 | if platform.system() == 'Windows' or platform.system() == 'Darwin': 404 | ctest_cmd += ' -C {}'.format(args.config) 405 | if args.tests_matching: 406 | ctest_cmd += ' --tests-regex {}'.format(args.tests_matching) 407 | 408 | result = subprocess.call(ctest_cmd, shell=True) 409 | if result != 0: 410 | sys.exit(result) 411 | 412 | def do_tests(build_dir, args): 413 | print('Running unit tests ...') 414 | 415 | if args.compiler == 'android': 416 | do_tests_android(build_dir, args) 417 | else: 418 | do_tests_cmake(args) 419 | 420 | if __name__ == "__main__": 421 | args = parse_argv() 422 | 423 | build_dir = os.path.join(os.getcwd(), 'build') 424 | cmake_script_dir = os.path.join(os.getcwd(), 'cmake') 425 | 426 | is_clean_requested = args.clean or args.clean_only 427 | if is_clean_requested and os.path.exists(build_dir): 428 | print('Cleaning previous build ...') 429 | shutil.rmtree(build_dir) 430 | 431 | if args.clean_only: 432 | sys.exit(0) 433 | 434 | if not os.path.exists(build_dir): 435 | os.makedirs(build_dir) 436 | 437 | os.chdir(build_dir) 438 | 439 | print('Using config: {}'.format(args.config)) 440 | print('Using cpu: {}'.format(args.cpu)) 441 | if args.compiler: 442 | print('Using compiler: {}'.format(args.compiler)) 443 | print('Using C++-{}'.format(args.cpp_version)) 444 | print('Using {} threads'.format(args.num_threads)) 445 | 446 | # Make sure 'make' runs with all available cores 447 | os.environ['MAKEFLAGS'] = '-j{}'.format(args.num_threads) 448 | 449 | do_generate_solution(build_dir, cmake_script_dir, args) 450 | 451 | if args.build: 452 | do_build(args) 453 | 454 | if args.unit_test: 455 | do_tests(build_dir, args) 456 | 457 | sys.exit(0) 458 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.organization=nfrechette-github 2 | 3 | sonar.projectKey=nfrechette_sjson-cpp 4 | sonar.projectName=sjson-cpp 5 | sonar.projectVersion=0.9.99 6 | 7 | sonar.sources=includes,tests/sources,tests/main_generic 8 | 9 | sonar.cfamily.build-wrapper-output=bw_output 10 | sonar.cfamily.threads=2 11 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.2) 2 | project(sjson-cpp_unit_tests_root NONE) 3 | 4 | if(PLATFORM_ANDROID) 5 | add_subdirectory("${PROJECT_SOURCE_DIR}/main_android") 6 | elseif(PLATFORM_IOS) 7 | add_subdirectory("${PROJECT_SOURCE_DIR}/main_ios") 8 | elseif(PLATFORM_EMSCRIPTEN) 9 | add_subdirectory("${PROJECT_SOURCE_DIR}/main_emscripten") 10 | else() 11 | add_subdirectory("${PROJECT_SOURCE_DIR}/main_generic") 12 | add_subdirectory("${PROJECT_SOURCE_DIR}/validate_includes") 13 | endif() 14 | -------------------------------------------------------------------------------- /tests/main_android/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(sjson-cpp_unit_tests_gradle_shim NONE) 3 | 4 | # Set our project root since our gradle files used to build live in the binary output directory 5 | # but the actual source files live in the source/current directory. 6 | set(SJSON_PROJECT_ROOT ${CMAKE_CURRENT_BINARY_DIR}/app) 7 | file(RELATIVE_PATH SJSON_PROJECT_ROOT ${SJSON_PROJECT_ROOT} ${PROJECT_SOURCE_DIR}/app) 8 | 9 | # Configure gradle for our build configuration 10 | configure_file(app/build.gradle.in app/build.gradle @ONLY) 11 | 12 | # Copy gradle related files 13 | file(COPY build.gradle gradle.properties settings.gradle gradle DESTINATION .) 14 | 15 | add_custom_target(${PROJECT_NAME} ALL 16 | COMMAND "${PROJECT_SOURCE_DIR}/gradlew.bat" 17 | # Decide whether we should build Debug or Release 18 | $<$:assembleDebug> 19 | $<$:assembleRelease>) 20 | -------------------------------------------------------------------------------- /tests/main_android/app/build.gradle.in: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | ndkVersion "20.1.5948944" 5 | compileSdkVersion 28 6 | defaultConfig { 7 | applicationId "com.sjson.unit_tests" 8 | minSdkVersion 24 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | externalNativeBuild { 14 | cmake { 15 | arguments '-DCMAKE_CXX_STANDARD=@CMAKE_CXX_STANDARD@' 16 | } 17 | } 18 | ndk { 19 | abiFilters '@CPU_INSTRUCTION_SET@' 20 | } 21 | 22 | } 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 27 | debuggable = true 28 | signingConfig signingConfigs.debug 29 | externalNativeBuild { 30 | cmake { 31 | // Because we want to debug 'release' builds, the 'debuggable' property above 32 | // forces the build configuration to 'Debug' which disables optimizations. 33 | // Force a release configutation anyway. 34 | arguments '-DCMAKE_BUILD_TYPE=Release' 35 | } 36 | } 37 | } 38 | } 39 | externalNativeBuild { 40 | cmake { 41 | path '@SJSON_PROJECT_ROOT@/src/main/cpp/CMakeLists.txt' 42 | } 43 | } 44 | sourceSets { 45 | main { 46 | manifest.srcFile '@SJSON_PROJECT_ROOT@/src/main/AndroidManifest.xml' 47 | java.srcDirs = ['@SJSON_PROJECT_ROOT@/src/main/java'] 48 | res.srcDirs = ['@SJSON_PROJECT_ROOT@/src/main/res'] 49 | } 50 | } 51 | } 52 | 53 | dependencies { 54 | implementation fileTree(dir: 'libs', include: ['*.jar']) 55 | implementation 'androidx.appcompat:appcompat:1.0.2' 56 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 57 | } 58 | -------------------------------------------------------------------------------- /tests/main_android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /tests/main_android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /tests/main_android/app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(sjson-cpp_unit_tests CXX) 3 | 4 | # Project root is \tests\main_android 5 | set(PROJECT_ROOT_DIR "${PROJECT_SOURCE_DIR}/../../../..") 6 | 7 | include_directories("${PROJECT_ROOT_DIR}/../../includes") 8 | include_directories("${PROJECT_ROOT_DIR}/../../external/catch2/single_include") 9 | include_directories("${PROJECT_ROOT_DIR}/../sources") 10 | 11 | # Grab all of our test source files 12 | file(GLOB_RECURSE ALL_TEST_SOURCE_FILES LIST_DIRECTORIES false 13 | ${PROJECT_ROOT_DIR}/../sources/*.h 14 | ${PROJECT_ROOT_DIR}/../sources/*.cpp) 15 | 16 | # Grab all of our main source files 17 | file(GLOB_RECURSE ALL_MAIN_SOURCE_FILES LIST_DIRECTORIES false 18 | ${PROJECT_SOURCE_DIR}/*.cpp) 19 | 20 | add_library(${PROJECT_NAME} SHARED ${ALL_TEST_SOURCE_FILES} ${ALL_MAIN_SOURCE_FILES}) 21 | 22 | # Enable exceptions 23 | target_compile_options(${PROJECT_NAME} PRIVATE -fexceptions) 24 | 25 | # Enable debug symbols 26 | target_compile_options(${PROJECT_NAME} PRIVATE -g) 27 | 28 | # Throw on failure to allow us to catch them and recover 29 | add_definitions(-DSJSON_CPP_ON_ASSERT_THROW) 30 | 31 | target_include_directories(${PROJECT_NAME} PUBLIC jni) 32 | 33 | target_link_libraries(${PROJECT_NAME} m log) 34 | -------------------------------------------------------------------------------- /tests/main_android/app/src/main/cpp/main.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2018 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | #define CATCH_CONFIG_RUNNER 26 | #include "catch2.impl.h" 27 | 28 | #include 29 | 30 | extern "C" jint Java_com_sjson_unit_1tests_MainActivity_getNumUnitTestCases(JNIEnv* env, jobject caller) 31 | { 32 | return Catch::getRegistryHub().getTestCaseRegistry().getAllTests().size(); 33 | } 34 | 35 | extern "C" jint Java_com_sjson_unit_1tests_MainActivity_runUnitTests(JNIEnv* env, jobject caller) 36 | { 37 | int result = Catch::Session().run(); 38 | 39 | return (result < 0xff ? result : 0xff); 40 | } 41 | -------------------------------------------------------------------------------- /tests/main_android/app/src/main/java/com/sjson/unit_tests/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.sjson.unit_tests; 2 | 3 | import android.app.Activity; 4 | import android.widget.TextView; 5 | import android.os.Bundle; 6 | 7 | public class MainActivity extends Activity { 8 | static { 9 | System.loadLibrary("sjson-cpp_unit_tests"); 10 | } 11 | 12 | @Override 13 | public void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | 16 | TextView resultTextView = new TextView(this); 17 | 18 | int numUnitTestCases = getNumUnitTestCases(); 19 | int numFailed = runUnitTests(); 20 | 21 | if (numFailed == 0) 22 | resultTextView.setText("All " + numUnitTestCases + " test cases ran successfully!"); 23 | else 24 | resultTextView.setText(numFailed + " test cases failed!"); 25 | 26 | setContentView(resultTextView); 27 | } 28 | 29 | public native int getNumUnitTestCases(); 30 | public native int runUnitTests(); 31 | } 32 | -------------------------------------------------------------------------------- /tests/main_android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | sjson-cpp Unit Tests 4 | 5 | -------------------------------------------------------------------------------- /tests/main_android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.3' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | google() 19 | jcenter() 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } 26 | -------------------------------------------------------------------------------- /tests/main_android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | 21 | # Run tasks in parallel 22 | org.gradle.parallel=true 23 | -------------------------------------------------------------------------------- /tests/main_android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nfrechette/sjson-cpp/c9490dd1ccd09bc14ee3f3e1517e8171856abd13/tests/main_android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /tests/main_android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Mar 27 21:02:15 EDT 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip 7 | -------------------------------------------------------------------------------- /tests/main_android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /tests/main_android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /tests/main_android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | rootProject.name='sjson-cpp Unit Tests' 3 | -------------------------------------------------------------------------------- /tests/main_emscripten/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.2) 2 | project(sjson-cpp_unit_tests CXX) 3 | 4 | include_directories("${PROJECT_SOURCE_DIR}/../../includes") 5 | include_directories("${PROJECT_SOURCE_DIR}/../../external/catch2/single_include") 6 | include_directories("${PROJECT_SOURCE_DIR}/../sources") 7 | 8 | # Grab all of our test source files 9 | file(GLOB_RECURSE ALL_TEST_SOURCE_FILES LIST_DIRECTORIES false 10 | ${PROJECT_SOURCE_DIR}/../sources/*.h 11 | ${PROJECT_SOURCE_DIR}/../sources/*.cpp) 12 | 13 | # Grab all of our main source files 14 | file(GLOB_RECURSE ALL_MAIN_SOURCE_FILES LIST_DIRECTORIES false 15 | ${PROJECT_SOURCE_DIR}/*.cpp) 16 | 17 | add_executable(${PROJECT_NAME} ${ALL_TEST_SOURCE_FILES} ${ALL_MAIN_SOURCE_FILES}) 18 | 19 | # Throw on failure to allow us to catch them and recover 20 | add_definitions(-DSJSON_CPP_ON_ASSERT_THROW) 21 | 22 | target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra) # Enable all warnings 23 | target_compile_options(${PROJECT_NAME} PRIVATE -Wshadow) # Enable shadowing warnings 24 | target_compile_options(${PROJECT_NAME} PRIVATE -Werror) # Treat warnings as errors 25 | 26 | # Exceptions are not enabled by default, enable them 27 | target_compile_options(${PROJECT_NAME} PRIVATE -fexceptions) 28 | target_link_libraries(${PROJECT_NAME} PRIVATE "-s DISABLE_EXCEPTION_CATCHING=0") 29 | 30 | target_link_libraries(${PROJECT_NAME} PRIVATE "-s NODERAWFS=1") # Enable the raw node file system 31 | target_link_libraries(${PROJECT_NAME} PRIVATE -lnodefs.js) # Link the node file system 32 | 33 | target_link_libraries(${PROJECT_NAME} PRIVATE "-s ENVIRONMENT=node") # Force the environment to node 34 | 35 | target_link_libraries(${PROJECT_NAME} PRIVATE "-s ALLOW_MEMORY_GROWTH=1") # Allow dynamic memory allocation 36 | 37 | # Setup Catch2 so we can find and execute the unit tests with CTest 38 | set(OptionalCatchTestLauncher node) 39 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../../external/catch2/contrib") 40 | include(CTest) 41 | include(ParseAndAddCatchTests) 42 | ParseAndAddCatchTests(${PROJECT_NAME}) 43 | 44 | install(FILES 45 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.js 46 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.wasm 47 | DESTINATION bin) 48 | -------------------------------------------------------------------------------- /tests/main_emscripten/main.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2020 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | #define CATCH_CONFIG_RUNNER 26 | #include "catch2.impl.h" 27 | 28 | int main(int argc, char* argv[]) 29 | { 30 | int result = Catch::Session().run(argc, argv); 31 | return (result < 0xff ? result : 0xff); 32 | } 33 | -------------------------------------------------------------------------------- /tests/main_generic/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.2) 2 | project(sjson-cpp_unit_tests CXX) 3 | 4 | include_directories("${PROJECT_SOURCE_DIR}/../../includes") 5 | include_directories("${PROJECT_SOURCE_DIR}/../../external/catch2/single_include") 6 | include_directories("${PROJECT_SOURCE_DIR}/../sources") 7 | 8 | # Grab all of our test source files 9 | file(GLOB_RECURSE ALL_TEST_SOURCE_FILES LIST_DIRECTORIES false 10 | ${PROJECT_SOURCE_DIR}/../sources/*.h 11 | ${PROJECT_SOURCE_DIR}/../sources/*.cpp) 12 | 13 | create_source_groups("${ALL_TEST_SOURCE_FILES}" ${PROJECT_SOURCE_DIR}/..) 14 | 15 | # Grab all of our main source files 16 | file(GLOB_RECURSE ALL_MAIN_SOURCE_FILES LIST_DIRECTORIES false 17 | ${PROJECT_SOURCE_DIR}/*.cpp) 18 | 19 | create_source_groups("${ALL_MAIN_SOURCE_FILES}" ${PROJECT_SOURCE_DIR}) 20 | 21 | add_executable(${PROJECT_NAME} ${ALL_TEST_SOURCE_FILES} ${ALL_MAIN_SOURCE_FILES}) 22 | 23 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../../external/catch2/contrib") 24 | include(CTest) 25 | include(Catch) 26 | catch_discover_tests(${PROJECT_NAME}) 27 | 28 | setup_default_compiler_flags(${PROJECT_NAME}) 29 | 30 | if(MSVC) 31 | if(CPU_INSTRUCTION_SET MATCHES "arm64") 32 | # Exceptions are not enabled by default for ARM targets, enable them 33 | target_compile_options(${PROJECT_NAME} PRIVATE /EHsc) 34 | endif() 35 | 36 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 37 | target_compile_options(${PROJECT_NAME} PRIVATE -Wno-float-equal) # Float comparison 38 | endif() 39 | endif() 40 | 41 | # Throw on failure to allow us to catch them and recover 42 | add_definitions(-DSJSON_CPP_ON_ASSERT_THROW) 43 | 44 | install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin) 45 | -------------------------------------------------------------------------------- /tests/main_generic/main.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2018 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | #define CATCH_CONFIG_RUNNER 26 | #include "catch2.impl.h" 27 | 28 | #ifdef _WIN32 29 | #include 30 | #endif 31 | 32 | int main(int argc, char* argv[]) 33 | { 34 | int result = Catch::Session().run(argc, argv); 35 | 36 | #ifdef _WIN32 37 | if (IsDebuggerPresent()) 38 | { 39 | printf("Press any key to continue...\n"); 40 | while (_kbhit() == 0); 41 | } 42 | #endif 43 | 44 | return (result < 0xff ? result : 0xff); 45 | } 46 | -------------------------------------------------------------------------------- /tests/main_ios/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.2) 2 | project(sjson-cpp_unit_tests) 3 | 4 | # iOS cmake toolchain does not support CMAKE_CXX_STANDARD 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++${CMAKE_CXX_STANDARD}") 6 | 7 | # Force enable debug symbols 8 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") 9 | 10 | # Enable optimizations in Release 11 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") 12 | 13 | set(MACOSX_BUNDLE_EXECUTABLE_NAME ${PROJECT_NAME}) 14 | set(MACOSX_BUNDLE_INFO_STRING "com.sjson.cpp.unit-tests") 15 | set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.sjson.cpp.unit-tests") 16 | set(MACOSX_BUNDLE_BUNDLE_NAME "sjson-cpp-unit-tests") 17 | 18 | include_directories("${PROJECT_SOURCE_DIR}/../../includes") 19 | include_directories("${PROJECT_SOURCE_DIR}/../../external/catch2/single_include") 20 | include_directories("${PROJECT_SOURCE_DIR}/../sources") 21 | 22 | # Grab all of our test source files 23 | file(GLOB_RECURSE ALL_TEST_SOURCE_FILES LIST_DIRECTORIES false 24 | ${PROJECT_SOURCE_DIR}/../sources/*.h 25 | ${PROJECT_SOURCE_DIR}/../sources/*.cpp) 26 | 27 | create_source_groups("${ALL_TEST_SOURCE_FILES}" ${PROJECT_SOURCE_DIR}/..) 28 | 29 | # Grab all of our main source files 30 | file(GLOB_RECURSE ALL_MAIN_SOURCE_FILES LIST_DIRECTORIES false 31 | ${PROJECT_SOURCE_DIR}/*.cpp) 32 | 33 | create_source_groups("${ALL_MAIN_SOURCE_FILES}" ${PROJECT_SOURCE_DIR}) 34 | 35 | add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${ALL_TEST_SOURCE_FILES} ${ALL_MAIN_SOURCE_FILES}) 36 | 37 | # Throw on failure to allow us to catch them and recover 38 | add_definitions(-DSJSON_CPP_ON_ASSERT_THROW) 39 | 40 | # Set XCode properties 41 | set_property(TARGET ${PROJECT_NAME} PROPERTY XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.sjson.cpp.unit-tests") 42 | -------------------------------------------------------------------------------- /tests/main_ios/main.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2018 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | #define CATCH_CONFIG_RUNNER 26 | #include "catch2.impl.h" 27 | 28 | int main(int argc, char* argv[]) 29 | { 30 | int result = Catch::Session().run(argc, argv); 31 | 32 | return (result < 0xff ? result : 0xff); 33 | } 34 | -------------------------------------------------------------------------------- /tests/sources/catch2.impl.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2023 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | // We disable various informational warnings with using /Wall with MSVC caused by Catch2 26 | 27 | #if defined(_MSC_VER) && !defined(__clang__) 28 | #pragma warning(push) 29 | #pragma warning(disable : 4365) // Signed/unsigned mismatch 30 | #pragma warning(disable : 4388) // Signed/unsigned comparison 31 | #pragma warning(disable : 4583) // Destructor is not implicitly called 32 | #pragma warning(disable : 4623) // Default constructor implicitly deleted 33 | #pragma warning(disable : 4625) // Copy constructor implicitly deleted 34 | #pragma warning(disable : 4626) // Copy assignment operator implicitly deleted 35 | #pragma warning(disable : 4868) // May not enforce left to right order in initializer 36 | #pragma warning(disable : 5026) // Move constructor implicitly deleted 37 | #pragma warning(disable : 5027) // Move assignment operator implicitly deleted 38 | #pragma warning(disable : 5039) // Pointer to potentially throwing function passed to extern C 39 | #pragma warning(disable : 5204) // Class has virtual functions but no virtual destructor 40 | #pragma warning(disable : 5219) // Implicit conversion, possible loss of data 41 | #pragma warning(disable : 5267) // Implicit copy constructor deprecated due to user destructor 42 | #endif 43 | 44 | #include 45 | 46 | #if defined(_MSC_VER) && !defined(__clang__) 47 | #pragma warning(pop) 48 | #endif 49 | -------------------------------------------------------------------------------- /tests/sources/test_header_fwd.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2022 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | #include 26 | -------------------------------------------------------------------------------- /tests/sources/test_parser.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2018 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | #include "catch2.impl.h" 26 | 27 | #include 28 | 29 | using namespace sjson; 30 | 31 | static Parser parser_from_c_str(const char* c_str) 32 | { 33 | return Parser(c_str, c_str != nullptr ? std::strlen(c_str) : 0); 34 | } 35 | 36 | TEST_CASE("Parser Misc", "[parser]") 37 | { 38 | { 39 | Parser parser = parser_from_c_str(""); 40 | CHECK(parser.eof()); 41 | CHECK(parser.is_valid()); 42 | } 43 | 44 | { 45 | Parser parser = parser_from_c_str(""); 46 | CHECK(parser.remainder_is_comments_and_whitespace()); 47 | CHECK(parser.eof()); 48 | CHECK(parser.is_valid()); 49 | } 50 | 51 | { 52 | Parser parser = parser_from_c_str(" "); 53 | CHECK(parser.remainder_is_comments_and_whitespace()); 54 | CHECK(parser.eof()); 55 | CHECK(parser.is_valid()); 56 | } 57 | 58 | { 59 | Parser parser = parser_from_c_str("// lol \\n "); 60 | CHECK(parser.remainder_is_comments_and_whitespace()); 61 | CHECK(parser.eof()); 62 | CHECK(parser.is_valid()); 63 | } 64 | 65 | { 66 | Parser parser = parser_from_c_str("\"key-one\" = true"); 67 | bool value = false; 68 | CHECK(parser.read("key-one", value)); 69 | CHECK(value == true); 70 | CHECK(parser.eof()); 71 | CHECK(parser.is_valid()); 72 | } 73 | 74 | { 75 | Parser parser = parser_from_c_str("key = /* bar */ true"); 76 | bool value = false; 77 | CHECK(parser.read("key", value)); 78 | CHECK(value == true); 79 | CHECK(parser.eof()); 80 | CHECK(parser.is_valid()); 81 | } 82 | 83 | { 84 | Parser parser = parser_from_c_str("key = /* bar * true"); 85 | bool value = false; 86 | CHECK_FALSE(parser.read("key", value)); 87 | CHECK_FALSE(parser.is_valid()); 88 | } 89 | 90 | { 91 | Parser parser = parser_from_c_str("key = // bar \ntrue"); 92 | bool value = false; 93 | CHECK(parser.read("key", value)); 94 | CHECK(value == true); 95 | CHECK(parser.eof()); 96 | CHECK(parser.is_valid()); 97 | } 98 | 99 | { 100 | Parser parser = parser_from_c_str("key /* bar */ = true"); 101 | bool value = false; 102 | CHECK(parser.read("key", value)); 103 | CHECK(value == true); 104 | CHECK(parser.eof()); 105 | CHECK(parser.is_valid()); 106 | } 107 | 108 | { 109 | Parser parser = parser_from_c_str("/* bar */ key = true"); 110 | bool value = false; 111 | CHECK(parser.read("key", value)); 112 | CHECK(value == true); 113 | CHECK(parser.eof()); 114 | CHECK(parser.is_valid()); 115 | } 116 | } 117 | 118 | TEST_CASE("Parser Bool Reading", "[parser]") 119 | { 120 | { 121 | Parser parser = parser_from_c_str("key = true"); 122 | bool value = false; 123 | CHECK(parser.read("key", value)); 124 | CHECK(value == true); 125 | CHECK(parser.eof()); 126 | CHECK(parser.is_valid()); 127 | } 128 | 129 | { 130 | Parser parser = parser_from_c_str("key = false"); 131 | bool value = true; 132 | CHECK(parser.read("key", value)); 133 | CHECK(value == false); 134 | CHECK(parser.eof()); 135 | CHECK(parser.is_valid()); 136 | } 137 | 138 | { 139 | Parser parser = parser_from_c_str("bad_key = 0"); 140 | bool value = true; 141 | CHECK_FALSE(parser.try_read("key", value, false)); 142 | CHECK(value == false); 143 | CHECK_FALSE(parser.eof()); 144 | CHECK(parser.is_valid()); 145 | } 146 | 147 | { 148 | Parser parser = parser_from_c_str("key = true"); 149 | bool value = false; 150 | CHECK(parser.try_read("key", value, false)); 151 | CHECK(value == true); 152 | CHECK(parser.eof()); 153 | CHECK(parser.is_valid()); 154 | } 155 | } 156 | 157 | TEST_CASE("Parser String Reading", "[parser]") 158 | { 159 | { 160 | Parser parser = parser_from_c_str("key = \"Quoted string\""); 161 | StringView value; 162 | CHECK(parser.read("key", value)); 163 | CHECK(value == "Quoted string"); 164 | CHECK(parser.eof()); 165 | CHECK(parser.is_valid()); 166 | } 167 | 168 | { 169 | // Note: Escaped quotes \" are left escaped within the StringView because we do not allocate memory 170 | Parser parser = parser_from_c_str("key = \"Quoted \\\" string\""); 171 | StringView value; 172 | CHECK(parser.read("key", value)); 173 | CHECK(value == "Quoted \\\" string"); 174 | CHECK(parser.eof()); 175 | CHECK(parser.is_valid()); 176 | } 177 | 178 | { 179 | Parser parser = parser_from_c_str("key = \"New\\nline\""); 180 | StringView value; 181 | CHECK(parser.read("key", value)); 182 | CHECK(value == "New\\nline"); 183 | CHECK(parser.eof()); 184 | CHECK(parser.is_valid()); 185 | } 186 | 187 | { 188 | Parser parser = parser_from_c_str("key = \"Tab\\tulator\""); 189 | StringView value; 190 | CHECK(parser.read("key", value)); 191 | CHECK(value == "Tab\\tulator"); 192 | CHECK(parser.eof()); 193 | CHECK(parser.is_valid()); 194 | } 195 | 196 | { 197 | Parser parser = parser_from_c_str("key = \"Tab\\tulator\""); 198 | StringView value; 199 | CHECK(parser.read("key", value)); 200 | CHECK(value == "Tab\\tulator"); 201 | CHECK(parser.eof()); 202 | CHECK(parser.is_valid()); 203 | } 204 | 205 | { 206 | Parser parser = parser_from_c_str("bad_key = 0"); 207 | StringView value; 208 | CHECK_FALSE(parser.try_read("key", value, "default")); 209 | CHECK(value == "default"); 210 | CHECK_FALSE(parser.eof()); 211 | CHECK(parser.is_valid()); 212 | } 213 | 214 | { 215 | Parser parser = parser_from_c_str("key = \"good\""); 216 | StringView value; 217 | CHECK(parser.try_read("key", value, "default")); 218 | CHECK(value == "good"); 219 | CHECK(parser.eof()); 220 | CHECK(parser.is_valid()); 221 | } 222 | 223 | { 224 | Parser parser = parser_from_c_str("key = \"bad"); 225 | StringView value; 226 | CHECK_FALSE(parser.read("key", value)); 227 | CHECK_FALSE(parser.is_valid()); 228 | } 229 | 230 | { 231 | Parser parser = parser_from_c_str("key = bad"); 232 | StringView value; 233 | CHECK_FALSE(parser.read("key", value)); 234 | CHECK_FALSE(parser.is_valid()); 235 | } 236 | } 237 | 238 | TEST_CASE("Parser Number Reading", "[parser]") 239 | { 240 | // Number reading 241 | { 242 | Parser parser = parser_from_c_str("key = 123.456789"); 243 | double value = 0.0; 244 | CHECK(parser.read("key", value)); 245 | CHECK(value == 123.456789); 246 | CHECK(parser.eof()); 247 | CHECK(parser.is_valid()); 248 | } 249 | 250 | { 251 | Parser parser = parser_from_c_str("key = \"nan\""); 252 | double value = 0.0; 253 | CHECK(parser.read("key", value)); 254 | CHECK(std::isnan(value)); 255 | CHECK(parser.eof()); 256 | CHECK(parser.is_valid()); 257 | } 258 | 259 | { 260 | Parser parser = parser_from_c_str("key = \"inf\""); 261 | double value = 0.0; 262 | CHECK(parser.read("key", value)); 263 | CHECK(std::isinf(value)); 264 | CHECK(value > 0.0); 265 | CHECK(parser.eof()); 266 | CHECK(parser.is_valid()); 267 | } 268 | 269 | { 270 | Parser parser = parser_from_c_str("key = \"-inf\""); 271 | double value = 0.0; 272 | CHECK(parser.read("key", value)); 273 | CHECK(std::isinf(value)); 274 | CHECK(value < 0.0); 275 | CHECK(parser.eof()); 276 | CHECK(parser.is_valid()); 277 | } 278 | 279 | { 280 | Parser parser = parser_from_c_str("key = 123.456789"); 281 | float value = 0.0F; 282 | CHECK(parser.read("key", value)); 283 | CHECK(value == 123.456789F); 284 | CHECK(parser.eof()); 285 | CHECK(parser.is_valid()); 286 | } 287 | 288 | { 289 | Parser parser = parser_from_c_str("key = \"nan\""); 290 | float value = 0.0F; 291 | CHECK(parser.read("key", value)); 292 | CHECK(std::isnan(value)); 293 | CHECK(parser.eof()); 294 | CHECK(parser.is_valid()); 295 | } 296 | 297 | { 298 | Parser parser = parser_from_c_str("key = \"inf\""); 299 | float value = 0.0F; 300 | CHECK(parser.read("key", value)); 301 | CHECK(std::isinf(value)); 302 | CHECK(value > 0.0F); 303 | CHECK(parser.eof()); 304 | CHECK(parser.is_valid()); 305 | } 306 | 307 | { 308 | Parser parser = parser_from_c_str("key = \"-inf\""); 309 | float value = 0.0F; 310 | CHECK(parser.read("key", value)); 311 | CHECK(std::isinf(value)); 312 | CHECK(value < 0.0F); 313 | CHECK(parser.eof()); 314 | CHECK(parser.is_valid()); 315 | } 316 | 317 | { 318 | Parser parser = parser_from_c_str("key = -123"); 319 | int8_t value = 0; 320 | CHECK(parser.read("key", value)); 321 | CHECK(value == -123); 322 | CHECK(parser.eof()); 323 | CHECK(parser.is_valid()); 324 | } 325 | 326 | { 327 | Parser parser = parser_from_c_str("key = 123"); 328 | uint8_t value = 0; 329 | CHECK(parser.read("key", value)); 330 | CHECK(value == 123); 331 | CHECK(parser.eof()); 332 | CHECK(parser.is_valid()); 333 | } 334 | 335 | { 336 | Parser parser = parser_from_c_str("key = -1234"); 337 | int16_t value = 0; 338 | CHECK(parser.read("key", value)); 339 | CHECK(value == -1234); 340 | CHECK(parser.eof()); 341 | CHECK(parser.is_valid()); 342 | } 343 | 344 | { 345 | Parser parser = parser_from_c_str("key = 1234"); 346 | uint16_t value = 0; 347 | CHECK(parser.read("key", value)); 348 | CHECK(value == 1234); 349 | CHECK(parser.eof()); 350 | CHECK(parser.is_valid()); 351 | } 352 | 353 | { 354 | Parser parser = parser_from_c_str("key = -123456"); 355 | int32_t value = 0; 356 | CHECK(parser.read("key", value)); 357 | CHECK(value == -123456); 358 | CHECK(parser.eof()); 359 | CHECK(parser.is_valid()); 360 | } 361 | 362 | { 363 | Parser parser = parser_from_c_str("key = 123456"); 364 | uint32_t value = 0; 365 | CHECK(parser.read("key", value)); 366 | CHECK(value == 123456); 367 | CHECK(parser.eof()); 368 | CHECK(parser.is_valid()); 369 | } 370 | 371 | { 372 | Parser parser = parser_from_c_str("key = -1234567890123456"); 373 | int64_t value = 0; 374 | CHECK(parser.read("key", value)); 375 | CHECK(value == -1234567890123456LL); 376 | CHECK(parser.eof()); 377 | CHECK(parser.is_valid()); 378 | } 379 | 380 | { 381 | Parser parser = parser_from_c_str("key = 1234567890123456"); 382 | uint64_t value = 0; 383 | CHECK(parser.read("key", value)); 384 | CHECK(value == 1234567890123456ULL); 385 | CHECK(parser.eof()); 386 | CHECK(parser.is_valid()); 387 | } 388 | 389 | // Number try reading 390 | 391 | { 392 | Parser parser = parser_from_c_str("bad_key = \"bad\""); 393 | double value = 0.0; 394 | CHECK_FALSE(parser.try_read("key", value, 1.0)); 395 | CHECK(value == 1.0); 396 | CHECK_FALSE(parser.eof()); 397 | CHECK(parser.is_valid()); 398 | } 399 | 400 | { 401 | Parser parser = parser_from_c_str("key = 2.0"); 402 | double value = 0.0; 403 | CHECK(parser.try_read("key", value, 1.0)); 404 | CHECK(value == 2.0); 405 | CHECK(parser.eof()); 406 | CHECK(parser.is_valid()); 407 | } 408 | 409 | { 410 | Parser parser = parser_from_c_str("bad_key = \"bad\""); 411 | float value = 0.0F; 412 | CHECK_FALSE(parser.try_read("key", value, 1.0F)); 413 | CHECK(value == 1.0F); 414 | CHECK_FALSE(parser.eof()); 415 | CHECK(parser.is_valid()); 416 | } 417 | 418 | { 419 | Parser parser = parser_from_c_str("key = 2.0"); 420 | float value = 0.0F; 421 | CHECK(parser.try_read("key", value, 1.0F)); 422 | CHECK(value == 2.0F); 423 | CHECK(parser.eof()); 424 | CHECK(parser.is_valid()); 425 | } 426 | 427 | { 428 | Parser parser = parser_from_c_str("bad_key = \"bad\""); 429 | int8_t value = 0; 430 | CHECK_FALSE(parser.try_read("key", value, 1)); 431 | CHECK(value == 1); 432 | CHECK_FALSE(parser.eof()); 433 | CHECK(parser.is_valid()); 434 | } 435 | 436 | { 437 | Parser parser = parser_from_c_str("key = -123"); 438 | int8_t value = 0; 439 | CHECK(parser.try_read("key", value, 1)); 440 | CHECK(value == -123); 441 | CHECK(parser.eof()); 442 | CHECK(parser.is_valid()); 443 | } 444 | 445 | { 446 | Parser parser = parser_from_c_str("bad_key = \"bad\""); 447 | uint8_t value = 0; 448 | CHECK_FALSE(parser.try_read("key", value, 1)); 449 | CHECK(value == 1); 450 | CHECK_FALSE(parser.eof()); 451 | CHECK(parser.is_valid()); 452 | } 453 | 454 | { 455 | Parser parser = parser_from_c_str("key = 123"); 456 | uint8_t value = 0; 457 | CHECK(parser.try_read("key", value, 1)); 458 | CHECK(value == 123); 459 | CHECK(parser.eof()); 460 | CHECK(parser.is_valid()); 461 | } 462 | 463 | { 464 | Parser parser = parser_from_c_str("bad_key = \"bad\""); 465 | int16_t value = 0; 466 | CHECK_FALSE(parser.try_read("key", value, 1)); 467 | CHECK(value == 1); 468 | CHECK_FALSE(parser.eof()); 469 | CHECK(parser.is_valid()); 470 | } 471 | 472 | { 473 | Parser parser = parser_from_c_str("key = -1234"); 474 | int16_t value = 0; 475 | CHECK(parser.try_read("key", value, 1)); 476 | CHECK(value == -1234); 477 | CHECK(parser.eof()); 478 | CHECK(parser.is_valid()); 479 | } 480 | 481 | { 482 | Parser parser = parser_from_c_str("bad_key = \"bad\""); 483 | uint16_t value = 0; 484 | CHECK_FALSE(parser.try_read("key", value, 1)); 485 | CHECK(value == 1); 486 | CHECK_FALSE(parser.eof()); 487 | CHECK(parser.is_valid()); 488 | } 489 | 490 | { 491 | Parser parser = parser_from_c_str("key = 1234"); 492 | uint16_t value = 0; 493 | CHECK(parser.try_read("key", value, 1)); 494 | CHECK(value == 1234); 495 | CHECK(parser.eof()); 496 | CHECK(parser.is_valid()); 497 | } 498 | 499 | { 500 | Parser parser = parser_from_c_str("bad_key = \"bad\""); 501 | int32_t value = 0; 502 | CHECK_FALSE(parser.try_read("key", value, 1)); 503 | CHECK(value == 1); 504 | CHECK_FALSE(parser.eof()); 505 | CHECK(parser.is_valid()); 506 | } 507 | 508 | { 509 | Parser parser = parser_from_c_str("key = -123456"); 510 | int32_t value = 0; 511 | CHECK(parser.try_read("key", value, 1)); 512 | CHECK(value == -123456); 513 | CHECK(parser.eof()); 514 | CHECK(parser.is_valid()); 515 | } 516 | 517 | { 518 | Parser parser = parser_from_c_str("bad_key = \"bad\""); 519 | uint32_t value = 0; 520 | CHECK_FALSE(parser.try_read("key", value, 1)); 521 | CHECK(value == 1); 522 | CHECK_FALSE(parser.eof()); 523 | CHECK(parser.is_valid()); 524 | } 525 | 526 | { 527 | Parser parser = parser_from_c_str("key = 123456"); 528 | uint32_t value = 0; 529 | CHECK(parser.try_read("key", value, 1)); 530 | CHECK(value == 123456); 531 | CHECK(parser.eof()); 532 | CHECK(parser.is_valid()); 533 | } 534 | 535 | { 536 | Parser parser = parser_from_c_str("bad_key = \"bad\""); 537 | int64_t value = 0; 538 | CHECK_FALSE(parser.try_read("key", value, 1)); 539 | CHECK(value == 1); 540 | CHECK_FALSE(parser.eof()); 541 | CHECK(parser.is_valid()); 542 | } 543 | 544 | { 545 | Parser parser = parser_from_c_str("key = -1234567890123456"); 546 | int64_t value = 0; 547 | CHECK(parser.try_read("key", value, 1)); 548 | CHECK(value == -1234567890123456LL); 549 | CHECK(parser.eof()); 550 | CHECK(parser.is_valid()); 551 | } 552 | 553 | { 554 | Parser parser = parser_from_c_str("bad_key = \"bad\""); 555 | uint64_t value = 0; 556 | CHECK_FALSE(parser.try_read("key", value, 1)); 557 | CHECK(value == 1); 558 | CHECK_FALSE(parser.eof()); 559 | CHECK(parser.is_valid()); 560 | } 561 | 562 | { 563 | Parser parser = parser_from_c_str("key = 1234567890123456"); 564 | uint64_t value = 0; 565 | CHECK(parser.try_read("key", value, 1)); 566 | CHECK(value == 1234567890123456ULL); 567 | CHECK(parser.eof()); 568 | CHECK(parser.is_valid()); 569 | } 570 | } 571 | 572 | TEST_CASE("Parser Array Reading", "[parser]") 573 | { 574 | { 575 | Parser parser = parser_from_c_str("key = [ 123.456789, 456.789, 151.091 ]"); 576 | double value[3] = { 0.0, 0.0, 0.0 }; 577 | CHECK(parser.read("key", value, 3)); 578 | CHECK(value[0] == 123.456789); 579 | CHECK(value[1] == 456.789); 580 | CHECK(value[2] == 151.091); 581 | CHECK(parser.eof()); 582 | CHECK(parser.is_valid()); 583 | } 584 | 585 | { 586 | Parser parser = parser_from_c_str("key = [ \"123.456789\", \"456.789\", \"151.091\" ]"); 587 | StringView value[3]; 588 | CHECK(parser.read("key", value, 3)); 589 | CHECK(value[0] == "123.456789"); 590 | CHECK(value[1] == "456.789"); 591 | CHECK(value[2] == "151.091"); 592 | CHECK(parser.eof()); 593 | CHECK(parser.is_valid()); 594 | } 595 | 596 | { 597 | Parser parser = parser_from_c_str("bad_key = \"bad\""); 598 | double value[3] = { 0.0, 0.0, 0.0 }; 599 | CHECK_FALSE(parser.try_read("key", value, 3, 1.0)); 600 | CHECK(value[0] == 1.0); 601 | CHECK(value[1] == 1.0); 602 | CHECK(value[2] == 1.0); 603 | CHECK_FALSE(parser.eof()); 604 | CHECK(parser.is_valid()); 605 | } 606 | 607 | { 608 | Parser parser = parser_from_c_str("key = [ 123.456789, 456.789, 151.091 ]"); 609 | double value[3] = { 0.0, 0.0, 0.0 }; 610 | CHECK(parser.try_read("key", value, 3, 1.0)); 611 | CHECK(value[0] == 123.456789); 612 | CHECK(value[1] == 456.789); 613 | CHECK(value[2] == 151.091); 614 | CHECK(parser.eof()); 615 | CHECK(parser.is_valid()); 616 | } 617 | 618 | { 619 | Parser parser = parser_from_c_str("bad_key = \"bad\""); 620 | StringView value[3]; 621 | CHECK_FALSE(parser.try_read("key", value, 3, "default")); 622 | CHECK(value[0] == "default"); 623 | CHECK(value[1] == "default"); 624 | CHECK(value[2] == "default"); 625 | CHECK_FALSE(parser.eof()); 626 | CHECK(parser.is_valid()); 627 | } 628 | 629 | { 630 | Parser parser = parser_from_c_str("key = [ \"123.456789\", \"456.789\", \"151.091\" ]"); 631 | StringView value[3]; 632 | CHECK(parser.try_read("key", value, 3, "default")); 633 | CHECK(value[0] == "123.456789"); 634 | CHECK(value[1] == "456.789"); 635 | CHECK(value[2] == "151.091"); 636 | CHECK(parser.eof()); 637 | CHECK(parser.is_valid()); 638 | } 639 | 640 | #if 0 641 | { 642 | Parser parser = parser_from_c_str("key = [ 123.456789, \"456.789\", false, [ 1.0, true ], { key0 = 1.0, key1 = false } ]"); 643 | 644 | CHECK(parser.array_begins("key")); 645 | // TODO 646 | CHECK(parser.array_ends()); 647 | CHECK(parser.eof()); 648 | CHECK(parser.is_valid()); 649 | } 650 | #endif 651 | } 652 | 653 | TEST_CASE("Parser Null Reading", "[parser]") 654 | { 655 | { 656 | Parser parser = parser_from_c_str("key = null"); 657 | bool value_bool = false; 658 | CHECK_FALSE(parser.try_read("key", value_bool, true)); 659 | CHECK(value_bool == true); 660 | double value_dbl = 0.0; 661 | CHECK_FALSE(parser.try_read("key", value_dbl, 1.0)); 662 | CHECK(value_dbl == 1.0); 663 | CHECK(parser.eof()); 664 | CHECK(parser.is_valid()); 665 | } 666 | } 667 | -------------------------------------------------------------------------------- /tests/sources/test_string_view.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2018 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | #include "catch2.impl.h" 26 | 27 | #include 28 | 29 | #include 30 | 31 | using namespace sjson; 32 | 33 | TEST_CASE("StringView", "[string]") 34 | { 35 | CHECK(StringView() == StringView("")); 36 | CHECK(StringView() == ""); 37 | CHECK(StringView().size() == 0); 38 | CHECK(StringView().c_str() != nullptr); 39 | CHECK(StringView("").size() == 0); 40 | CHECK(StringView("").c_str() != nullptr); 41 | 42 | const char* str0 = "this is a test string"; 43 | const char* str1 = "this is not a test string"; 44 | const char* str2 = "this is a test asset!"; 45 | 46 | CHECK(StringView(str0) == str0); 47 | CHECK(StringView(str0) != str1); 48 | CHECK(StringView(str0) != str2); 49 | CHECK(StringView(str0) == StringView(str0)); 50 | CHECK(StringView(str0) != StringView(str1)); 51 | CHECK(StringView(str0) != StringView(str2)); 52 | CHECK(StringView(str0).c_str() == str0); 53 | CHECK(StringView(str0).size() == std::strlen(str0)); 54 | CHECK(StringView(str0, 4) == StringView(str1, 4)); 55 | CHECK(StringView(str0, 4) == StringView("this")); 56 | 57 | StringView view0(str0); 58 | CHECK(view0 == str0); 59 | view0 = str1; 60 | CHECK(view0 == str1); 61 | 62 | CHECK(StringView().empty() == true); 63 | CHECK(StringView("").empty() == true); 64 | CHECK(view0.empty() == false); 65 | 66 | CHECK(view0[0] == 't'); 67 | CHECK(view0[1] == 'h'); 68 | CHECK(view0[2] == 'i'); 69 | CHECK(view0[3] == 's'); 70 | CHECK(view0[4] == ' '); 71 | CHECK(view0[5] == 'i'); 72 | CHECK(view0[view0.size() - 1] == 'g'); 73 | CHECK_THROWS(view0[view0.size()]); 74 | } 75 | -------------------------------------------------------------------------------- /tests/sources/test_writer.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2018 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | #include "catch2.impl.h" 26 | 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | using namespace sjson; 34 | 35 | class StringStreamWriter final : public StreamWriter 36 | { 37 | public: 38 | StringStreamWriter() = default; 39 | StringStreamWriter(const StringStreamWriter&) = delete; 40 | StringStreamWriter& operator=(const StringStreamWriter&) = delete; 41 | 42 | virtual void write(const void* buffer, size_t buffer_size) override 43 | { 44 | m_buffer.sputn(sjson_impl::bit_cast(buffer), static_cast(buffer_size)); 45 | } 46 | 47 | std::string str() const { return m_buffer.str(); } 48 | 49 | private: 50 | std::stringbuf m_buffer; 51 | }; 52 | 53 | TEST_CASE("Writer Object Bool Writing", "[writer]") 54 | { 55 | { 56 | StringStreamWriter str_writer; 57 | Writer writer(str_writer); 58 | writer.insert("key", true); 59 | CHECK(str_writer.str() == "key = true\r\n"); 60 | } 61 | 62 | { 63 | StringStreamWriter str_writer; 64 | Writer writer(str_writer); 65 | writer.insert("key", false); 66 | CHECK(str_writer.str() == "key = false\r\n"); 67 | } 68 | 69 | { 70 | StringStreamWriter str_writer; 71 | Writer writer(str_writer); 72 | writer["key"] = true; 73 | CHECK(str_writer.str() == "key = true\r\n"); 74 | } 75 | 76 | { 77 | StringStreamWriter str_writer; 78 | Writer writer(str_writer); 79 | writer["key"] = false; 80 | CHECK(str_writer.str() == "key = false\r\n"); 81 | } 82 | } 83 | 84 | TEST_CASE("Writer Object String Writing", "[writer]") 85 | { 86 | { 87 | StringStreamWriter str_writer; 88 | Writer writer(str_writer); 89 | writer.insert("key", "some string"); 90 | CHECK(str_writer.str() == "key = \"some string\"\r\n"); 91 | } 92 | 93 | { 94 | StringStreamWriter str_writer; 95 | Writer writer(str_writer); 96 | writer["key"] = "some string"; 97 | CHECK(str_writer.str() == "key = \"some string\"\r\n"); 98 | } 99 | 100 | { 101 | StringStreamWriter str_writer; 102 | Writer writer(str_writer); 103 | writer.insert("key", "some\tstring"); 104 | CHECK(str_writer.str() == "key = \"some\tstring\"\r\n"); 105 | } 106 | 107 | { 108 | StringStreamWriter str_writer; 109 | Writer writer(str_writer); 110 | writer["key"] = "some\tstring"; 111 | CHECK(str_writer.str() == "key = \"some\tstring\"\r\n"); 112 | } 113 | 114 | { 115 | StringStreamWriter str_writer; 116 | Writer writer(str_writer); 117 | writer.insert("key", "some\nstring"); 118 | CHECK(str_writer.str() == "key = \"some\nstring\"\r\n"); 119 | } 120 | 121 | { 122 | StringStreamWriter str_writer; 123 | Writer writer(str_writer); 124 | writer["key"] = "some\nstring"; 125 | CHECK(str_writer.str() == "key = \"some\nstring\"\r\n"); 126 | } 127 | 128 | { 129 | StringStreamWriter str_writer; 130 | Writer writer(str_writer); 131 | writer.insert("key", "some\"string"); 132 | CHECK(str_writer.str() == "key = \"some\"string\"\r\n"); 133 | } 134 | 135 | { 136 | StringStreamWriter str_writer; 137 | Writer writer(str_writer); 138 | writer["key"] = "some\"string"; 139 | CHECK(str_writer.str() == "key = \"some\"string\"\r\n"); 140 | } 141 | } 142 | 143 | TEST_CASE("Writer Object Number Writing", "[writer]") 144 | { 145 | { 146 | StringStreamWriter str_writer; 147 | Writer writer(str_writer); 148 | writer.insert("key", 123.0); 149 | CHECK(str_writer.str() == "key = 123\r\n"); 150 | } 151 | 152 | { 153 | StringStreamWriter str_writer; 154 | Writer writer(str_writer); 155 | writer["key"] = 123.0; 156 | CHECK(str_writer.str() == "key = 123\r\n"); 157 | } 158 | 159 | { 160 | StringStreamWriter str_writer; 161 | Writer writer(str_writer); 162 | writer.insert("key", 123.456); 163 | CHECK(str_writer.str() == "key = 123.456\r\n"); 164 | } 165 | 166 | { 167 | StringStreamWriter str_writer; 168 | Writer writer(str_writer); 169 | writer["key"] = 123.456; 170 | CHECK(str_writer.str() == "key = 123.456\r\n"); 171 | } 172 | 173 | { 174 | StringStreamWriter str_writer; 175 | Writer writer(str_writer); 176 | writer.insert("key", std::nan("")); 177 | CHECK(str_writer.str() == "key = \"nan\"\r\n"); 178 | } 179 | 180 | { 181 | StringStreamWriter str_writer; 182 | Writer writer(str_writer); 183 | writer["key"] = std::nan(""); 184 | CHECK(str_writer.str() == "key = \"nan\"\r\n"); 185 | } 186 | 187 | { 188 | StringStreamWriter str_writer; 189 | Writer writer(str_writer); 190 | writer.insert("key", std::numeric_limits::infinity()); 191 | CHECK(str_writer.str() == "key = \"inf\"\r\n"); 192 | } 193 | 194 | { 195 | StringStreamWriter str_writer; 196 | Writer writer(str_writer); 197 | writer["key"] = std::numeric_limits::infinity(); 198 | CHECK(str_writer.str() == "key = \"inf\"\r\n"); 199 | } 200 | 201 | { 202 | StringStreamWriter str_writer; 203 | Writer writer(str_writer); 204 | writer.insert("key", -std::numeric_limits::infinity()); 205 | CHECK(str_writer.str() == "key = \"-inf\"\r\n"); 206 | } 207 | 208 | { 209 | StringStreamWriter str_writer; 210 | Writer writer(str_writer); 211 | writer["key"] = -std::numeric_limits::infinity(); 212 | CHECK(str_writer.str() == "key = \"-inf\"\r\n"); 213 | } 214 | 215 | { 216 | StringStreamWriter str_writer; 217 | Writer writer(str_writer); 218 | writer.insert("key", 123.0F); 219 | CHECK(str_writer.str() == "key = 123\r\n"); 220 | } 221 | 222 | { 223 | StringStreamWriter str_writer; 224 | Writer writer(str_writer); 225 | writer["key"] = 123.0F; 226 | CHECK(str_writer.str() == "key = 123\r\n"); 227 | } 228 | 229 | { 230 | StringStreamWriter str_writer; 231 | Writer writer(str_writer); 232 | writer.insert("key", 123.5F); 233 | CHECK(str_writer.str() == "key = 123.5\r\n"); 234 | } 235 | 236 | { 237 | StringStreamWriter str_writer; 238 | Writer writer(str_writer); 239 | writer["key"] = 123.5F; 240 | CHECK(str_writer.str() == "key = 123.5\r\n"); 241 | } 242 | 243 | { 244 | StringStreamWriter str_writer; 245 | Writer writer(str_writer); 246 | writer.insert("key", std::nanf("")); 247 | CHECK(str_writer.str() == "key = \"nan\"\r\n"); 248 | } 249 | 250 | { 251 | StringStreamWriter str_writer; 252 | Writer writer(str_writer); 253 | writer["key"] = std::nanf(""); 254 | CHECK(str_writer.str() == "key = \"nan\"\r\n"); 255 | } 256 | 257 | { 258 | StringStreamWriter str_writer; 259 | Writer writer(str_writer); 260 | writer.insert("key", std::numeric_limits::infinity()); 261 | CHECK(str_writer.str() == "key = \"inf\"\r\n"); 262 | } 263 | 264 | { 265 | StringStreamWriter str_writer; 266 | Writer writer(str_writer); 267 | writer["key"] = std::numeric_limits::infinity(); 268 | CHECK(str_writer.str() == "key = \"inf\"\r\n"); 269 | } 270 | 271 | { 272 | StringStreamWriter str_writer; 273 | Writer writer(str_writer); 274 | writer.insert("key", -std::numeric_limits::infinity()); 275 | CHECK(str_writer.str() == "key = \"-inf\"\r\n"); 276 | } 277 | 278 | { 279 | StringStreamWriter str_writer; 280 | Writer writer(str_writer); 281 | writer["key"] = -std::numeric_limits::infinity(); 282 | CHECK(str_writer.str() == "key = \"-inf\"\r\n"); 283 | } 284 | 285 | { 286 | StringStreamWriter str_writer; 287 | Writer writer(str_writer); 288 | int8_t value = -123; 289 | writer.insert("key", value); 290 | CHECK(str_writer.str() == "key = -123\r\n"); 291 | } 292 | 293 | { 294 | StringStreamWriter str_writer; 295 | Writer writer(str_writer); 296 | int8_t value = -123; 297 | writer["key"] = value; 298 | CHECK(str_writer.str() == "key = -123\r\n"); 299 | } 300 | 301 | { 302 | StringStreamWriter str_writer; 303 | Writer writer(str_writer); 304 | uint8_t value = 123; 305 | writer.insert("key", value); 306 | CHECK(str_writer.str() == "key = 123\r\n"); 307 | } 308 | 309 | { 310 | StringStreamWriter str_writer; 311 | Writer writer(str_writer); 312 | uint8_t value = 123; 313 | writer["key"] = value; 314 | CHECK(str_writer.str() == "key = 123\r\n"); 315 | } 316 | 317 | { 318 | StringStreamWriter str_writer; 319 | Writer writer(str_writer); 320 | int16_t value = -1234; 321 | writer.insert("key", value); 322 | CHECK(str_writer.str() == "key = -1234\r\n"); 323 | } 324 | 325 | { 326 | StringStreamWriter str_writer; 327 | Writer writer(str_writer); 328 | int16_t value = -1234; 329 | writer["key"] = value; 330 | CHECK(str_writer.str() == "key = -1234\r\n"); 331 | } 332 | 333 | { 334 | StringStreamWriter str_writer; 335 | Writer writer(str_writer); 336 | uint16_t value = 1234; 337 | writer.insert("key", value); 338 | CHECK(str_writer.str() == "key = 1234\r\n"); 339 | } 340 | 341 | { 342 | StringStreamWriter str_writer; 343 | Writer writer(str_writer); 344 | uint16_t value = 1234; 345 | writer["key"] = value; 346 | CHECK(str_writer.str() == "key = 1234\r\n"); 347 | } 348 | 349 | { 350 | StringStreamWriter str_writer; 351 | Writer writer(str_writer); 352 | int32_t value = -123456; 353 | writer.insert("key", value); 354 | CHECK(str_writer.str() == "key = -123456\r\n"); 355 | } 356 | 357 | { 358 | StringStreamWriter str_writer; 359 | Writer writer(str_writer); 360 | int32_t value = -123456; 361 | writer["key"] = value; 362 | CHECK(str_writer.str() == "key = -123456\r\n"); 363 | } 364 | 365 | { 366 | StringStreamWriter str_writer; 367 | Writer writer(str_writer); 368 | uint32_t value = 123456; 369 | writer.insert("key", value); 370 | CHECK(str_writer.str() == "key = 123456\r\n"); 371 | } 372 | 373 | { 374 | StringStreamWriter str_writer; 375 | Writer writer(str_writer); 376 | uint32_t value = 123456; 377 | writer["key"] = value; 378 | CHECK(str_writer.str() == "key = 123456\r\n"); 379 | } 380 | 381 | { 382 | StringStreamWriter str_writer; 383 | Writer writer(str_writer); 384 | int64_t value = -1234567890123456LL; 385 | writer.insert("key", value); 386 | CHECK(str_writer.str() == "key = -1234567890123456\r\n"); 387 | } 388 | 389 | { 390 | StringStreamWriter str_writer; 391 | Writer writer(str_writer); 392 | int64_t value = -1234567890123456LL; 393 | writer["key"] = value; 394 | CHECK(str_writer.str() == "key = -1234567890123456\r\n"); 395 | } 396 | 397 | { 398 | StringStreamWriter str_writer; 399 | Writer writer(str_writer); 400 | uint64_t value = 1234567890123456ULL; 401 | writer.insert("key", value); 402 | CHECK(str_writer.str() == "key = 1234567890123456\r\n"); 403 | } 404 | 405 | { 406 | StringStreamWriter str_writer; 407 | Writer writer(str_writer); 408 | uint64_t value = 1234567890123456ULL; 409 | writer["key"] = value; 410 | CHECK(str_writer.str() == "key = 1234567890123456\r\n"); 411 | } 412 | } 413 | 414 | TEST_CASE("Writer Object Array Writing", "[writer]") 415 | { 416 | { 417 | StringStreamWriter str_writer; 418 | Writer writer(str_writer); 419 | writer.insert("key", [](ArrayWriter& /*array_writer*/) 420 | { 421 | }); 422 | CHECK(str_writer.str() == "key = [ ]\r\n"); 423 | } 424 | 425 | { 426 | StringStreamWriter str_writer; 427 | Writer writer(str_writer); 428 | writer.insert("key", [](ArrayWriter& array_writer) 429 | { 430 | array_writer.push(123.5); 431 | array_writer.push(456.5); 432 | }); 433 | CHECK(str_writer.str() == "key = [ 123.5, 456.5 ]\r\n"); 434 | } 435 | 436 | { 437 | StringStreamWriter str_writer; 438 | Writer writer(str_writer); 439 | writer["key"] = [](ArrayWriter& array_writer) 440 | { 441 | array_writer.push(123.5); 442 | array_writer.push(456.5); 443 | }; 444 | CHECK(str_writer.str() == "key = [ 123.5, 456.5 ]\r\n"); 445 | } 446 | } 447 | 448 | TEST_CASE("Writer Object Object Writing", "[writer]") 449 | { 450 | { 451 | StringStreamWriter str_writer; 452 | Writer writer(str_writer); 453 | writer.insert("key", [](ObjectWriter& /*object_writer*/) 454 | { 455 | }); 456 | CHECK(str_writer.str() == "key = {\r\n}\r\n"); 457 | } 458 | 459 | { 460 | StringStreamWriter str_writer; 461 | Writer writer(str_writer); 462 | writer.insert("key", [](ObjectWriter& object_writer) 463 | { 464 | object_writer["key0"] = 123.5; 465 | object_writer["key1"] = 456.5; 466 | }); 467 | CHECK(str_writer.str() == "key = {\r\n\tkey0 = 123.5\r\n\tkey1 = 456.5\r\n}\r\n"); 468 | } 469 | 470 | { 471 | StringStreamWriter str_writer; 472 | Writer writer(str_writer); 473 | writer["key"] = [](ObjectWriter& object_writer) 474 | { 475 | object_writer["key0"] = 123.5; 476 | object_writer["key1"] = 456.5; 477 | }; 478 | CHECK(str_writer.str() == "key = {\r\n\tkey0 = 123.5\r\n\tkey1 = 456.5\r\n}\r\n"); 479 | } 480 | } 481 | 482 | TEST_CASE("Writer Array Bool Writing", "[writer]") 483 | { 484 | { 485 | StringStreamWriter str_writer; 486 | Writer writer(str_writer); 487 | writer.insert("key", [](ArrayWriter& array_writer) 488 | { 489 | array_writer.push(true); 490 | }); 491 | CHECK(str_writer.str() == "key = [ true ]\r\n"); 492 | } 493 | 494 | { 495 | StringStreamWriter str_writer; 496 | Writer writer(str_writer); 497 | writer.insert("key", [](ArrayWriter& array_writer) 498 | { 499 | array_writer.push(false); 500 | }); 501 | CHECK(str_writer.str() == "key = [ false ]\r\n"); 502 | } 503 | } 504 | 505 | TEST_CASE("Writer Array String Writing", "[writer]") 506 | { 507 | { 508 | StringStreamWriter str_writer; 509 | Writer writer(str_writer); 510 | writer.insert("key", [](ArrayWriter& array_writer) 511 | { 512 | array_writer.push("some string"); 513 | }); 514 | CHECK(str_writer.str() == "key = [ \"some string\" ]\r\n"); 515 | } 516 | 517 | { 518 | StringStreamWriter str_writer; 519 | Writer writer(str_writer); 520 | writer.insert("key", [](ArrayWriter& array_writer) 521 | { 522 | array_writer.push("some\tstring"); 523 | }); 524 | CHECK(str_writer.str() == "key = [ \"some\tstring\" ]\r\n"); 525 | } 526 | 527 | { 528 | StringStreamWriter str_writer; 529 | Writer writer(str_writer); 530 | writer.insert("key", [](ArrayWriter& array_writer) 531 | { 532 | array_writer.push("some\nstring"); 533 | }); 534 | CHECK(str_writer.str() == "key = [ \"some\nstring\" ]\r\n"); 535 | } 536 | 537 | { 538 | StringStreamWriter str_writer; 539 | Writer writer(str_writer); 540 | writer.insert("key", [](ArrayWriter& array_writer) 541 | { 542 | array_writer.push("some\"string"); 543 | }); 544 | CHECK(str_writer.str() == "key = [ \"some\"string\" ]\r\n"); 545 | } 546 | } 547 | 548 | TEST_CASE("Writer Array Number Writing", "[writer]") 549 | { 550 | { 551 | StringStreamWriter str_writer; 552 | Writer writer(str_writer); 553 | writer.insert("key", [](ArrayWriter& array_writer) 554 | { 555 | array_writer.push(123.0); 556 | }); 557 | CHECK(str_writer.str() == "key = [ 123 ]\r\n"); 558 | } 559 | 560 | { 561 | StringStreamWriter str_writer; 562 | Writer writer(str_writer); 563 | writer.insert("key", [](ArrayWriter& array_writer) 564 | { 565 | array_writer.push(123.456); 566 | }); 567 | CHECK(str_writer.str() == "key = [ 123.456 ]\r\n"); 568 | } 569 | 570 | { 571 | StringStreamWriter str_writer; 572 | Writer writer(str_writer); 573 | writer.insert("key", [](ArrayWriter& array_writer) 574 | { 575 | array_writer.push(std::nan("")); 576 | array_writer.push(std::numeric_limits::infinity()); 577 | array_writer.push(-std::numeric_limits::infinity()); 578 | }); 579 | CHECK(str_writer.str() == "key = [ \"nan\", \"inf\", \"-inf\" ]\r\n"); 580 | } 581 | 582 | { 583 | StringStreamWriter str_writer; 584 | Writer writer(str_writer); 585 | writer.insert("key", [](ArrayWriter& array_writer) 586 | { 587 | array_writer.push(123.0F); 588 | }); 589 | CHECK(str_writer.str() == "key = [ 123 ]\r\n"); 590 | } 591 | 592 | { 593 | StringStreamWriter str_writer; 594 | Writer writer(str_writer); 595 | writer.insert("key", [](ArrayWriter& array_writer) 596 | { 597 | array_writer.push(123.5F); 598 | }); 599 | CHECK(str_writer.str() == "key = [ 123.5 ]\r\n"); 600 | } 601 | 602 | { 603 | StringStreamWriter str_writer; 604 | Writer writer(str_writer); 605 | writer.insert("key", [](ArrayWriter& array_writer) 606 | { 607 | array_writer.push(std::nanf("")); 608 | array_writer.push(std::numeric_limits::infinity()); 609 | array_writer.push(-std::numeric_limits::infinity()); 610 | }); 611 | CHECK(str_writer.str() == "key = [ \"nan\", \"inf\", \"-inf\" ]\r\n"); 612 | } 613 | 614 | { 615 | StringStreamWriter str_writer; 616 | Writer writer(str_writer); 617 | writer.insert("key", [](ArrayWriter& array_writer) 618 | { 619 | int8_t value = -123; 620 | array_writer.push(value); 621 | }); 622 | CHECK(str_writer.str() == "key = [ -123 ]\r\n"); 623 | } 624 | 625 | { 626 | StringStreamWriter str_writer; 627 | Writer writer(str_writer); 628 | writer.insert("key", [](ArrayWriter& array_writer) 629 | { 630 | uint8_t value = 123; 631 | array_writer.push(value); 632 | }); 633 | CHECK(str_writer.str() == "key = [ 123 ]\r\n"); 634 | } 635 | 636 | { 637 | StringStreamWriter str_writer; 638 | Writer writer(str_writer); 639 | writer.insert("key", [](ArrayWriter& array_writer) 640 | { 641 | int16_t value = -1234; 642 | array_writer.push(value); 643 | }); 644 | CHECK(str_writer.str() == "key = [ -1234 ]\r\n"); 645 | } 646 | 647 | { 648 | StringStreamWriter str_writer; 649 | Writer writer(str_writer); 650 | writer.insert("key", [](ArrayWriter& array_writer) 651 | { 652 | uint16_t value = 1234; 653 | array_writer.push(value); 654 | }); 655 | CHECK(str_writer.str() == "key = [ 1234 ]\r\n"); 656 | } 657 | 658 | { 659 | StringStreamWriter str_writer; 660 | Writer writer(str_writer); 661 | writer.insert("key", [](ArrayWriter& array_writer) 662 | { 663 | int32_t value = -123456; 664 | array_writer.push(value); 665 | }); 666 | CHECK(str_writer.str() == "key = [ -123456 ]\r\n"); 667 | } 668 | 669 | { 670 | StringStreamWriter str_writer; 671 | Writer writer(str_writer); 672 | writer.insert("key", [](ArrayWriter& array_writer) 673 | { 674 | uint32_t value = 123456; 675 | array_writer.push(value); 676 | }); 677 | CHECK(str_writer.str() == "key = [ 123456 ]\r\n"); 678 | } 679 | 680 | { 681 | StringStreamWriter str_writer; 682 | Writer writer(str_writer); 683 | writer.insert("key", [](ArrayWriter& array_writer) 684 | { 685 | int64_t value = -1234567890123456LL; 686 | array_writer.push(value); 687 | }); 688 | CHECK(str_writer.str() == "key = [ -1234567890123456 ]\r\n"); 689 | } 690 | 691 | { 692 | StringStreamWriter str_writer; 693 | Writer writer(str_writer); 694 | writer.insert("key", [](ArrayWriter& array_writer) 695 | { 696 | uint64_t value = 1234567890123456ULL; 697 | array_writer.push(value); 698 | }); 699 | CHECK(str_writer.str() == "key = [ 1234567890123456 ]\r\n"); 700 | } 701 | } 702 | 703 | TEST_CASE("Writer Array Array Writing", "[writer]") 704 | { 705 | { 706 | StringStreamWriter str_writer; 707 | Writer writer(str_writer); 708 | writer.insert("key", [](ArrayWriter& array_writer) 709 | { 710 | array_writer.push([](ArrayWriter& /*array_writer*/) 711 | { 712 | }); 713 | }); 714 | CHECK(str_writer.str() == "key = [ [ ] ]\r\n"); 715 | } 716 | 717 | { 718 | StringStreamWriter str_writer; 719 | Writer writer(str_writer); 720 | writer.insert("key", [](ArrayWriter& array_writer) 721 | { 722 | array_writer.push([](ArrayWriter& array_writer1) 723 | { 724 | array_writer1.push(123.5); 725 | array_writer1.push(456.5); 726 | }); 727 | }); 728 | CHECK(str_writer.str() == "key = [ [ 123.5, 456.5 ] ]\r\n"); 729 | } 730 | } 731 | 732 | TEST_CASE("Writer Array Object Writing", "[writer]") 733 | { 734 | { 735 | StringStreamWriter str_writer; 736 | Writer writer(str_writer); 737 | writer.insert("key", [](ArrayWriter& array_writer) 738 | { 739 | array_writer.push([](ObjectWriter& /*object_writer*/) 740 | { 741 | }); 742 | }); 743 | CHECK(str_writer.str() == "key = [ \r\n\t{\r\n\t}\r\n]\r\n"); 744 | } 745 | 746 | { 747 | StringStreamWriter str_writer; 748 | Writer writer(str_writer); 749 | writer.insert("key", [](ArrayWriter& array_writer) 750 | { 751 | array_writer.push([](ObjectWriter& object_writer1) 752 | { 753 | object_writer1["key0"] = 123.5; 754 | object_writer1["key1"] = 456.5; 755 | }); 756 | }); 757 | CHECK(str_writer.str() == "key = [ \r\n\t{\r\n\t\tkey0 = 123.5\r\n\t\tkey1 = 456.5\r\n\t}\r\n]\r\n"); 758 | } 759 | } 760 | -------------------------------------------------------------------------------- /tests/validate_includes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.2) 2 | project(sjson-cpp_validate_includes CXX) 3 | 4 | # The goal of this project is to generate a single cpp file for every public header 5 | # This will allow us to detect if we are missing an include file during development 6 | 7 | include_directories("${PROJECT_SOURCE_DIR}/../../includes") 8 | 9 | # Grab all of our public header files 10 | file(GLOB ALL_PUBLIC_HEADER_FILES LIST_DIRECTORIES false 11 | ${PROJECT_SOURCE_DIR}/../../includes/sjson/*.h) 12 | 13 | # Generate the single include cpp files 14 | foreach(HEADER_FILE ${ALL_PUBLIC_HEADER_FILES}) 15 | # Find the root include directory position 16 | string(FIND ${HEADER_FILE} "sjson" HEADER_FILE_SJSON_CPP_POS REVERSE) 17 | 18 | # Strip the root of the include path 19 | string(SUBSTRING ${HEADER_FILE} ${HEADER_FILE_SJSON_CPP_POS} -1 HEADER_INCLUDE_PATH) 20 | 21 | # Configure our cpp file content 22 | set(SJSON_CPP_SINGLE_INCLUDE_NAME ${HEADER_INCLUDE_PATH}) 23 | 24 | # Sanitize our filename so we can generate a unique cpp file for it 25 | string(REPLACE "/" "_" HEADER_SANITIZED_FILENAME ${HEADER_INCLUDE_PATH}) 26 | string(REPLACE "\\" "_" HEADER_SANITIZED_FILENAME ${HEADER_SANITIZED_FILENAME}) 27 | 28 | # Generate our single include cpp file 29 | configure_file(${PROJECT_SOURCE_DIR}/single_include.cpp.in single_include_${HEADER_SANITIZED_FILENAME}.cpp @ONLY) 30 | endforeach(HEADER_FILE) 31 | 32 | # Grab all of our main source files 33 | file(GLOB_RECURSE ALL_MAIN_SOURCE_FILES LIST_DIRECTORIES false 34 | ${PROJECT_SOURCE_DIR}/*.cpp 35 | ${PROJECT_BINARY_DIR}/*.cpp) 36 | 37 | create_source_groups("${ALL_MAIN_SOURCE_FILES}" ${PROJECT_SOURCE_DIR}) 38 | 39 | add_library(${PROJECT_NAME} STATIC ${ALL_MAIN_SOURCE_FILES}) 40 | 41 | setup_default_compiler_flags(${PROJECT_NAME}) 42 | -------------------------------------------------------------------------------- /tests/validate_includes/dummy.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2024 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | // We define a single symbol here to avoid a warning when we build our static library 26 | // to validate includes 27 | void some_function() {} 28 | -------------------------------------------------------------------------------- /tests/validate_includes/single_include.cpp.in: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // The MIT License (MIT) 3 | // 4 | // Copyright (c) 2024 Nicholas Frechette, Cody Jones, and sjson-cpp contributors 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | //////////////////////////////////////////////////////////////////////////////// 24 | 25 | #include <@SJSON_CPP_SINGLE_INCLUDE_NAME@> 26 | -------------------------------------------------------------------------------- /tools/appveyor_ci.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Unpack arguments 4 | SET WORKER_IMAGE=%1 5 | SET PLATFORM=%2 6 | SET CONFIG=%3 7 | SET TOOLCHAIN=%4 8 | SET PYTHON_PATH=%5 9 | 10 | echo Worker image: %WORKER_IMAGE% 11 | echo Platform: %PLATFORM% 12 | echo Config: %CONFIG% 13 | echo Toolchain: %TOOLCHAIN% 14 | echo Python path: %PYTHON_PATH% 15 | 16 | REM Convert the build image and toolchain into our compiler string 17 | IF /i %TOOLCHAIN%==msvc GOTO :msvc 18 | IF /i %TOOLCHAIN%==clang GOTO :clang 19 | 20 | echo Unknown toolchain: %TOOLCHAIN% 21 | exit /B 1 22 | 23 | :msvc 24 | IF /i %WORKER_IMAGE%=="Visual Studio 2015" SET COMPILER=vs2015 25 | IF /i %WORKER_IMAGE%=="Visual Studio 2017" SET COMPILER=vs2017 26 | IF /i %WORKER_IMAGE%=="Visual Studio 2019" SET COMPILER=vs2019 27 | IF /i %WORKER_IMAGE%=="Previous Visual Studio 2019" SET COMPILER=vs2019 28 | GOTO :next 29 | 30 | :clang 31 | IF /i %WORKER_IMAGE%=="Visual Studio 2019" SET COMPILER=vs2019-clang 32 | IF /i %WORKER_IMAGE%=="Previous Visual Studio 2019" SET COMPILER=vs2019-clang 33 | 34 | REM HACK!!! Disable clang build for now with appveyor since vcpkg breaks the compiler detection of cmake 35 | REM Fake build success 36 | exit /B 0 37 | 38 | GOTO :next 39 | 40 | :next 41 | REM Set our switch if we need to run unit tests 42 | SET UNIT_TEST_FLAG=-unit_test 43 | IF /i %PLATFORM%==arm64 SET UNIT_TEST_FLAG= 44 | 45 | REM If PYTHON_PATH isn't set, assume it is in PATH 46 | IF NOT DEFINED PYTHON_PATH SET PYTHON_PATH=python.exe 47 | 48 | REM Build and run unit tests 49 | %PYTHON_PATH% make.py -build %UNIT_TEST_FLAG% -compiler %COMPILER% -config %CONFIG% -cpu %PLATFORM% -clean 50 | IF NOT %ERRORLEVEL% EQU 0 GOTO :build_failure 51 | 52 | REM Done! 53 | exit /B 0 54 | 55 | :build_failure 56 | echo Build failed! 57 | exit /B 1 58 | -------------------------------------------------------------------------------- /tools/release_scripts/test_everything.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import shutil 4 | import subprocess 5 | import sys 6 | 7 | def get_platform_compilers(): 8 | if platform.system() == 'Windows': 9 | return [ 'vs2015', 'vs2017', 'vs2019', 'vs2019-clang' ] 10 | elif platform.system() == 'Linux': 11 | compilers = [] 12 | if shutil.which('g++-5'): 13 | compilers.append('gcc5') 14 | if shutil.which('g++-6'): 15 | compilers.append('gcc6') 16 | if shutil.which('g++-7'): 17 | compilers.append('gcc7') 18 | if shutil.which('g++-8'): 19 | compilers.append('gcc8') 20 | if shutil.which('g++-9'): 21 | compilers.append('gcc9') 22 | if shutil.which('g++-10'): 23 | compilers.append('gcc10') 24 | 25 | if shutil.which('clang++-4.0'): 26 | compilers.append('clang4') 27 | if shutil.which('clang++-5.0'): 28 | compilers.append('clang5') 29 | if shutil.which('clang++-6.0'): 30 | compilers.append('clang6') 31 | if shutil.which('clang++-7'): 32 | compilers.append('clang7') 33 | if shutil.which('clang++-8'): 34 | compilers.append('clang8') 35 | if shutil.which('clang++-9'): 36 | compilers.append('clang9') 37 | if shutil.which('clang++-10'): 38 | compilers.append('clang10') 39 | if shutil.which('clang++-11'): 40 | compilers.append('clang11') 41 | 42 | return compilers 43 | elif platform.system() == 'Darwin': 44 | return [ 'osx' ] 45 | else: 46 | print('Unknown platform!') 47 | sys.exit(1) 48 | 49 | def get_python_exe_name(): 50 | if platform.system() == 'Windows': 51 | return 'python' 52 | else: 53 | return 'python3' 54 | 55 | if __name__ == "__main__": 56 | os.environ['PYTHONIOENCODING'] = 'utf_8' 57 | 58 | configs = [ 'debug', 'release' ] 59 | archs = [ 'x86', 'x64' ] 60 | compilers = get_platform_compilers() 61 | python_exe = get_python_exe_name() 62 | 63 | if platform.system() == 'Darwin': 64 | result = subprocess.check_output(['xcodebuild', '-version']).decode("utf-8") 65 | if 'Xcode 11' in result: 66 | archs.remove('x86') 67 | 68 | cmd_args = [] 69 | for config in configs: 70 | for arch in archs: 71 | for compiler in compilers: 72 | args = [python_exe, 'make.py', '-compiler', compiler, '-cpu', arch, '-config', config, '-build', '-unit_test', '-clean'] 73 | cmd_args.append([x for x in args if x]) 74 | 75 | if platform.system() == 'Windows': 76 | for config in configs: 77 | # Windows ARM 78 | args = [python_exe, 'make.py', '-compiler', 'vs2017', '-cpu', 'arm64', '-config', config, '-build', '-clean'] 79 | cmd_args.append([x for x in args if x]) 80 | 81 | # Android 82 | args = [python_exe, 'make.py', '-compiler', 'android', '-cpu', 'armv7', '-config', config, '-build', '-clean'] 83 | cmd_args.append([x for x in args if x]) 84 | args = [python_exe, 'make.py', '-compiler', 'android', '-cpu', 'arm64', '-config', config, '-build', '-clean'] 85 | cmd_args.append([x for x in args if x]) 86 | elif platform.system() == 'Darwin': 87 | for config in configs: 88 | # iOS 89 | args = [python_exe, 'make.py', '-compiler', 'ios', '-config', config, '-build', '-clean'] 90 | cmd_args.append([x for x in args if x]) 91 | 92 | if platform.system() == 'Darwin' or platform.system() == 'Linux': 93 | for config in configs: 94 | # Emscripten 95 | args = [python_exe, 'make.py', '-compiler', 'emscripten', '-config', config, '-build', '-unit_test', '-clean'] 96 | cmd_args.append([x for x in args if x]) 97 | 98 | root_dir = os.path.join(os.getcwd(), '../..') 99 | os.chdir(root_dir) 100 | 101 | for args in cmd_args: 102 | print('Running command: "{}" ...'.format(" ".join(args))) 103 | try: 104 | if 'android' in args: 105 | subprocess.check_call(args) 106 | else: 107 | subprocess.check_output(args) 108 | except subprocess.CalledProcessError as e: 109 | print('Failed command: {}'.format(" ".join(args))) 110 | print(e.output.decode(sys.stdout.encoding)) 111 | sys.exit(1) 112 | 113 | print('Done!') 114 | sys.exit(0) 115 | -------------------------------------------------------------------------------- /tools/setup_linux_compiler.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Extract our command line arguments 4 | COMPILER=$1 5 | 6 | # Convert our GCC compiler into a list of packages it needs 7 | if [[ $COMPILER == gcc5 ]]; then 8 | PACKAGES="g++-5 g++-5-multilib g++-multilib" 9 | elif [[ $COMPILER == gcc6 ]]; then 10 | PACKAGES="g++-6 g++-6-multilib g++-multilib" 11 | elif [[ $COMPILER == gcc7 ]]; then 12 | PACKAGES="g++-7 g++-7-multilib g++-multilib" 13 | elif [[ $COMPILER == gcc8 ]]; then 14 | PACKAGES="g++-8 g++-8-multilib g++-multilib" 15 | elif [[ $COMPILER == gcc9 ]]; then 16 | PACKAGES="g++-9 g++-9-multilib g++-multilib" 17 | elif [[ $COMPILER == gcc10 ]]; then 18 | PACKAGES="g++-10 g++-10-multilib g++-multilib" 19 | fi 20 | 21 | # If using clang, add our apt source key 22 | if [[ $COMPILER == clang* ]]; then 23 | curl -sSL "http://apt.llvm.org/llvm-snapshot.gpg.key" | sudo -E apt-key add - ; 24 | fi 25 | 26 | # Convert our clang compiler into a list of packages it needs and its source 27 | if [[ $COMPILER == clang4 ]]; then 28 | # clang4 isn't available after xenial 29 | PACKAGES="clang-4.0 libstdc++-5-dev libc6-dev-i386 g++-5-multilib g++-multilib" 30 | echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-4.0 main" | sudo tee -a /etc/apt/sources.list > /dev/null ; 31 | elif [[ $COMPILER == clang5 ]]; then 32 | PACKAGES="clang-5.0 libstdc++-5-dev libc6-dev-i386 g++-5-multilib g++-multilib" 33 | echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-5.0 main" | sudo tee -a /etc/apt/sources.list > /dev/null ; 34 | elif [[ $COMPILER == clang6 ]]; then 35 | PACKAGES="clang-6.0 libstdc++-5-dev libc6-dev-i386 g++-5-multilib g++-multilib" 36 | echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-6.0 main" | sudo tee -a /etc/apt/sources.list > /dev/null ; 37 | elif [[ $COMPILER == clang7 ]]; then 38 | PACKAGES="clang-7 libstdc++-5-dev libc6-dev-i386 g++-5-multilib g++-multilib" 39 | echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-7 main" | sudo tee -a /etc/apt/sources.list > /dev/null ; 40 | elif [[ $COMPILER == clang8 ]]; then 41 | PACKAGES="clang-8 libstdc++-5-dev libc6-dev-i386 g++-5-multilib g++-multilib" 42 | echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main" | sudo tee -a /etc/apt/sources.list > /dev/null ; 43 | elif [[ $COMPILER == clang9 ]]; then 44 | PACKAGES="clang-9 libstdc++-5-dev libc6-dev-i386 g++-5-multilib g++-multilib" 45 | echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main" | sudo tee -a /etc/apt/sources.list > /dev/null ; 46 | elif [[ $COMPILER == clang10 ]]; then 47 | PACKAGES="clang-10 libstdc++-5-dev libc6-dev-i386 g++-5-multilib g++-multilib" 48 | echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main" | sudo tee -a /etc/apt/sources.list > /dev/null ; 49 | elif [[ $COMPILER == clang11 ]]; then 50 | PACKAGES="clang-11 libstdc++-5-dev libc6-dev-i386 g++-5-multilib g++-multilib" 51 | echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main" | sudo tee -a /etc/apt/sources.list > /dev/null ; 52 | fi 53 | 54 | # Install the packages we need 55 | sudo -E apt-add-repository -y "ppa:ubuntu-toolchain-r/test"; 56 | sudo -E apt-get -yq update; 57 | sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install $PACKAGES; 58 | -------------------------------------------------------------------------------- /tools/setup_osx_compiler.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Extract our command line arguments 4 | COMPILER=$1 5 | 6 | # See Github hosted runners: 7 | # macos-14: https://github.com/actions/runner-images/blob/main/images/macos/macos-14-arm64-Readme.md 8 | # xcode 14.3.1, 15.0.1, 15.1, 15.2, 15.3, 15.4, 16.0 9 | # macos-13: https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md 10 | # xcode 14.1, 14.2, 14.3.1, 15.0.1, 15.1, 15.2 11 | # macos-12: https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md 12 | # xcode 13.1, 12.2.1, 13.3.1, 13.4.1, 14.1, 14.2 13 | # maxos-11: https://github.com/actions/runner-images/blob/main/images/macos/macos-11-Readme.md 14 | # xcode 11.7, 12.4, 12.5.1, 13.0, 13.1, 13.2.1 15 | # maxos-10.15: https://github.com/actions/runner-images/blob/main/images/macos/macos-10.15-Readme.md 16 | # xcode 10.3, 11.2.1, 11.3.1, 11.4.1, 11.5, 11.6, 11.7, 12, 12.1, 12.1.1, 12.2, 12.3, 12.4 17 | 18 | # Convert our compiler string into our XCode path 19 | # Paths must match Github Action virtual images 20 | if [[ $COMPILER == xcode10 ]]; then 21 | XCODE_PATH="/Applications/Xcode_10.3.app" 22 | elif [[ $COMPILER == xcode11 ]]; then 23 | XCODE_PATH="/Applications/Xcode_11.7.app" 24 | elif [[ $COMPILER == xcode12 ]]; then 25 | XCODE_PATH="/Applications/Xcode_12.5.1.app" 26 | elif [[ $COMPILER == xcode13 ]]; then 27 | XCODE_PATH="/Applications/Xcode_13.2.1.app" 28 | elif [[ $COMPILER == xcode14 ]]; then 29 | XCODE_PATH="/Applications/Xcode_14.3.1.app" 30 | elif [[ $COMPILER == xcode15 ]]; then 31 | XCODE_PATH="/Applications/Xcode_15.2.app" 32 | elif [[ $COMPILER == xcode16 ]]; then 33 | XCODE_PATH="/Applications/Xcode_16.0.app" 34 | fi 35 | 36 | # Select our XCode version 37 | sudo xcode-select -s $XCODE_PATH/Contents/Developer; 38 | -------------------------------------------------------------------------------- /tools/vs_visualizers/sjson-cpp.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {m_c_str,[m_length]s8} 7 | 8 | 9 | 10 | --------------------------------------------------------------------------------