├── .circleci └── config.yml ├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE.md ├── README.md ├── SPECIFICATION.md ├── appveyor.yml ├── benchmarks └── sajson │ ├── .gitignore │ ├── README.md │ ├── dub.json │ ├── source │ └── app.d │ └── testdata │ ├── apache_builds.json │ ├── github_events.json │ ├── instruments.json │ ├── mesh.json │ ├── mesh.pretty.json │ ├── nested.json │ ├── svg_menu.json │ ├── truenull.json │ ├── twitter.json │ ├── update-center.json │ └── whitespace.json ├── dub.json ├── examples ├── arrays_count │ ├── .gitignore │ ├── dub.json │ ├── input.jsonl │ └── source │ │ └── app.d └── filter_and_arrays │ ├── .gitignore │ ├── dub.json │ ├── input.jsonl │ └── source │ └── app.d ├── index.d ├── meson.build ├── meson_options.txt ├── source └── asdf │ ├── asdf.d │ ├── jsonbuffer.d │ ├── jsonparser.d │ ├── outputarray.d │ ├── package.d │ ├── serialization.d │ ├── transform.d │ └── utility.d ├── subprojects ├── mir-algorithm.wrap └── mir-core.wrap ├── test_parser.sh ├── test_parser ├── .gitignore ├── dub.json ├── run_asdf_tests.py ├── source │ └── app.d └── test_parser.sh └── test_travis.sh /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | mirci: libmir/upload_docs@0.1.4 5 | 6 | workflows: 7 | version: 2 8 | build-deploy: 9 | jobs: 10 | - mirci/test_and_build_docs: 11 | filters: 12 | tags: 13 | only: /^v(\d)+(\.(\d)+)+$/ 14 | - mirci/upload_docs: 15 | to: asdf.libmir.org 16 | requires: 17 | - mirci/test_and_build_docs 18 | filters: 19 | branches: 20 | ignore: /.*/ 21 | tags: 22 | only: /^v(\d)+(\.(\d)+)+$/ 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.o 5 | *.obj 6 | 7 | __test__library__ 8 | 9 | dub.selections.json 10 | 11 | *.sublime-project 12 | 13 | 1461741434.1461741945.100000.jsonl 14 | 15 | libasdf.a 16 | builddir 17 | example/filter_and_arrays/filter_and_arrays 18 | 19 | examples/filter_and_arrays/filter_and_arrays 20 | 21 | examples/arrays_count/arrays_count 22 | 23 | *.sublime-workspace 24 | 25 | *.s 26 | 27 | __test__native-sse42__ 28 | 29 | __test__generic__ 30 | 31 | .generated 32 | 33 | web 34 | 35 | *.lst 36 | 37 | test_parser/test_json-asdf 38 | 39 | JSONTestSuite/ 40 | 41 | sajson-benchmark/sajson-benchmark 42 | 43 | .vscode 44 | bin/ut.d 45 | asdf-test-library 46 | benchmarks/sajson/sajson-benchmark 47 | *.lib 48 | *.exe 49 | .vs 50 | build/ 51 | test.d 52 | test 53 | subprojects/*/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "doc/dlang.org"] 2 | path = doc/dlang.org 3 | url = https://github.com/dlang/dlang.org 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | - osx 4 | 5 | language: d 6 | d: 7 | - dmd 8 | - ldc 9 | - dmd-2.095.1 10 | - ldc-1.25.1 11 | 12 | env: 13 | - ARCH="x86_64" 14 | 15 | matrix: 16 | include: 17 | - os: linux 18 | d: ldc 19 | env: ARCH="AARCH64" 20 | arch: arm64 21 | - os: linux 22 | d: dmd 23 | env: ARCH="x86" 24 | addons: 25 | - apt: 26 | - packages: gcc-multilib 27 | - os: linux 28 | d: ldc 29 | env: ARCH="x86" 30 | addons: 31 | - apt: 32 | - packages: gcc-multilib 33 | 34 | branches: 35 | only: 36 | - master 37 | 38 | script: bash -e test_travis.sh 39 | after_success: 40 | - bash <(curl -s https://codecov.io/bash) 41 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Dub version](https://img.shields.io/dub/v/asdf.svg)](http://code.dlang.org/packages/asdf) 2 | [![Dub downloads](https://img.shields.io/dub/dt/asdf.svg)](http://code.dlang.org/packages/asdf) 3 | [![License](https://img.shields.io/dub/l/asdf.svg)](http://code.dlang.org/packages/asdf) 4 | [![codecov.io](https://codecov.io/github/libmir/asdf/coverage.svg?branch=master)](https://codecov.io/github/tamediadigital/asdf?branch=master) 5 | [![Build Status](https://travis-ci.org/libmir/asdf.svg?branch=master)](https://travis-ci.org/libmir/asdf) 6 | [![Circle CI Docs](https://circleci.com/gh/libmir/asdf.svg?style=shield&circle-token=:circle-ci-badge-token)](https://circleci.com/gh/libmir/asdf) 7 | [![Build status](https://ci.appveyor.com/api/projects/status/libmir/asdf?svg=true)](https://ci.appveyor.com/project/9il/asdf-sesrc) 8 | 9 | # A Simple Document Format 10 | 11 | ASDF is a cache oriented string based JSON representation. 12 | Besides, it is a convenient Json Library for D that gets out of your way. 13 | ASDF is specially geared towards transforming high volumes of JSON dataframes, either to new 14 | JSON Objects or to custom data types. 15 | 16 | #### Why ASDF? 17 | 18 | asdf was originally developed at [Tamedia](https://www.tamedia.ch/) to extract and transform real-time click streams. 19 | 20 | - ASDF is fast. It can be really helpful if you have gigabytes of JSON line separated values. 21 | - ASDF is simple. It uses D's modelling power to make you write less boilerplate code. 22 | - ASDF is tested and used in production for real World JSON generated by millions of web clients (we call it _the great fuzzer_). 23 | 24 | see also [github.com/tamediadigital/je](https://github.com/tamediadigital/je) a tool for fast extraction of json properties into a csv/tsv. 25 | 26 | #### Simple Example 27 | 28 | 1. define your struct 29 | 2. call `serializeToJson` ( or `serializeToJsonPretty` for pretty printing! ) 30 | 3. profit! 31 | 32 | ```D 33 | /+dub.sdl: 34 | dependency "asdf" version="~>0.2.5" 35 | 36 | #turns on SSE4.2 optimizations when compiled with LDC 37 | dflags "-mattr=+sse4.2" platform="ldc" 38 | +/ 39 | import asdf; 40 | 41 | struct Simple 42 | { 43 | string name; 44 | ulong level; 45 | } 46 | 47 | void main() 48 | { 49 | auto o = Simple("asdf", 42); 50 | string data = `{"name":"asdf","level":42}`; 51 | assert(o.serializeToJson() == data); 52 | assert(data.deserialize!Simple == o); 53 | } 54 | ``` 55 | #### Documentation 56 | 57 | See ASDF [API](http://asdf.libmir.org) and [Specification](https://github.com/tamediadigital/asdf/blob/master/SPECIFICATION.md). 58 | 59 | #### I/O Speed 60 | 61 | - Reading JSON line separated values and parsing them to ASDF - 300+ MB per second (SSD). 62 | - Writing ASDF range to JSON line separated values - 300+ MB per second (SSD). 63 | 64 | #### Fast setup with the dub package manager 65 | 66 | [![Dub version](https://img.shields.io/dub/v/asdf.svg)](http://code.dlang.org/packages/asdf) 67 | 68 | [Dub](https://code.dlang.org/getting_started) is D's package manager. 69 | You can create a new project with: 70 | 71 | ``` 72 | dub init 73 | ``` 74 | 75 | Now you need to edit the `dub.json` add `asdf` as dependency and set its targetType to `executable`. 76 | 77 | (dub.json) 78 | ```json 79 | { 80 | ... 81 | "dependencies": { 82 | "asdf": "~>" 83 | }, 84 | "targetType": "executable", 85 | "dflags-ldc": ["-mcpu=native"] 86 | } 87 | ``` 88 | 89 | (dub.sdl) 90 | ```sdl 91 | dependency "asdf" version="~>" 92 | targetType "executable" 93 | dflags "-mcpu=native" platform="ldc" 94 | ``` 95 | 96 | Now you can create a main file in the `source` and run your code with 97 | ``` 98 | dub 99 | ``` 100 | Flags `--build=release` and `--compiler=ldmd2` can be added for a performance boost: 101 | ``` 102 | dub --build=release --compiler=ldmd2 103 | ``` 104 | 105 | `ldmd2` is a shell on top of [LDC (LLVM D Compiler)](https://github.com/ldc-developers/ldc). 106 | `"dflags-ldc": ["-mcpu=native"]` allows LDC to optimize ASDF for your CPU. 107 | 108 | Instead of using `-mcpu=native`, you may specify an additional instruction set for a target with `-mattr`. 109 | For example, `-mattr=+sse4.2`. ASDF has specialized code for 110 | [SSE4.2](https://en.wikipedia.org/wiki/SSE4#SSE4.2 instruction set). 111 | 112 | #### Main transformation functions 113 | 114 | | uda | function | 115 | | ------------- |:-------------:| 116 | | `@serdeKeys("bar_common", "bar")` | tries to read the data from either property. saves it to the first one | 117 | | `@serdeKeysIn("a", "b")` | tries to read the data from `a`, then `b`. last one occuring in the json wins | 118 | | `@serdeKeyOut("a")` | writes it to `a` | 119 | | `@serdeIgnore` | ignore this property completely | 120 | | `@serdeIgnoreIn` | don't read this property | 121 | | `@serdeIgnoreOut` | don't write this property | 122 | | `@serdeIgnoreOutIf!condition` | run function `condition` on serialization and don't write this property if the result is true | 123 | | `@serdeScoped` | Dangerous! non allocating strings. this means data can vanish if the underlying buffer is removed. | 124 | | `@serdeProxy!string` | call to!string | 125 | | `@serdeTransformIn!fin` | call function `fin` to transform the data | 126 | | `@serdeTransformOut!fout` | run function `fout` on serialization, different notation | 127 | | `@serdeAllowMultiple` | Allows deserialiser to serialize multiple keys for the same object member input. | 128 | | `@serdeOptional` | Allows deserialiser to to skip member desrization of no keys corresponding keys input. | 129 | 130 | 131 | Please also look into the Docs or Unittest for concrete examples! 132 | 133 | #### ASDF Example (incomplete) 134 | 135 | ```D 136 | import std.algorithm; 137 | import std.stdio; 138 | import asdf; 139 | 140 | void main() 141 | { 142 | auto target = Asdf("red"); 143 | File("input.jsonl") 144 | // Use at least 4096 bytes for real world apps 145 | .byChunk(4096) 146 | // 32 is minimum size for internal buffer. Buffer can be reallocated to get more memory. 147 | .parseJsonByLine(4096) 148 | .filter!(object => object 149 | // opIndex accepts array of keys: {"key0": {"key1": { ... {"keyN-1": }... }}} 150 | ["colors"] 151 | // iterates over an array 152 | .byElement 153 | // Comparison with ASDF is little bit faster 154 | // than comparison with a string. 155 | .canFind(target)) 156 | //.canFind("red")) 157 | // Formatting uses internal buffer to reduce system delegate and system function calls 158 | .each!writeln; 159 | } 160 | ``` 161 | 162 | ##### Input 163 | 164 | Single object per line: 4th and 5th lines are broken. 165 | 166 | ```json 167 | null 168 | {"colors": ["red"]} 169 | {"a":"b", "colors": [4, "red", "string"]} 170 | {"colors":["red"], 171 | "comment" : "this is broken (multiline) object"} 172 | {"colors": "green"} 173 | {"colors": "red"]}} 174 | [] 175 | ``` 176 | 177 | ##### Output 178 | 179 | ```json 180 | {"colors":["red"]} 181 | {"a":"b","colors":[4,"red","string"]} 182 | ``` 183 | 184 | 185 | #### JSON and ASDF Serialization Examples 186 | 187 | ##### Simple struct or object 188 | ```d 189 | struct S 190 | { 191 | string a; 192 | long b; 193 | private int c; // private fields are ignored 194 | package int d; // package fields are ignored 195 | // all other fields in JSON are ignored 196 | } 197 | ``` 198 | 199 | ##### Selection 200 | ```d 201 | struct S 202 | { 203 | // ignored 204 | @serdeIgnore int temp; 205 | 206 | // can be formatted to json 207 | @serdeIgnoreIn int a; 208 | 209 | //can be parsed from json 210 | @serdeIgnoreOut int b; 211 | 212 | // ignored if negative 213 | @serdeIgnoreOutIf!`a < 0` int c; 214 | } 215 | ``` 216 | 217 | ##### Key overriding 218 | ```d 219 | struct S 220 | { 221 | // key is overrided to "aaa" 222 | @serdeKeys("aaa") int a; 223 | 224 | // overloads multiple keys for parsing 225 | @serdeKeysIn("b", "_b") 226 | // overloads key for generation 227 | @serdeKeyOut("_b_") 228 | int b; 229 | } 230 | ``` 231 | 232 | ##### User-Defined Serialization 233 | ```d 234 | struct DateTimeProxy 235 | { 236 | DateTime datetime; 237 | alias datetime this; 238 | 239 | SerdeException deserializeFromAsdf(Asdf data) 240 | { 241 | string val; 242 | if (auto exc = deserializeScopedString(data, val)) 243 | return exc; 244 | this = DateTimeProxy(DateTime.fromISOString(val)); 245 | return null; 246 | } 247 | 248 | void serialize(S)(ref S serializer) 249 | { 250 | serializer.putValue(datetime.toISOString); 251 | } 252 | } 253 | ``` 254 | 255 | ```d 256 | //serialize a Doubly Linked list into an Array 257 | struct SomeDoublyLinkedList 258 | { 259 | @serdeIgnore DList!(SomeArr[]) myDll; 260 | alias myDll this; 261 | 262 | //no template but a function this time! 263 | void serialize(ref AsdfSerializer serializer) 264 | { 265 | auto state = serializer.listBegin(); 266 | foreach (ref elem; myDll) 267 | { 268 | serializer.elemBegin; 269 | serializer.serializeValue(elem); 270 | } 271 | serializer.listEnd(state); 272 | } 273 | } 274 | ``` 275 | 276 | ##### Serialization Proxy 277 | ```d 278 | struct S 279 | { 280 | @serdeProxy!DateTimeProxy DateTime time; 281 | } 282 | ``` 283 | 284 | ```d 285 | @serdeProxy!ProxyE 286 | enum E 287 | { 288 | none, 289 | bar, 290 | } 291 | 292 | // const(char)[] doesn't reallocate ASDF data. 293 | @serdeProxy!(const(char)[]) 294 | struct ProxyE 295 | { 296 | E e; 297 | 298 | this(E e) 299 | { 300 | this.e = e; 301 | } 302 | 303 | this(in char[] str) 304 | { 305 | switch(str) 306 | { 307 | case "NONE": 308 | case "NA": 309 | case "N/A": 310 | e = E.none; 311 | break; 312 | case "BAR": 313 | case "BR": 314 | e = E.bar; 315 | break; 316 | default: 317 | throw new Exception("Unknown: " ~ cast(string)str); 318 | } 319 | } 320 | 321 | string toString() 322 | { 323 | if (e == E.none) 324 | return "NONE"; 325 | else 326 | return "BAR"; 327 | } 328 | 329 | E opCast(T : E)() 330 | { 331 | return e; 332 | } 333 | } 334 | 335 | unittest 336 | { 337 | assert(serializeToJson(E.bar) == `"BAR"`); 338 | assert(`"N/A"`.deserialize!E == E.none); 339 | assert(`"NA"`.deserialize!E == E.none); 340 | } 341 | ``` 342 | 343 | 344 | ##### Finalizer 345 | If you need to do additional calculations or etl transformations that happen to depend on the deserialized data use the `finalizeDeserialization` method. 346 | 347 | ```d 348 | struct S 349 | { 350 | string a; 351 | int b; 352 | 353 | @serdeIgnoreIn double sum; 354 | 355 | void finalizeDeserialization(Asdf data) 356 | { 357 | auto r = data["c", "d"]; 358 | auto a = r["e"].get(0.0); 359 | auto b = r["g"].get(0.0); 360 | sum = a + b; 361 | } 362 | } 363 | assert(`{"a":"bar","b":3,"c":{"d":{"e":6,"g":7}}}`.deserialize!S == S("bar", 3, 13)); 364 | ``` 365 | -------------------------------------------------------------------------------- /SPECIFICATION.md: -------------------------------------------------------------------------------- 1 | # Version v1.0-beta1 2 | 3 | ### Value 4 | 5 | ``` 6 | value ::= 7 | \x00 # null 8 | | \x01 # true 9 | | \x02 # false 10 | | \x03 number 11 | | \x05 string 12 | | \x09 array 13 | | \x0A object 14 | | \b1??????? # deleted 15 | 16 | number ::= number_length json_number_string 17 | 18 | number_length ::= uint8 19 | 20 | string ::= string_length string_data 21 | 22 | string_length ::= uint32 23 | ``` 24 | 25 | ### JSON character encoding 26 | Following JSON encoded characters are decoded to an appropriate unicode character. 27 | ``` 28 | \" 29 | \\ 30 | \/ 31 | \b 32 | \f 33 | \n 34 | \r 35 | \t 36 | \u-four-hex-digits 37 | ``` 38 | 39 | ### Array 40 | 41 | ``` 42 | array ::= array_length elements 43 | 44 | array_length ::= uint32 # size of elements 45 | 46 | elements 47 | element elements 48 | | < empty > 49 | ``` 50 | 51 | ### Object 52 | 53 | ``` 54 | object ::= object_length key_value_pairs 55 | 56 | object_length ::= uint32 # size of key_value_pairs 57 | 58 | key_value_pairs 59 | key value key_value_pairs 60 | | < empty > 61 | 62 | key ::= key_length string_data 63 | 64 | key_length ::= uint8 65 | ``` 66 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | platform: x64 2 | environment: 3 | matrix: 4 | - DC: ldc 5 | DVersion: 1.18.0 6 | arch: x64 7 | 8 | skip_tags: true 9 | branches: 10 | only: 11 | - master 12 | - stable 13 | 14 | install: 15 | - ps: function SetUpDCompiler 16 | { 17 | if($env:DC -eq "dmd"){ 18 | if($env:arch -eq "x86"){ 19 | $env:DConf = "m32"; 20 | } 21 | elseif($env:arch -eq "x64"){ 22 | $env:DConf = "m64"; 23 | } 24 | echo "downloading ..."; 25 | $env:toolchain = "msvc"; 26 | $version = $env:DVersion; 27 | Invoke-WebRequest "http://downloads.dlang.org/releases/2.x/$($version)/dmd.$($version).windows.7z" -OutFile "c:\dmd.7z"; 28 | echo "finished."; 29 | pushd c:\\; 30 | 7z x dmd.7z > $null; 31 | popd; 32 | } 33 | elseif($env:DC -eq "ldc"){ 34 | echo "downloading ..."; 35 | if($env:arch -eq "x86"){ 36 | $env:DConf = "m32"; 37 | } 38 | elseif($env:arch -eq "x64"){ 39 | $env:DConf = "m64"; 40 | } 41 | $env:toolchain = "msvc"; 42 | $version = $env:DVersion; 43 | Invoke-WebRequest "https://github.com/ldc-developers/ldc/releases/download/v$($version)/ldc2-$($version)-windows-x64.7z" -OutFile "c:\ldc.7z"; 44 | echo "finished."; 45 | pushd c:\\; 46 | 7z x ldc.7z > $null; 47 | popd; 48 | } 49 | } 50 | - ps: SetUpDCompiler 51 | - powershell -Command Invoke-WebRequest https://code.dlang.org/files/dub-1.9.0-windows-x86.zip -OutFile dub.zip 52 | - 7z x dub.zip -odub > nul 53 | - set PATH=%CD%\%binpath%;%CD%\dub;%PATH% 54 | - dub --version 55 | 56 | before_build: 57 | - ps: if($env:arch -eq "x86"){ 58 | $env:compilersetupargs = "x86"; 59 | $env:Darch = "x86"; 60 | } 61 | elseif($env:arch -eq "x64"){ 62 | $env:compilersetupargs = "amd64"; 63 | $env:Darch = "x86_64"; 64 | } 65 | - ps : if($env:DC -eq "dmd"){ 66 | $env:PATH += ";C:\dmd2\windows\bin;"; 67 | } 68 | elseif($env:DC -eq "ldc"){ 69 | $version = $env:DVersion; 70 | $env:PATH += ";C:\ldc2-$($version)-windows-x64\bin"; 71 | $env:DC = "ldc2"; 72 | } 73 | - ps: $env:compilersetup = "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall"; 74 | - '"%compilersetup%" %compilersetupargs%' 75 | 76 | build_script: 77 | - echo dummy build script - dont remove me 78 | 79 | test_script: 80 | - echo %PLATFORM% 81 | - echo %Darch% 82 | - echo %DC% 83 | - echo %PATH% 84 | - dub test --arch %Darch% --build=unittest-cov 85 | - dub test --arch %Darch% --build=unittest 86 | -------------------------------------------------------------------------------- /benchmarks/sajson/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.o 5 | *.obj 6 | __test__*__ 7 | -------------------------------------------------------------------------------- /benchmarks/sajson/README.md: -------------------------------------------------------------------------------- 1 | ## ASDF vs [sajson](https://github.com/chadaustin/sajson) Benchmark 2 | 3 | ### Platform 4 | Intel Haswell (AVX2), 5 | 6 | ### ASDF 7 | ``` 8 | dub build --build=release-nobounds --compiler=ldmd2 9 | ``` 10 | 11 | #### sajson 12 | ``` 13 | // sources are in sajson GitHub repository 14 | clang++ -O3 -march=native -std=c++14 benchmark/benchmark.cpp -Iinclude 15 | ``` 16 | 17 | ### Results 18 | 19 | | Test | sajson, avg μs | asdf, avg μs | Speedup | 20 | |---|---|---|---| 21 | | apache_builds | 142 | 93 | 53 % | 22 | | github_events | 78 | 44 | 77 % | 23 | | instruments | 272 | 182 | 49 % | 24 | | mesh | 1844 | 733 | 152 % | 25 | | mesh.pretty | 2728 | 1178 | 132 % | 26 | | nested | 93 | 111 | -16 % | 27 | | svg_menu | 2 | 1 | 100 % | 28 | | truenull | 18 | 10 | 80 % | 29 | | twitter | 919 | 461 | 99 % | 30 | | update-center | 838 | 405 | 107 % | 31 | | whitespace | 9 | 7 | 29 % | 32 | -------------------------------------------------------------------------------- /benchmarks/sajson/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sajson-benchmark", 3 | "authors": [ 4 | "Ilia Ki" 5 | ], 6 | "dependencies": { 7 | "asdf": {"path": "../../"} 8 | }, 9 | "description": "sajson benchmark https://github.com/chadaustin/sajson", 10 | "copyright": "Copyright © 2017, Tamedia Digital", 11 | "license": "BSL-1.0", 12 | "dflags-ldc" : ["-mcpu=native"] 13 | } -------------------------------------------------------------------------------- /benchmarks/sajson/source/app.d: -------------------------------------------------------------------------------- 1 | import asdf; 2 | 3 | import std.algorithm; 4 | import std.conv; 5 | import std.datetime; 6 | import std.file; 7 | 8 | static import std.stdio; 9 | 10 | immutable folder = "testdata/"; 11 | immutable files = 12 | [ 13 | "apache_builds.json", 14 | "github_events.json", 15 | "instruments.json", 16 | "mesh.json", 17 | "mesh.pretty.json", 18 | "nested.json", 19 | "svg_menu.json", 20 | "truenull.json", 21 | "twitter.json", 22 | "update-center.json", 23 | "whitespace.json", 24 | ]; 25 | 26 | immutable int max_string_length = files.map!"a.length".reduce!max + folder.length; 27 | 28 | void run_benchmark(string fileName) 29 | { 30 | auto text = cast(string) fileName.read; 31 | // text ~= '\0'; 32 | const size_t N = 1000; 33 | Duration minTime = Duration.max, avgTime; 34 | 35 | import std.experimental.allocator.mallocator : Mallocator; 36 | 37 | foreach (size_t i; 0 .. N) 38 | { 39 | auto sw = StopWatch(); 40 | sw.start; 41 | import std.typecons: No, Yes; 42 | auto json = text.parseJson!( 43 | Yes.includingNewLine, 44 | Yes.spaces, 45 | No.assumeValid, 46 | )(Mallocator.instance); 47 | sw.stop; 48 | Mallocator.instance.deallocate(json.data.ptr[0 .. text.length * 6]); 49 | auto time = sw.peek.to!Duration; 50 | avgTime += time; 51 | minTime = min(minTime, time); 52 | } 53 | avgTime /= N; 54 | std.stdio.writef("%*s - %0.3f ms - %0.3f ms\n", max_string_length, fileName, double(avgTime.total!"usecs") / 1000, double(minTime.total!"usecs") / 1000); 55 | } 56 | 57 | void main() 58 | { 59 | std.stdio.writef("%*s - %8s - %8s\n", max_string_length, "file", "avg", "min"); 60 | std.stdio.writef("%*s - %8s - %8s\n", max_string_length, "----", "---", "---"); 61 | 62 | foreach (file; files) 63 | run_benchmark(folder ~ file); 64 | } 65 | -------------------------------------------------------------------------------- /benchmarks/sajson/testdata/nested.json: -------------------------------------------------------------------------------- 1 | [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] -------------------------------------------------------------------------------- /benchmarks/sajson/testdata/svg_menu.json: -------------------------------------------------------------------------------- 1 | {"menu": { 2 | "header": "SVG Viewer", 3 | "items": [ 4 | {"id": "Open"}, 5 | {"id": "OpenNew", "label": "Open New"}, 6 | null, 7 | {"id": "ZoomIn", "label": "Zoom In"}, 8 | {"id": "ZoomOut", "label": "Zoom Out"}, 9 | {"id": "OriginalView", "label": "Original View"}, 10 | null, 11 | {"id": "Quality"}, 12 | {"id": "Pause"}, 13 | {"id": "Mute"}, 14 | null, 15 | {"id": "Find", "label": "Find..."}, 16 | {"id": "FindAgain", "label": "Find Again"}, 17 | {"id": "Copy"}, 18 | {"id": "CopyAgain", "label": "Copy Again"}, 19 | {"id": "CopySVG", "label": "Copy SVG"}, 20 | {"id": "ViewSVG", "label": "View SVG"}, 21 | {"id": "ViewSource", "label": "View Source"}, 22 | {"id": "SaveAs", "label": "Save As"}, 23 | null, 24 | {"id": "Help"}, 25 | {"id": "About", "label": "About Adobe CVG Viewer..."} 26 | ] 27 | }} 28 | -------------------------------------------------------------------------------- /benchmarks/sajson/testdata/truenull.json: -------------------------------------------------------------------------------- 1 | [true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null, true, null] -------------------------------------------------------------------------------- /benchmarks/sajson/testdata/whitespace.json: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | [] 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asdf", 3 | "description": "Fast, Expressive, and Easy to use JSON Serialization Library with optional SSE4 Optimization.", 4 | "license": "BSL-1.0", 5 | "authors": ["Ilia Ki", "Yannick Koechlin"], 6 | "copyright": "Tamedia Digital, 2016", 7 | "dependencies": { 8 | "mir-algorithm": ">=3.10.43" 9 | }, 10 | "buildTypes": { 11 | "unittest-sse42": { 12 | "buildOptions": ["unittests", "debugMode", "debugInfo"], 13 | "dflags-ldc": ["-mattr=+sse4.2"] 14 | }, 15 | "unittest-cov-sse42": { 16 | "buildOptions": ["unittests", "coverage", "debugMode", "debugInfo"], 17 | "dflags-ldc": ["-mattr=+sse4.2"] 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/arrays_count/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.o 5 | *.obj 6 | -------------------------------------------------------------------------------- /examples/arrays_count/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "arrays_count", 3 | "dependencies": { 4 | "asdf" : { 5 | "path" : "../../" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/arrays_count/input.jsonl: -------------------------------------------------------------------------------- 1 | {"colors": ["red"]} 2 | {"a":"b", "colors": [4, "red", "string"]} 3 | {"a":"b", "colors": [4, 244, 2324, "string"]} 4 | {"a":"b", "colors": [4, "green", "string"]} 5 | {"a":"b", "colors": [4, "blue", true, false, "string"]} 6 | {"colors": "green"} 7 | -------------------------------------------------------------------------------- /examples/arrays_count/source/app.d: -------------------------------------------------------------------------------- 1 | import std.algorithm; 2 | import std.range; 3 | import std.stdio; 4 | import asdf; 5 | 6 | void main() 7 | { 8 | foreach(a; 9 | File("input.jsonl") 10 | .byChunk(4096) 11 | .parseJsonByLine 12 | .map!(a => a["colors"])) 13 | { 14 | auto elems = a.byElement; 15 | auto count = elems.save.count; 16 | if(count < 2) 17 | writefln(`{"num_cols": %s}`, count); 18 | else 19 | writefln(`{"num_cols": %s, "fav_color": %s}`, count, elems.dropExactly(1).front); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/filter_and_arrays/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.o 5 | *.obj 6 | -------------------------------------------------------------------------------- /examples/filter_and_arrays/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "filter_and_arrays", 3 | "dependencies": { 4 | "asdf" : { 5 | "path" : "../../" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/filter_and_arrays/input.jsonl: -------------------------------------------------------------------------------- 1 | null 2 | {"colors": ["red"]} 3 | {"a":"b", "colors": [4, "red", "string"]} 4 | {"colors":["red"], 5 | "comment" : "this is broken (multiline) object"} 6 | {"colors": "green"} 7 | {"colors": "red"]}} 8 | [] 9 | -------------------------------------------------------------------------------- /examples/filter_and_arrays/source/app.d: -------------------------------------------------------------------------------- 1 | import std.algorithm; 2 | import std.stdio; 3 | import asdf; 4 | 5 | void main() 6 | { 7 | auto target = Asdf("red"); 8 | File("input.jsonl") 9 | .byChunk(10) // Use at least 4096 bytes for real world apps 10 | .parseJsonByLine 11 | .filter!(object => object["colors"] 12 | .byElement // iterates over an array 13 | .canFind(target)) // Comparison with ASDF is little bit faster than 14 | //.canFind("tadmp5800")) // comparison with a string. 15 | .each!writeln; // See also `lockingTextWriter` from `std.stdio`. 16 | } 17 | -------------------------------------------------------------------------------- /index.d: -------------------------------------------------------------------------------- 1 | Ddoc 2 | 3 | $(P JSON Parsing and Serialization library.) 4 | 5 | $(P The following table is a quick reference guide for which ASDF modules to 6 | use for a given category of functionality.) 7 | 8 | $(BOOKTABLE , 9 | $(TR 10 | $(TH Modules) 11 | $(TH Description) 12 | ) 13 | $(TR 14 | $(TDNW $(MREF0 asdf)) 15 | $(TD Publicly inludes 16 | $(MREF asdf,asdf), 17 | $(MREF asdf,jsonparser), 18 | $(MREF asdf,serialization), 19 | $(MREF asdf,transform). 20 | ) 21 | ) 22 | $(TR 23 | $(TDNW $(MREF asdf,asdf)) 24 | $(TD ASDF Representation) 25 | ) 26 | $(TR 27 | $(TDNW $(MREF asdf,jsonparser)) 28 | $(TD JSON Parsing API) 29 | ) 30 | $(TR 31 | $(TDNW $(MREF asdf,serialization)) 32 | $(TD ASDF and JSON Serialization) 33 | ) 34 | $(TR 35 | $(TDNW $(MREF asdf,transform)) 36 | $(TD Mutable ASDF data structure) 37 | ) 38 | ) 39 | 40 | Copyright: Copyright © 2020-, Ilia Ki. 41 | 42 | Macros: 43 | TITLE=ASDF 44 | WIKI=ASDF 45 | DDOC_BLANKLINE= 46 | _= 47 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('asdf', 'd', version : '0.5.5', license: 'BSL-1.0') 2 | 3 | description = 'Mir ASDF - JSON Parsing and Serialization' 4 | 5 | subprojects = ['mir-core', 'mir-algorithm'] 6 | 7 | has_cpp_headers = false 8 | 9 | sources_list = [ 10 | 'asdf/asdf', 11 | 'asdf/jsonbuffer', 12 | 'asdf/jsonparser', 13 | 'asdf/outputarray', 14 | 'asdf/package', 15 | 'asdf/serialization', 16 | 'asdf/transform', 17 | 'asdf/utility', 18 | ] 19 | 20 | sources = [] 21 | foreach s : sources_list 22 | sources += 'source/' + s + '.d' 23 | endforeach 24 | 25 | add_project_arguments([ 26 | '-preview=dip1008', 27 | '-lowmem', 28 | ], language: 'd') 29 | 30 | required_deps = [] 31 | 32 | foreach p : subprojects 33 | required_deps += dependency(p, fallback : [p, p.underscorify() + '_dep']) 34 | endforeach 35 | 36 | directories = ['source'] 37 | 38 | if has_cpp_headers 39 | directories += 'include' 40 | endif 41 | 42 | directories = include_directories(directories) 43 | 44 | this_lib = library(meson.project_name(), 45 | sources, 46 | include_directories: directories, 47 | install: true, 48 | version: meson.project_version(), 49 | dependencies: required_deps, 50 | ) 51 | 52 | this_dep = declare_dependency( 53 | link_with: [this_lib], 54 | include_directories: directories, 55 | dependencies: required_deps, 56 | ) 57 | 58 | test_versions = [] 59 | 60 | if has_cpp_headers 61 | install_subdir('include/', 62 | strip_directory :true, 63 | install_dir: 'include/', 64 | ) 65 | endif 66 | 67 | install_subdir('source/', 68 | strip_directory : true, 69 | install_dir: 'include/d/' + meson.project_name(), 70 | ) 71 | 72 | # import('pkgconfig').generate(this_lib, 73 | # description: description, 74 | # subdirs: 'd/' + meson.project_name(), 75 | # ) 76 | 77 | asdf_dep = this_dep 78 | asdf_lib = this_lib 79 | 80 | test_subdirs = [] 81 | 82 | if get_option('with_test_explicit') 83 | 84 | test_exe = executable(meson.project_name() + '-tset', 85 | sources, 86 | include_directories: directories, 87 | d_unittest: true, 88 | d_module_versions: test_versions, 89 | link_args: '-main', 90 | dependencies: required_deps, 91 | ) 92 | 93 | test(meson.project_name() + '-test', test_exe) 94 | 95 | foreach dir : test_subdirs 96 | subdir(dir) 97 | endforeach 98 | endif 99 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('with_test_explicit', type : 'boolean', value : false) 2 | -------------------------------------------------------------------------------- /source/asdf/asdf.d: -------------------------------------------------------------------------------- 1 | /++ 2 | ASDF Representation 3 | 4 | Copyright: Tamedia Digital, 2016 5 | 6 | Authors: Ilia Ki 7 | 8 | License: MIT 9 | 10 | Macros: 11 | SUBMODULE = $(LINK2 asdf_$1.html, asdf.$1) 12 | SUBREF = $(LINK2 asdf_$1.html#.$2, $(TT $2))$(NBSP) 13 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 14 | T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4)) 15 | +/ 16 | module asdf.asdf; 17 | 18 | import std.exception; 19 | import std.range.primitives; 20 | import std.typecons; 21 | import std.traits; 22 | 23 | import asdf.jsonbuffer; 24 | import asdf.jsonparser: assumePure; 25 | 26 | version(X86_64) 27 | version = X86_Any; 28 | else 29 | version(X86) 30 | version = X86_Any; 31 | 32 | version (D_Exceptions) 33 | { 34 | import mir.serde: SerdeException; 35 | /++ 36 | Serde Exception 37 | +/ 38 | class AsdfSerdeException : SerdeException 39 | { 40 | /// zero based faulty location 41 | size_t location; 42 | 43 | /// 44 | this( 45 | string msg, 46 | size_t location, 47 | string file = __FILE__, 48 | size_t line = __LINE__, 49 | ) pure nothrow @nogc @safe 50 | { 51 | this.location = location; 52 | super(msg, file, line); 53 | } 54 | 55 | /// 56 | this( 57 | string msg, 58 | string file = __FILE__, 59 | size_t line = __LINE__, 60 | Throwable next = null) pure nothrow @nogc @safe 61 | { 62 | super(msg, file, line, next); 63 | } 64 | 65 | /// 66 | this( 67 | string msg, 68 | Throwable next, 69 | string file = __FILE__, 70 | size_t line = __LINE__, 71 | ) pure nothrow @nogc @safe 72 | { 73 | this(msg, file, line, next); 74 | } 75 | 76 | override AsdfSerdeException toMutable() @trusted pure nothrow @nogc const 77 | { 78 | return cast() this; 79 | } 80 | 81 | alias toMutable this; 82 | } 83 | } 84 | 85 | deprecated("use mir.serde: SerdeException instead") 86 | alias AsdfException = SerdeException; 87 | 88 | /// 89 | class InvalidAsdfException: SerdeException 90 | { 91 | /// 92 | this( 93 | uint kind, 94 | string file = __FILE__, 95 | size_t line = __LINE__, 96 | Throwable next = null) pure nothrow @safe 97 | { 98 | import mir.format: text; 99 | super(text("ASDF values is invalid for kind = ", kind), file, line, next); 100 | } 101 | 102 | /// 103 | this( 104 | uint kind, 105 | Throwable next, 106 | string file = __FILE__, 107 | size_t line = __LINE__, 108 | ) pure nothrow @safe 109 | { 110 | this(kind, file, line, next); 111 | } 112 | } 113 | 114 | private void enforceValidAsdf( 115 | bool condition, 116 | uint kind, 117 | string file = __FILE__, 118 | size_t line = __LINE__) @safe pure 119 | { 120 | if(!condition) 121 | throw new InvalidAsdfException(kind, file, line); 122 | } 123 | 124 | /// 125 | class EmptyAsdfException: SerdeException 126 | { 127 | /// 128 | this( 129 | string msg = "ASDF value is empty", 130 | string file = __FILE__, 131 | size_t line = __LINE__, 132 | Throwable next = null) pure nothrow @nogc @safe 133 | { 134 | super(msg, file, line, next); 135 | } 136 | } 137 | 138 | /++ 139 | The structure for ASDF manipulation. 140 | +/ 141 | struct Asdf 142 | { 143 | /// 144 | enum Kind : ubyte 145 | { 146 | /// 147 | null_ = 0x00, 148 | /// 149 | true_ = 0x01, 150 | /// 151 | false_ = 0x02, 152 | /// 153 | number = 0x03, 154 | /// 155 | string = 0x05, 156 | /// 157 | array = 0x09, 158 | /// 159 | object = 0x0A, 160 | } 161 | 162 | /// Returns ASDF Kind 163 | ubyte kind() const pure @safe @nogc 164 | { 165 | if (!data.length) 166 | { 167 | static immutable exc = new EmptyAsdfException; 168 | throw exc; 169 | } 170 | return data[0]; 171 | } 172 | 173 | /++ 174 | Plain ASDF data. 175 | +/ 176 | ubyte[] data; 177 | 178 | /// Creates ASDF using already allocated data 179 | this(ubyte[] data) pure @safe nothrow @nogc 180 | { 181 | this.data = data; 182 | } 183 | 184 | /// Creates ASDF from a string 185 | this(in char[] str) pure @safe 186 | { 187 | data = new ubyte[str.length + 5]; 188 | data[0] = Kind.string; 189 | length4 = str.length; 190 | data[5 .. $] = cast(const(ubyte)[])str; 191 | } 192 | 193 | /// 194 | unittest 195 | { 196 | assert(Asdf("string") == "string"); 197 | assert(Asdf("string") != "String"); 198 | } 199 | 200 | // \uXXXX character support 201 | unittest 202 | { 203 | import mir.conv: to; 204 | import asdf.jsonparser; 205 | assert(Asdf("begin\u000bend").to!string == `"begin\u000Bend"`); 206 | assert("begin\u000bend" == cast(string) `"begin\u000Bend"`.parseJson, to!string(cast(ubyte[]) cast(string)( `"begin\u000Bend"`.parseJson))); 207 | } 208 | 209 | /// Sets deleted bit on 210 | void remove() pure @safe nothrow @nogc 211 | { 212 | if(data.length) 213 | data[0] |= 0x80; 214 | } 215 | 216 | /// 217 | unittest 218 | { 219 | import mir.conv: to; 220 | import asdf.jsonparser; 221 | auto asdfData = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`.parseJson; 222 | asdfData["inner", "d"].remove; 223 | assert(asdfData.to!string == `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","e":{}}}`); 224 | } 225 | 226 | /// 227 | void toString(Dg)(scope Dg sink) const 228 | { 229 | scope buffer = JsonBuffer!Dg(sink); 230 | toStringImpl(buffer); 231 | buffer.flush; 232 | } 233 | 234 | /+ 235 | Internal recursive toString implementation. 236 | Params: 237 | sink = output range that accepts `char`, `in char[]` and compile time string `(string str)()` 238 | +/ 239 | private void toStringImpl(Dg)(ref JsonBuffer!Dg sink) const 240 | { 241 | if (!data.length) 242 | { 243 | static immutable exc = new EmptyAsdfException("Data buffer is empty"); 244 | throw exc; 245 | } 246 | auto t = data[0]; 247 | switch(t) 248 | { 249 | case Kind.null_: 250 | enforceValidAsdf(data.length == 1, t); 251 | sink.put!"null"; 252 | break; 253 | case Kind.true_: 254 | enforceValidAsdf(data.length == 1, t); 255 | sink.put!"true"; 256 | break; 257 | case Kind.false_: 258 | enforceValidAsdf(data.length == 1, t); 259 | sink.put!"false"; 260 | break; 261 | case Kind.number: 262 | enforceValidAsdf(data.length > 1, t); 263 | size_t length = data[1]; 264 | enforceValidAsdf(data.length == length + 2, t); 265 | sink.putSmallEscaped(cast(const(char)[]) data[2 .. $]); 266 | break; 267 | case Kind.string: 268 | enforceValidAsdf(data.length >= 5, Kind.object); 269 | enforceValidAsdf(data.length == length4 + 5, t); 270 | sink.put('"'); 271 | sink.put(cast(const(char)[]) data[5 .. $]); 272 | sink.put('"'); 273 | break; 274 | case Kind.array: 275 | auto elems = Asdf(cast(ubyte[])data).byElement; 276 | if(elems.empty) 277 | { 278 | sink.put!"[]"; 279 | break; 280 | } 281 | sink.put('['); 282 | elems.front.toStringImpl(sink); 283 | elems.popFront; 284 | foreach(e; elems) 285 | { 286 | sink.put(','); 287 | e.toStringImpl(sink); 288 | } 289 | sink.put(']'); 290 | break; 291 | case Kind.object: 292 | auto pairs = Asdf(cast(ubyte[])data).byKeyValue; 293 | if(pairs.empty) 294 | { 295 | sink.put!"{}"; 296 | break; 297 | } 298 | sink.put!"{\""; 299 | sink.put(pairs.front.key); 300 | sink.put!"\":"; 301 | pairs.front.value.toStringImpl(sink); 302 | pairs.popFront; 303 | foreach(e; pairs) 304 | { 305 | sink.put!",\""; 306 | sink.put(e.key); 307 | sink.put!"\":"; 308 | e.value.toStringImpl(sink); 309 | } 310 | sink.put('}'); 311 | break; 312 | default: 313 | enforceValidAsdf(0, t); 314 | } 315 | } 316 | 317 | /// 318 | unittest 319 | { 320 | import mir.conv: to; 321 | import asdf.jsonparser; 322 | auto text = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`; 323 | const asdfData = text.parseJson; 324 | assert(asdfData.to!string == text); 325 | } 326 | 327 | /++ 328 | `==` operator overloads for `null` 329 | +/ 330 | bool opEquals(in Asdf rhs) const @safe pure nothrow @nogc 331 | { 332 | return data == rhs.data; 333 | } 334 | 335 | /// 336 | unittest 337 | { 338 | import asdf.jsonparser; 339 | auto asdfData = `null`.parseJson; 340 | assert(asdfData == asdfData); 341 | } 342 | 343 | /++ 344 | `==` operator overloads for `null` 345 | +/ 346 | bool opEquals(typeof(null)) const pure @safe nothrow 347 | { 348 | return data.length == 1 && data[0] == 0; 349 | } 350 | 351 | /// 352 | unittest 353 | { 354 | import asdf.jsonparser; 355 | auto asdfData = `null`.parseJson; 356 | assert(asdfData == null); 357 | } 358 | 359 | /++ 360 | `==` operator overloads for `bool` 361 | +/ 362 | bool opEquals(bool boolean) const pure @safe nothrow 363 | { 364 | return data.length == 1 && (data[0] == Kind.true_ && boolean || data[0] == Kind.false_ && !boolean); 365 | } 366 | 367 | /// 368 | unittest 369 | { 370 | import asdf.jsonparser; 371 | auto asdfData = `true`.parseJson; 372 | assert(asdfData == true); 373 | assert(asdfData != false); 374 | } 375 | 376 | /++ 377 | `==` operator overloads for `string` 378 | +/ 379 | bool opEquals(in char[] str) const pure @trusted nothrow 380 | { 381 | return data.length >= 5 && data[0] == Kind.string && data[5 .. 5 + length4] == cast(const(ubyte)[]) str; 382 | } 383 | 384 | /// 385 | unittest 386 | { 387 | import asdf.jsonparser; 388 | auto asdfData = `"str"`.parseJson; 389 | assert(asdfData == "str"); 390 | assert(asdfData != "stR"); 391 | } 392 | 393 | /++ 394 | Returns: 395 | input range composed of elements of an array. 396 | +/ 397 | auto byElement() pure 398 | { 399 | static struct Range 400 | { 401 | private ubyte[] _data; 402 | private Asdf _front; 403 | 404 | auto save()() @safe pure @property 405 | { 406 | return this; 407 | } 408 | 409 | void popFront() @safe pure 410 | { 411 | while(!_data.empty) 412 | { 413 | uint t = cast(ubyte) _data.front; 414 | switch(t) 415 | { 416 | case Kind.null_: 417 | case Kind.true_: 418 | case Kind.false_: 419 | _front = Asdf(_data[0 .. 1]); 420 | _data.popFront; 421 | return; 422 | case Kind.number: 423 | enforceValidAsdf(_data.length >= 2, t); 424 | size_t len = _data[1] + 2; 425 | enforceValidAsdf(_data.length >= len, t); 426 | _front = Asdf(_data[0 .. len]); 427 | _data = _data[len .. $]; 428 | return; 429 | case Kind.string: 430 | case Kind.array: 431 | case Kind.object: 432 | enforceValidAsdf(_data.length >= 5, t); 433 | size_t len = Asdf(_data).length4 + 5; 434 | enforceValidAsdf(_data.length >= len, t); 435 | _front = Asdf(_data[0 .. len]); 436 | _data = _data[len .. $]; 437 | return; 438 | case 0x80 | Kind.null_: 439 | case 0x80 | Kind.true_: 440 | case 0x80 | Kind.false_: 441 | _data.popFront; 442 | continue; 443 | case 0x80 | Kind.number: 444 | enforceValidAsdf(_data.length >= 2, t); 445 | _data.popFrontExactly(_data[1] + 2); 446 | continue; 447 | case 0x80 | Kind.string: 448 | case 0x80 | Kind.array: 449 | case 0x80 | Kind.object: 450 | enforceValidAsdf(_data.length >= 5, t); 451 | size_t len = Asdf(_data).length4 + 5; 452 | _data.popFrontExactly(len); 453 | continue; 454 | default: 455 | enforceValidAsdf(0, t); 456 | } 457 | } 458 | _front = Asdf.init; 459 | } 460 | 461 | auto front() pure @property 462 | { 463 | assert(!empty); 464 | return _front; 465 | } 466 | 467 | bool empty() @safe pure @property 468 | { 469 | return _front.data.length == 0; 470 | } 471 | } 472 | if(data.empty || data[0] != Kind.array) 473 | return Range.init; 474 | enforceValidAsdf(data.length >= 5, Kind.array); 475 | enforceValidAsdf(length4 == data.length - 5, Kind.array); 476 | auto ret = Range(data[5 .. $]); 477 | if(ret._data.length) 478 | ret.popFront; 479 | return ret; 480 | } 481 | 482 | /++ 483 | Returns: 484 | Input range composed of key-value pairs of an object. 485 | Elements are type of `Tuple!(const(char)[], "key", Asdf, "value")`. 486 | +/ 487 | auto byKeyValue() pure 488 | { 489 | static struct Range 490 | { 491 | private ubyte[] _data; 492 | private Tuple!(const(char)[], "key", Asdf, "value") _front; 493 | 494 | auto save() @safe pure @property 495 | { 496 | return this; 497 | } 498 | 499 | void popFront() @safe pure 500 | { 501 | while(!_data.empty) 502 | { 503 | enforceValidAsdf(_data.length > 1, Kind.object); 504 | size_t l = cast(ubyte) _data[0]; 505 | _data.popFront; 506 | enforceValidAsdf(_data.length >= l, Kind.object); 507 | _front.key = cast(const(char)[])_data[0 .. l]; 508 | _data.popFrontExactly(l); 509 | uint t = cast(ubyte) _data.front; 510 | switch(t) 511 | { 512 | case Kind.null_: 513 | case Kind.true_: 514 | case Kind.false_: 515 | _front.value = Asdf(_data[0 .. 1]); 516 | _data.popFront; 517 | return; 518 | case Kind.number: 519 | enforceValidAsdf(_data.length >= 2, t); 520 | size_t len = _data[1] + 2; 521 | enforceValidAsdf(_data.length >= len, t); 522 | _front.value = Asdf(_data[0 .. len]); 523 | _data = _data[len .. $]; 524 | return; 525 | case Kind.string: 526 | case Kind.array: 527 | case Kind.object: 528 | enforceValidAsdf(_data.length >= 5, t); 529 | size_t len = Asdf(_data).length4 + 5; 530 | enforceValidAsdf(_data.length >= len, t); 531 | _front.value = Asdf(_data[0 .. len]); 532 | _data = _data[len .. $]; 533 | return; 534 | case 0x80 | Kind.null_: 535 | case 0x80 | Kind.true_: 536 | case 0x80 | Kind.false_: 537 | _data.popFront; 538 | continue; 539 | case 0x80 | Kind.number: 540 | enforceValidAsdf(_data.length >= 2, t); 541 | _data.popFrontExactly(_data[1] + 2); 542 | continue; 543 | case 0x80 | Kind.string: 544 | case 0x80 | Kind.array: 545 | case 0x80 | Kind.object: 546 | enforceValidAsdf(_data.length >= 5, t); 547 | size_t len = Asdf(_data).length4 + 5; 548 | _data.popFrontExactly(len); 549 | continue; 550 | default: 551 | enforceValidAsdf(0, t); 552 | } 553 | } 554 | _front = _front.init; 555 | } 556 | 557 | auto front() pure @property 558 | { 559 | assert(!empty); 560 | return _front; 561 | } 562 | 563 | bool empty() @safe pure @property 564 | { 565 | return _front.value.data.length == 0; 566 | } 567 | } 568 | if(data.empty || data[0] != Kind.object) 569 | return Range.init; 570 | enforceValidAsdf(data.length >= 5, Kind.object); 571 | enforceValidAsdf(length4 == data.length - 5, Kind.object); 572 | auto ret = Range(data[5 .. $]); 573 | if(ret._data.length) 574 | ret.popFront; 575 | return ret; 576 | } 577 | 578 | /// returns 4-byte length 579 | private size_t length4() const @property pure nothrow @nogc @trusted 580 | { 581 | assert(data.length >= 5); 582 | version(X86_Any) 583 | { 584 | return (cast(uint*)(data.ptr + 1))[0]; 585 | } 586 | else 587 | { 588 | align(4) auto ret = *cast(ubyte[4]*)(data.ptr + 1); 589 | return (cast(uint[1])ret)[0]; 590 | } 591 | } 592 | 593 | /// ditto 594 | private void length4(size_t len) const @property pure nothrow @nogc @trusted 595 | { 596 | assert(data.length >= 5); 597 | assert(len <= uint.max); 598 | version(X86_Any) 599 | { 600 | *(cast(uint*)(data.ptr + 1)) = cast(uint) len; 601 | } 602 | else 603 | { 604 | *(cast(ubyte[4]*)(data.ptr + 1)) = cast(ubyte[4]) cast(uint[1]) [cast(uint) len]; 605 | } 606 | } 607 | 608 | /++ 609 | Searches for a value recursively in an ASDF object. 610 | 611 | Params: 612 | keys = list of keys keys 613 | Returns 614 | ASDF value if it was found (first win) or ASDF with empty plain data. 615 | +/ 616 | Asdf opIndex(in char[][] keys...) @safe pure 617 | { 618 | auto asdf = this; 619 | if(asdf.data.empty) 620 | return Asdf.init; 621 | L: foreach(key; keys) 622 | { 623 | if(asdf.data[0] != Asdf.Kind.object) 624 | return Asdf.init; 625 | foreach(e; asdf.byKeyValue) 626 | { 627 | if(e.key == key) 628 | { 629 | asdf = e.value; 630 | continue L; 631 | } 632 | } 633 | return Asdf.init; 634 | } 635 | return asdf; 636 | } 637 | 638 | /// 639 | unittest 640 | { 641 | import asdf.jsonparser; 642 | auto asdfData = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`.parseJson; 643 | assert(asdfData["inner", "a"] == true); 644 | assert(asdfData["inner", "b"] == false); 645 | assert(asdfData["inner", "c"] == "32323"); 646 | assert(asdfData["inner", "d"] == null); 647 | assert(asdfData["no", "such", "keys"] == Asdf.init); 648 | } 649 | 650 | /++ 651 | Params: 652 | def = default value. It is used when ASDF value equals `Asdf.init`. 653 | Returns: 654 | `cast(T) this` if `this != Asdf.init` and `def` otherwise. 655 | +/ 656 | T get(T)(T def) 657 | { 658 | if(data.length) 659 | { 660 | return cast(T) this; 661 | } 662 | return def; 663 | } 664 | 665 | /// 666 | unittest 667 | { 668 | import asdf.jsonparser; 669 | auto asdfData = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`.parseJson; 670 | assert(asdfData["inner", "a"].get(false) == true); 671 | assert(asdfData["inner", "b"].get(true) == false); 672 | assert(asdfData["inner", "c"].get(100) == 32323); 673 | assert(asdfData["no", "such", "keys"].get(100) == 100); 674 | } 675 | 676 | /++ 677 | `cast` operator overloading. 678 | +/ 679 | T opCast(T)() 680 | { 681 | import std.datetime: SysTime, DateTime, usecs, UTC; 682 | import std.traits: isNumeric; 683 | import mir.conv: to; 684 | import std.conv: ConvException; 685 | import std.format: format; 686 | import std.math: trunc; 687 | import asdf.serialization; 688 | auto k = kind; 689 | with(Kind) switch(kind) 690 | { 691 | case null_ : 692 | static if (isNumeric!T 693 | || is(T == interface) 694 | || is(T == class) 695 | || is(T == E[], E) 696 | || is(T == E[K], E, K) 697 | || is(T == bool)) 698 | return T.init; 699 | else goto default; 700 | case true_ : 701 | static if(__traits(compiles, true.to!T)) 702 | return true.to!T; 703 | else goto default; 704 | case false_: 705 | static if(__traits(compiles, false.to!T)) 706 | return false.to!T; 707 | else goto default; 708 | case number: 709 | { 710 | auto str = cast(const(char)[]) data[2 .. $]; 711 | static if(is(T == bool)) 712 | return assumePure(() => str.to!double)() != 0; 713 | else 714 | static if(is(T == SysTime) || is(T == DateTime)) 715 | { 716 | auto unixTime = assumePure(() => str.to!real)(); 717 | auto secsR = assumePure(() => unixTime.trunc)(); 718 | auto rem = unixTime - secsR; 719 | auto st = SysTime.fromUnixTime(cast(long)(secsR), UTC()); 720 | assumePure((ref SysTime st) => st.fracSecs = usecs(cast(long)(rem * 1_000_000)))(st); 721 | return assumePure(() => st.to!T)(); 722 | } 723 | else 724 | static if(__traits(compiles, assumePure(() => str.to!T)())) 725 | { 726 | static if (isFloatingPoint!T) 727 | { 728 | import mir.bignum.internal.dec2float: decimalToFloatImpl; 729 | import mir.bignum.internal.parse: parseJsonNumberImpl; 730 | auto result = str.parseJsonNumberImpl; 731 | if (!result.success) 732 | throw new Exception("Failed to deserialize number"); 733 | 734 | auto fp = decimalToFloatImpl!(Unqual!T)(result.coefficient, result.exponent); 735 | if (result.sign) 736 | fp = -fp; 737 | return fp; 738 | } 739 | else 740 | { 741 | return assumePure(() => str.to!T)(); 742 | } 743 | } 744 | else goto default; 745 | } 746 | case string: 747 | { 748 | auto str = cast(const(char)[]) data[5 .. $]; 749 | static if(is(T == bool)) 750 | return str != "0" && str != "false" && str != ""; 751 | else 752 | static if(__traits(compiles, str.to!T)) 753 | return str.to!T; 754 | else goto default; 755 | } 756 | static if (isAggregateType!T || isArray!T) 757 | { 758 | case array : 759 | case object: 760 | static if(__traits(compiles, {T t = deserialize!T(this);})) 761 | return deserialize!T(this); 762 | else goto default; 763 | } 764 | default: 765 | throw new ConvException(format("Cannot convert kind %s(\\x%02X) to %s", cast(Kind) k, k, T.stringof)); 766 | } 767 | } 768 | 769 | /// null 770 | unittest 771 | { 772 | import std.math; 773 | import asdf.serialization; 774 | auto null_ = serializeToAsdf(null); 775 | interface I {} 776 | class C {} 777 | assert(cast(uint[]) null_ is null); 778 | assert(cast(uint[uint]) null_ is null); 779 | assert(cast(I) null_ is null); 780 | assert(cast(C) null_ is null); 781 | assert(isNaN(cast(double) null_)); 782 | assert(! cast(bool) null_); 783 | } 784 | 785 | /// boolean 786 | unittest 787 | { 788 | import std.math; 789 | import asdf.serialization; 790 | auto true_ = serializeToAsdf(true); 791 | auto false_ = serializeToAsdf(false); 792 | static struct C { 793 | this(bool){} 794 | } 795 | auto a = cast(C) true_; 796 | auto b = cast(C) false_; 797 | assert(cast(bool) true_ == true); 798 | assert(cast(bool) false_ == false); 799 | assert(cast(uint) true_ == 1); 800 | assert(cast(uint) false_ == 0); 801 | assert(cast(double) true_ == 1); 802 | assert(cast(double) false_ == 0); 803 | } 804 | 805 | /// numbers 806 | unittest 807 | { 808 | import std.bigint; 809 | import asdf.serialization; 810 | auto number = serializeToAsdf(1234); 811 | auto zero = serializeToAsdf(0); 812 | static struct C 813 | { 814 | this(in char[] numberString) 815 | { 816 | assert(numberString == "1234"); 817 | } 818 | } 819 | auto a = cast(C) number; 820 | assert(cast(bool) number == true); 821 | assert(cast(bool) zero == false); 822 | assert(cast(uint) number == 1234); 823 | assert(cast(double) number == 1234); 824 | assert(cast(BigInt) number == 1234); 825 | assert(cast(uint) zero == 0); 826 | assert(cast(double) zero == 0); 827 | assert(cast(BigInt) zero == 0); 828 | } 829 | 830 | /// string 831 | unittest 832 | { 833 | import std.bigint; 834 | import asdf.serialization; 835 | auto number = serializeToAsdf("1234"); 836 | auto false_ = serializeToAsdf("false"); 837 | auto bar = serializeToAsdf("bar"); 838 | auto zero = serializeToAsdf("0"); 839 | static struct C 840 | { 841 | this(in char[] str) 842 | { 843 | assert(str == "1234"); 844 | } 845 | } 846 | auto a = cast(C) number; 847 | assert(cast(string) number == "1234"); 848 | assert(cast(bool) number == true); 849 | assert(cast(bool) bar == true); 850 | assert(cast(bool) zero == false); 851 | assert(cast(bool) false_ == false); 852 | assert(cast(uint) number == 1234); 853 | assert(cast(double) number == 1234); 854 | assert(cast(BigInt) number == 1234); 855 | assert(cast(uint) zero == 0); 856 | assert(cast(double) zero == 0); 857 | assert(cast(BigInt) zero == 0); 858 | } 859 | 860 | /++ 861 | For ASDF arrays and objects `cast(T)` just returns `this.deserialize!T`. 862 | +/ 863 | unittest 864 | { 865 | import std.bigint; 866 | import asdf.serialization; 867 | assert(cast(int[]) serializeToAsdf([100, 20]) == [100, 20]); 868 | } 869 | 870 | /// UNIX Time 871 | unittest 872 | { 873 | import std.datetime; 874 | import asdf.serialization; 875 | 876 | auto num = serializeToAsdf(0.123456789); // rounding up to usecs 877 | assert(cast(DateTime) num == DateTime(1970, 1, 1)); 878 | assert(cast(SysTime) num == SysTime(DateTime(1970, 1, 1), usecs(123456), UTC())); // UTC time zone is used. 879 | } 880 | } 881 | -------------------------------------------------------------------------------- /source/asdf/jsonbuffer.d: -------------------------------------------------------------------------------- 1 | module asdf.jsonbuffer; 2 | 3 | package struct JsonBuffer(Dg) 4 | { 5 | Dg sink; 6 | // current buffer length 7 | size_t length; 8 | 9 | char[4096 * 4] buffer = void; 10 | 11 | /+ 12 | Puts char 13 | +/ 14 | void put(char c) 15 | { 16 | if(length == buffer.length) 17 | { 18 | flush; 19 | } 20 | buffer[length++] = c; 21 | } 22 | 23 | /+ 24 | Uses compile time loop for values `null`, `true`, `false` 25 | +/ 26 | void put(string str)() 27 | { 28 | size_t newLength = length + str.length; 29 | if(newLength > buffer.length) 30 | { 31 | flush; 32 | newLength = str.length; 33 | } 34 | import asdf.utility; 35 | // compile time loop 36 | foreach(i; Iota!(0, str.length)) 37 | buffer[length + i] = str[i]; 38 | length = newLength; 39 | } 40 | 41 | /+ 42 | Puts key/number 43 | +/ 44 | void putSmallEscaped(in char[] str) 45 | { 46 | assert(str.length <= ubyte.max); 47 | size_t newLength = length + str.length; 48 | if(newLength > buffer.length) 49 | { 50 | flush; 51 | newLength = str.length; 52 | } 53 | buffer[length .. newLength] = str; 54 | length = newLength; 55 | } 56 | 57 | /++ 58 | Decodes byte `b` to the form `u00XX`, where `XX` is 2 hexadecimal characters. 59 | +/ 60 | private void putUnicode(ubyte b) 61 | { 62 | buffer[length + 0] = 'u'; 63 | buffer[length + 1] = '0'; 64 | buffer[length + 2] = '0'; 65 | ubyte[2] spl; 66 | spl[0] = b >> 4; 67 | spl[1] = b & 0xF; 68 | buffer[length + 3] = cast(ubyte)(spl[0] < 10 ? spl[0] + '0' : spl[0] - 10 + 'A'); 69 | buffer[length + 4] = cast(ubyte)(spl[1] < 10 ? spl[1] + '0' : spl[1] - 10 + 'A'); 70 | length += 5; 71 | } 72 | 73 | /+ 74 | Puts string 75 | +/ 76 | void put(in char[] str) 77 | { 78 | import std.range: chunks; 79 | import std.string: representation; 80 | 81 | version(SSE42) 82 | { 83 | import core.simd; 84 | import asdf.simd; 85 | import ldc.gccbuiltins_x86; 86 | 87 | enum byte16 str2E = [ 88 | '\u0001', '\u001F', 89 | '\"', '\"', 90 | '\\', '\\', 91 | '\u007f', '\u007f', 92 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0']; 93 | enum byte16 str3E = ['\"', '\\', '\b', '\f', '\n', '\r', '\t', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0']; 94 | byte16 str2 = str2E; 95 | byte16 str3 = str3E; 96 | 97 | static immutable emap = ['\"', '\\', 'b', 'f', 'n', 'r', 't']; 98 | 99 | for(auto d = str.representation; d.length;) 100 | { 101 | if(length + 21 > buffer.length) 102 | { 103 | flush; 104 | } 105 | int ecx = void; 106 | byte16 str1 = void; 107 | if(d.length >= 16) 108 | { 109 | str1 = loadUnaligned!byte16(cast(ubyte*) d.ptr); 110 | storeUnaligned!byte16(str1, cast(ubyte*) buffer.ptr + length); 111 | auto cflag = __builtin_ia32_pcmpistric128(str2, str1, 0x04); 112 | ecx = __builtin_ia32_pcmpistri128 (str2, str1, 0x04); 113 | d = d[ecx .. $]; 114 | length += ecx; 115 | if(ecx == 16) 116 | continue; 117 | } 118 | else 119 | { 120 | str1 ^= str1; 121 | switch(d.length) 122 | { 123 | default : goto case; 124 | case 0xE+1: str1.array[0xE] = d[0xE]; goto case; 125 | case 0xD+1: str1.array[0xD] = d[0xD]; goto case; 126 | case 0xC+1: str1.array[0xC] = d[0xC]; goto case; 127 | case 0xB+1: str1.array[0xB] = d[0xB]; goto case; 128 | case 0xA+1: str1.array[0xA] = d[0xA]; goto case; 129 | case 0x9+1: str1.array[0x9] = d[0x9]; goto case; 130 | case 0x8+1: str1.array[0x8] = d[0x8]; goto case; 131 | case 0x7+1: str1.array[0x7] = d[0x7]; goto case; 132 | case 0x6+1: str1.array[0x6] = d[0x6]; goto case; 133 | case 0x5+1: str1.array[0x5] = d[0x5]; goto case; 134 | case 0x4+1: str1.array[0x4] = d[0x4]; goto case; 135 | case 0x3+1: str1.array[0x3] = d[0x3]; goto case; 136 | case 0x2+1: str1.array[0x2] = d[0x2]; goto case; 137 | case 0x1+1: str1.array[0x1] = d[0x1]; goto case; 138 | case 0x0+1: str1.array[0x0] = d[0x0]; goto case; 139 | case 0x0 : break; 140 | } 141 | storeUnaligned!byte16(str1, cast(ubyte*) buffer.ptr + length); 142 | auto cflag = __builtin_ia32_pcmpistric128(str2, str1, 0x04); 143 | ecx = __builtin_ia32_pcmpistri128 (str2, str1, 0x04); 144 | if(!cflag) 145 | { 146 | length += d.length; 147 | break; 148 | } 149 | d = d[ecx .. $]; 150 | length += ecx; 151 | } 152 | 153 | int eax = ecx + 1; 154 | auto cflag = __builtin_ia32_pcmpestric128(str1, eax, str3, emap.length, 0x00); 155 | auto edx = __builtin_ia32_pcmpestri128 (str1, eax, str3, emap.length, 0x00); 156 | d = d[1 .. $]; 157 | buffer[length + 0] = '\\'; 158 | if(cflag) 159 | { 160 | buffer[length + 1] = emap[edx]; 161 | length += 2; 162 | continue; 163 | } 164 | length += 1; 165 | putUnicode(str1.array[ecx]); 166 | } 167 | } 168 | else 169 | { 170 | foreach(chunk; str.representation.chunks(256)) 171 | { 172 | if(chunk.length * 2 + length + 16 > buffer.length) 173 | { 174 | flush; 175 | } 176 | foreach(size_t i, char e; chunk) 177 | { 178 | switch(e) 179 | { 180 | case '\b': 181 | buffer[length + 0] = '\\'; 182 | buffer[length + 1] = 'b'; 183 | length += 2; 184 | continue; 185 | case '\f': 186 | buffer[length + 0] = '\\'; 187 | buffer[length + 1] = 'f'; 188 | length += 2; 189 | continue; 190 | case '\n': 191 | buffer[length + 0] = '\\'; 192 | buffer[length + 1] = 'n'; 193 | length += 2; 194 | continue; 195 | case '\r': 196 | buffer[length + 0] = '\\'; 197 | buffer[length + 1] = 'r'; 198 | length += 2; 199 | continue; 200 | case '\t': 201 | buffer[length + 0] = '\\'; 202 | buffer[length + 1] = 't'; 203 | length += 2; 204 | continue; 205 | case '\\': 206 | buffer[length + 0] = '\\'; 207 | buffer[length + 1] = '\\'; 208 | length += 2; 209 | continue; 210 | case '\"': 211 | buffer[length + 0] = '\\'; 212 | buffer[length + 1] = '\"'; 213 | length += 2; 214 | continue; 215 | case '\0': .. case '\u0007': 216 | case '\u000e': .. case '\u001f': 217 | case '\u000b': 218 | case '\u00ff': 219 | buffer[length + 0] = '\\'; 220 | length++; 221 | putUnicode(e); 222 | if(chunk.length * 2 + length + 16 > buffer.length) 223 | { 224 | flush; 225 | } 226 | continue; 227 | default: 228 | buffer[length] = e; 229 | length++; 230 | } 231 | } 232 | } 233 | } 234 | } 235 | 236 | /+ 237 | Sends remaining data to `sink`. 238 | +/ 239 | void flush() 240 | { 241 | sink(buffer[0 .. length]); 242 | length = 0; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /source/asdf/jsonparser.d: -------------------------------------------------------------------------------- 1 | /++ 2 | JSON Parsing API 3 | 4 | Copyright: Tamedia Digital, 2016-2017 5 | 6 | Authors: Ilia Ki 7 | 8 | License: MIT 9 | 10 | Macros: 11 | SUBMODULE = $(LINK2 asdf_$1.html, asdf.$1) 12 | SUBREF = $(LINK2 asdf_$1.html#.$2, $(TT $2))$(NBSP) 13 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 14 | T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4)) 15 | +/ 16 | module asdf.jsonparser; 17 | 18 | import asdf.asdf; 19 | import asdf.outputarray; 20 | import std.experimental.allocator.gc_allocator; 21 | import std.meta; 22 | import std.range.primitives; 23 | import std.traits; 24 | import std.typecons; 25 | import mir.serde: SerdeException; 26 | 27 | version(LDC) 28 | { 29 | import ldc.attributes: optStrategy; 30 | enum minsize = optStrategy("minsize"); 31 | 32 | static if (__traits(targetHasFeature, "sse4.2")) 33 | { 34 | import core.simd; 35 | import ldc.simd; 36 | import ldc.gccbuiltins_x86; 37 | version = SSE42; 38 | } 39 | } 40 | else 41 | { 42 | enum minsize; 43 | } 44 | 45 | version(X86_64) 46 | version = X86_Any; 47 | else 48 | version(X86) 49 | version = X86_Any; 50 | 51 | private alias ASDFGCAllocator = typeof(GCAllocator.instance); 52 | 53 | /++ 54 | Parses json value 55 | Params: 56 | chunks = input range composed of elements type of `const(ubyte)[]`. 57 | `chunks` can use the same buffer for each chunk. 58 | initLength = initial output buffer length. Minimum value is 32. 59 | Returns: 60 | ASDF value 61 | +/ 62 | Asdf parseJson( 63 | Flag!"includingNewLine" includingNewLine = Yes.includingNewLine, 64 | Flag!"spaces" spaces = Yes.spaces, 65 | Chunks) 66 | (Chunks chunks, size_t initLength = 32) 67 | if(is(ElementType!Chunks : const(ubyte)[])) 68 | { 69 | enum assumeValid = false; 70 | auto parser = jsonParser!(includingNewLine, spaces, assumeValid)(ASDFGCAllocator.instance, chunks); 71 | return parseJson(parser); 72 | } 73 | 74 | /// 75 | unittest 76 | { 77 | import std.range: chunks; 78 | auto text = cast(const ubyte[])`true `; 79 | auto ch = text.chunks(3); 80 | assert(ch.parseJson(32).data == [1]); 81 | } 82 | 83 | 84 | /++ 85 | Parses json value 86 | Params: 87 | str = input string 88 | allocator = (optional) memory allocator 89 | Returns: 90 | ASDF value 91 | +/ 92 | Asdf parseJson( 93 | Flag!"includingNewLine" includingNewLine = Yes.includingNewLine, 94 | Flag!"spaces" spaces = Yes.spaces, 95 | Flag!"assumeValid" assumeValid = No.assumeValid, 96 | Allocator, 97 | ) 98 | (in char[] str, auto ref Allocator allocator) 99 | { 100 | auto parser = jsonParser!(includingNewLine, spaces, assumeValid)(allocator, str); 101 | return parseJson(parser); 102 | } 103 | 104 | 105 | /// 106 | @system unittest { 107 | import std.experimental.allocator.mallocator: Mallocator; 108 | import std.experimental.allocator.showcase: StackFront; 109 | 110 | StackFront!(1024, Mallocator) allocator; 111 | auto json = parseJson(`{"ak": {"sub": "subval"} }`, allocator); 112 | assert(json["ak", "sub"] == "subval"); 113 | } 114 | 115 | /// Faulty location 116 | pure unittest 117 | { 118 | import asdf; 119 | try 120 | { 121 | auto data = `[1, 2, ]`.parseJson; 122 | } 123 | catch(AsdfSerdeException e) 124 | { 125 | import std.conv; 126 | /// zero based index 127 | assert(e.location == 7); 128 | return; 129 | } 130 | assert(0); 131 | } 132 | 133 | /// ditto 134 | Asdf parseJson( 135 | Flag!"includingNewLine" includingNewLine = Yes.includingNewLine, 136 | Flag!"spaces" spaces = Yes.spaces, 137 | Flag!"assumeValid" assumeValid = No.assumeValid, 138 | ) 139 | (in char[] str) 140 | { 141 | auto parser = jsonParser!(includingNewLine, spaces, assumeValid)(&ASDFGCAllocator.instance, str); 142 | return parseJson(parser); 143 | } 144 | 145 | /// 146 | unittest 147 | { 148 | assert(`{"ak": {"sub": "subval"} }`.parseJson["ak", "sub"] == "subval"); 149 | } 150 | 151 | 152 | private Asdf parseJson(Parser)(ref Parser parser) { 153 | size_t location; 154 | if (parser.parse(location)) 155 | throw new AsdfSerdeException(parser.lastError, location); 156 | return Asdf(parser.result); 157 | } 158 | 159 | 160 | deprecated("please remove the initBufferLength argument (latest)") 161 | auto parseJsonByLine( 162 | Flag!"spaces" spaces = Yes.spaces, 163 | Input) 164 | (Input input, sizediff_t initBufferLength) 165 | { 166 | return .parseJsonByLine!(spaces, No.throwOnInvalidLines, Input)(input); 167 | } 168 | 169 | /++ 170 | Parses JSON value in each line from a Range of buffers. 171 | Params: 172 | spaces = adds support for spaces beetwen json tokens. Default value is Yes. 173 | throwOnInvalidLines = throws an $(LREF SerdeException) on invalid lines if Yes and ignore invalid lines if No. Default value is No. 174 | input = input range composed of elements type of `const(ubyte)[]` or string / const(char)[]. 175 | `chunks` can use the same buffer for each chunk. 176 | Returns: 177 | Input range composed of ASDF values. Each value uses the same internal buffer. 178 | +/ 179 | auto parseJsonByLine( 180 | Flag!"spaces" spaces = Yes.spaces, 181 | Flag!"throwOnInvalidLines" throwOnInvalidLines = No.throwOnInvalidLines, 182 | Input) 183 | (Input input) 184 | { 185 | alias Parser = JsonParser!(false, cast(bool)spaces, false, ASDFGCAllocator, Input); 186 | struct ByLineValue 187 | { 188 | Parser parser; 189 | private bool _empty, _nextEmpty; 190 | 191 | void popFront() 192 | { 193 | for(;;) 194 | { 195 | assert(!empty); 196 | if(_nextEmpty) 197 | { 198 | _empty = true; 199 | return; 200 | } 201 | // parser.oa.shift = 0; 202 | parser.dataLength = 0; 203 | auto error = parser.parse; 204 | if(!error) 205 | { 206 | auto t = parser.skipSpaces_; 207 | if(t != '\n' && t != 0) 208 | { 209 | error = AsdfErrorCode.unexpectedValue; 210 | parser._lastError = "expected new line or end of input"; 211 | } 212 | else 213 | if(t == 0) 214 | { 215 | _nextEmpty = true; 216 | return; 217 | } 218 | else 219 | { 220 | parser.skipNewLine; 221 | _nextEmpty = !parser.skipSpaces_; 222 | return; 223 | } 224 | } 225 | static if (throwOnInvalidLines) 226 | throw new SerdeException(parser.lastError); 227 | else 228 | parser.skipLine(); 229 | } 230 | } 231 | 232 | auto front() @property 233 | { 234 | assert(!empty); 235 | return Asdf(parser.result); 236 | } 237 | 238 | bool empty() 239 | { 240 | return _empty; 241 | } 242 | } 243 | ByLineValue ret; 244 | if(input.empty) 245 | { 246 | ret._empty = ret._nextEmpty = true; 247 | } 248 | else 249 | { 250 | ret = ByLineValue(Parser(ASDFGCAllocator.instance, input)); 251 | ret.popFront; 252 | } 253 | return ret; 254 | } 255 | 256 | version(LDC) 257 | { 258 | public import ldc.intrinsics: _expect = llvm_expect; 259 | } 260 | else 261 | { 262 | T _expect(T)(T val, T expected_val) if (__traits(isIntegral, T)) 263 | { 264 | return val; 265 | } 266 | } 267 | 268 | enum AsdfErrorCode 269 | { 270 | success, 271 | unexpectedEnd, 272 | unexpectedValue, 273 | } 274 | 275 | private __gshared immutable ubyte[256] parseFlags = [ 276 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 277 | 0,0,0,0,0,0,0,0, 0,6,2,0,0,6,0,0, // 0 278 | 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, // 1 279 | 7,1,0,1,1,1,1,1, 1,1,1,9,1,9,9,1, // 2 280 | 9,9,9,9,9,9,9,9, 9,9,1,1,1,1,1,1, // 3 281 | 282 | 1,1,1,1,1,9,1,1, 1,1,1,1,1,1,1,1, // 4 283 | 1,1,1,1,1,1,1,1, 1,1,1,1,0,1,1,1, // 5 284 | 1,1,1,1,1,9,1,1, 1,1,1,1,1,1,1,1, // 6 285 | 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, // 7 286 | 287 | 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 288 | 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 289 | 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 290 | 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 291 | 292 | 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 293 | 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 294 | 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 295 | 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 296 | ]; 297 | 298 | private __gshared immutable byte[256] uniFlags = [ 299 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 300 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 0 301 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 1 302 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 2 303 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 3 304 | 305 | -1,10,11,12,13,14,15,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 4 306 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 5 307 | -1,10,11,12,13,14,15,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 6 308 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, // 7 309 | 310 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 311 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 312 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 313 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 314 | 315 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 316 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 317 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 318 | -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, 319 | ]; 320 | 321 | 322 | pragma(inline, true) 323 | bool isPlainJsonCharacter()(size_t c) 324 | { 325 | return (parseFlags[c] & 1) != 0; 326 | } 327 | 328 | pragma(inline, true) 329 | bool isJsonWhitespace()(size_t c) 330 | { 331 | return (parseFlags[c] & 2) != 0; 332 | } 333 | 334 | pragma(inline, true) 335 | bool isJsonLineWhitespace()(size_t c) 336 | { 337 | return (parseFlags[c] & 4) != 0; 338 | } 339 | 340 | pragma(inline, true) 341 | bool isJsonNumber()(size_t c) 342 | { 343 | return (parseFlags[c] & 8) != 0; 344 | } 345 | 346 | package auto assumePure(T)(T t) 347 | if (isFunctionPointer!T || isDelegate!T) 348 | { 349 | enum attrs = functionAttributes!T | FunctionAttribute.pure_; 350 | return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; 351 | } 352 | 353 | package auto callPure(alias fn,T...)(T args) 354 | { 355 | auto fp = assumePure(&fn); 356 | return (*fp)(args); 357 | } 358 | 359 | /+ 360 | Fast picewise stack 361 | +/ 362 | private struct Stack 363 | { 364 | import core.stdc.stdlib: cmalloc = malloc, cfree = free; 365 | @disable this(this); 366 | 367 | struct Node 368 | { 369 | enum length = 32; // 2 power 370 | Node* prev; 371 | size_t* buff; 372 | } 373 | 374 | size_t[Node.length] buffer = void; 375 | size_t length = 0; 376 | Node node; 377 | 378 | pure: 379 | 380 | void push()(size_t value) 381 | { 382 | version(LDC) 383 | pragma(inline, true); 384 | immutable local = length++ & (Node.length - 1); 385 | if (local) 386 | { 387 | node.buff[local] = value; 388 | } 389 | else 390 | if (length == 1) 391 | { 392 | node = Node(null, buffer.ptr); 393 | buffer[0] = value; 394 | } 395 | else 396 | { 397 | auto prevNode = cast(Node*) callPure!cmalloc(Node.sizeof); 398 | *prevNode = node; 399 | node.prev = prevNode; 400 | node.buff = cast(size_t*) callPure!cmalloc(Node.length * size_t.sizeof); 401 | node.buff[0] = value; 402 | } 403 | } 404 | 405 | size_t top()() 406 | { 407 | version(LDC) 408 | pragma(inline, true); 409 | assert(length); 410 | immutable local = (length - 1) & (Node.length - 1); 411 | return node.buff[local]; 412 | } 413 | 414 | size_t pop()() 415 | { 416 | version(LDC) 417 | pragma(inline, true); 418 | assert(length); 419 | immutable local = --length & (Node.length - 1); 420 | immutable ret = node.buff[local]; 421 | if (local == 0) 422 | { 423 | if (node.buff != buffer.ptr) 424 | { 425 | callPure!cfree(node.buff); 426 | node = *node.prev; 427 | } 428 | } 429 | return ret; 430 | } 431 | 432 | pragma(inline, false) 433 | void free()() 434 | { 435 | version(LDC) 436 | pragma(inline, true); 437 | if (node.buff is null) 438 | return; 439 | while(node.buff !is buffer.ptr) 440 | { 441 | callPure!cfree(node.buff); 442 | node = *node.prev; 443 | } 444 | } 445 | } 446 | 447 | unittest 448 | { 449 | Stack stack; 450 | assert(stack.length == 0); 451 | foreach(i; 1 .. 100) 452 | { 453 | stack.push(i); 454 | assert(stack.length == i); 455 | assert(stack.top() == i); 456 | } 457 | foreach_reverse(i; 1 .. 100) 458 | { 459 | assert(stack.length == i); 460 | assert(stack.pop() == i); 461 | } 462 | assert(stack.length == 0); 463 | } 464 | 465 | /// 466 | auto jsonParser(bool includingNewLine, bool hasSpaces, bool assumeValid, Allocator, Input = const(ubyte)[])(auto ref Allocator allocator, Input input) 467 | if (!isPointer!Allocator) 468 | { 469 | return JsonParser!(includingNewLine, hasSpaces, assumeValid, Allocator, Input)(allocator, input); 470 | } 471 | 472 | /// 473 | auto jsonParser(bool includingNewLine, bool hasSpaces, bool assumeValid, Allocator, Input = const(ubyte)[])(Allocator* allocator, Input input) { 474 | return JsonParser!(includingNewLine, hasSpaces, assumeValid, Allocator, Input)(allocator, input); 475 | } 476 | 477 | /// 478 | struct JsonParser(bool includingNewLine, bool hasSpaces, bool assumeValid, Allocator, Input = const(ubyte)[]) 479 | { 480 | 481 | ubyte[] data; 482 | Allocator* allocator; 483 | Input input; 484 | static if (chunked) 485 | ubyte[] front; 486 | else 487 | alias front = input; 488 | size_t dataLength; 489 | 490 | string _lastError; 491 | 492 | enum bool chunked = !is(Input : const(char)[]); 493 | 494 | this(ref Allocator allocator, Input input) 495 | { 496 | this.input = input; 497 | this.allocator = &allocator; 498 | } 499 | 500 | this(Allocator* allocator, Input input) 501 | { 502 | this.input = input; 503 | this.allocator = allocator; 504 | } 505 | 506 | bool prepareInput_()() 507 | { 508 | static if (chunked) 509 | { 510 | if (front.length == 0) 511 | { 512 | assert(!input.empty); 513 | input.popFront; 514 | if (input.empty) 515 | return false; 516 | front = cast(typeof(front)) input.front; 517 | } 518 | } 519 | return front.length != 0; 520 | } 521 | 522 | void skipNewLine()() 523 | { 524 | assert(front.length); 525 | assert(front[0] == '\n'); 526 | front = front[1 .. $]; 527 | } 528 | 529 | char skipSpaces_()() 530 | { 531 | static if (hasSpaces) 532 | for(;;) 533 | { 534 | if (prepareInput_ == false) 535 | return 0; 536 | static if (includingNewLine) 537 | alias isWhite = isJsonWhitespace; 538 | else 539 | alias isWhite = isJsonLineWhitespace; 540 | if (isWhite(front[0])) 541 | { 542 | front = front[1 .. $]; 543 | continue; 544 | } 545 | return front[0]; 546 | } 547 | else 548 | { 549 | if (prepareInput_ == false) 550 | return 0; 551 | return front[0]; 552 | } 553 | } 554 | 555 | bool skipLine()() 556 | { 557 | for(;;) 558 | { 559 | if (_expect(!prepareInput_, false)) 560 | return false; 561 | auto c = front[0]; 562 | front = front[1 .. $]; 563 | if (c == '\n') 564 | return true; 565 | } 566 | } 567 | 568 | auto result()() 569 | { 570 | return data[0 .. dataLength]; 571 | } 572 | 573 | string lastError()() @property 574 | { 575 | return _lastError; 576 | } 577 | 578 | 579 | AsdfErrorCode parse() 580 | { 581 | size_t location; 582 | return parse(location); 583 | } 584 | 585 | static if(is(Allocator == ASDFGCAllocator)) 586 | { 587 | auto parse(out size_t location) @trusted 588 | { 589 | return parseImpl(location); 590 | } 591 | } 592 | else 593 | { 594 | auto parse(out size_t location) 595 | { 596 | return parseImpl(location); 597 | } 598 | } 599 | 600 | pragma(inline, false) 601 | private AsdfErrorCode parseImpl(out size_t location) 602 | { 603 | version(SSE42) 604 | { 605 | enum byte16 str2E = [ 606 | '\u0001', '\u001F', 607 | '\"', '\"', 608 | '\\', '\\', 609 | '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0']; 610 | enum byte16 num2E = ['+', '-', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', 'E', '\0']; 611 | byte16 str2 = str2E; 612 | byte16 num2 = num2E; 613 | } 614 | 615 | const(ubyte)* strPtr; 616 | const(ubyte)* strEnd; 617 | ubyte* dataPtr; 618 | ubyte* stringAndNumberShift = void; 619 | static if (chunked) 620 | { 621 | bool prepareInput()() 622 | { 623 | pragma(inline, false); 624 | if(strPtr) 625 | { 626 | location += front.length; 627 | input.popFront; 628 | if (input.empty) 629 | { 630 | return false; 631 | } 632 | } 633 | front = cast(typeof(front)) input.front; 634 | if (front.length == 0) 635 | return false; 636 | strPtr = front.ptr; 637 | strEnd = front.ptr + front.length; 638 | const dataAddLength = front.length * 6; 639 | const dataLength = dataPtr - data.ptr; 640 | const dataRequiredLength = dataLength + dataAddLength; 641 | if (data.length < dataRequiredLength) 642 | { 643 | const valueLength = stringAndNumberShift - dataPtr; 644 | import std.algorithm.comparison: max; 645 | const len = max(data.length * 2, dataRequiredLength); 646 | allocator.reallocate(*cast(void[]*)&data, len); 647 | dataPtr = data.ptr + dataLength; 648 | stringAndNumberShift = dataPtr + valueLength; 649 | } 650 | return true; 651 | } 652 | strPtr = front.ptr; 653 | strEnd = front.ptr + front.length; 654 | } 655 | else 656 | { 657 | strPtr = cast(const(ubyte)*) input.ptr; 658 | strEnd = cast(const(ubyte)*) input.ptr + input.length; 659 | enum bool prepareInput = false; 660 | } 661 | 662 | auto rl = (strEnd - strPtr) * 6; 663 | if (data.ptr !is null && data.length < rl) 664 | { 665 | allocator.deallocate(data); 666 | data = null; 667 | } 668 | if (data.ptr is null) 669 | { 670 | data = cast(ubyte[])allocator.allocate(rl); 671 | } 672 | dataPtr = data.ptr; 673 | 674 | bool skipSpaces()() 675 | { 676 | version(LDC) 677 | pragma(inline, true); 678 | static if (includingNewLine) 679 | alias isWhite = isJsonWhitespace; 680 | else 681 | alias isWhite = isJsonLineWhitespace; 682 | F: 683 | { 684 | if (_expect(strEnd != strPtr, true)) 685 | { 686 | L: 687 | static if (hasSpaces) 688 | { 689 | if (isWhite(strPtr[0])) 690 | { 691 | strPtr++; 692 | goto F; 693 | } 694 | } 695 | return true; 696 | } 697 | else 698 | { 699 | if (prepareInput) 700 | goto L; 701 | return false; 702 | } 703 | } 704 | 705 | } 706 | 707 | @minsize 708 | int readUnicode()(ref dchar d) 709 | { 710 | version(LDC) 711 | pragma(inline, true); 712 | uint e = 0; 713 | size_t i = 4; 714 | do 715 | { 716 | if (strEnd == strPtr && !prepareInput) 717 | return 1; 718 | int c = uniFlags[*strPtr++]; 719 | assert(c < 16); 720 | if (c == -1) 721 | return -1; 722 | assert(c >= 0); 723 | e <<= 4; 724 | e ^= c; 725 | } 726 | while(--i); 727 | d = e; 728 | return 0; 729 | } 730 | 731 | Stack stack; 732 | 733 | typeof(return) retCode; 734 | bool currIsKey = void; 735 | size_t stackValue = void; 736 | goto value; 737 | 738 | /////////// RETURN 739 | ret: 740 | front = front[cast(typeof(front.ptr)) strPtr - front.ptr .. $]; 741 | dataLength = dataPtr - data.ptr; 742 | assert(stack.length == 0); 743 | ret_final: 744 | return retCode; 745 | /////////// 746 | 747 | key: 748 | if (!skipSpaces) 749 | goto object_key_unexpectedEnd; 750 | key_start: 751 | if (*strPtr != '"') 752 | goto object_key_start_unexpectedValue; 753 | currIsKey = true; 754 | stringAndNumberShift = dataPtr; 755 | // reserve 1 byte for the length 756 | dataPtr += 1; 757 | goto string; 758 | next: 759 | if (stack.length == 0) 760 | goto ret; 761 | { 762 | if (!skipSpaces) 763 | goto next_unexpectedEnd; 764 | stackValue = stack.top; 765 | const isObject = stackValue & 1; 766 | auto v = *strPtr++; 767 | if (isObject) 768 | { 769 | if (v == ',') 770 | goto key; 771 | if (v != '}') 772 | goto next_unexpectedValue; 773 | } 774 | else 775 | { 776 | if (v == ',') 777 | goto value; 778 | if (v != ']') 779 | goto next_unexpectedValue; 780 | } 781 | } 782 | structure_end: { 783 | stackValue = stack.pop(); 784 | const structureShift = stackValue >> 1; 785 | const structureLengthPtr = data.ptr + structureShift; 786 | const size_t structureLength = dataPtr - structureLengthPtr - 4; 787 | if (structureLength > uint.max) 788 | goto object_or_array_is_to_large; 789 | version(X86_Any) 790 | *cast(uint*) structureLengthPtr = cast(uint) structureLength; 791 | else 792 | *cast(ubyte[4]*) structureLengthPtr = cast(ubyte[4]) cast(uint[1]) [cast(uint) structureLength]; 793 | goto next; 794 | } 795 | value: 796 | if (!skipSpaces) 797 | goto value_unexpectedEnd; 798 | value_start: 799 | switch(*strPtr) 800 | { 801 | stringValue: 802 | case '"': 803 | currIsKey = false; 804 | *dataPtr++ = Asdf.Kind.string; 805 | stringAndNumberShift = dataPtr; 806 | // reserve 4 byte for the length 807 | dataPtr += 4; 808 | goto string; 809 | case '-': 810 | case '0': 811 | .. 812 | case '9': { 813 | *dataPtr++ = Asdf.Kind.number; 814 | stringAndNumberShift = dataPtr; 815 | // reserve 1 byte for the length 816 | dataPtr++; // write the first character 817 | *dataPtr++ = *strPtr++; 818 | for(;;) 819 | { 820 | if (strEnd == strPtr && !prepareInput) 821 | goto number_found; 822 | version(SSE42) 823 | { 824 | while (strEnd >= strPtr + 16) 825 | { 826 | byte16 str1 = loadUnaligned!byte16(cast(byte*)strPtr); 827 | size_t ecx = __builtin_ia32_pcmpistri128(num2, str1, 0x10); 828 | storeUnaligned!byte16(str1, cast(byte*)dataPtr); 829 | strPtr += ecx; 830 | dataPtr += ecx; 831 | if(ecx != 16) 832 | goto number_found; 833 | } 834 | } 835 | else 836 | { 837 | while(strEnd >= strPtr + 4) 838 | { 839 | char c0 = strPtr[0]; dataPtr += 4; if (!isJsonNumber(c0)) goto number_found0; 840 | char c1 = strPtr[1]; dataPtr[-4] = c0; if (!isJsonNumber(c1)) goto number_found1; 841 | char c2 = strPtr[2]; dataPtr[-3] = c1; if (!isJsonNumber(c2)) goto number_found2; 842 | char c3 = strPtr[3]; dataPtr[-2] = c2; if (!isJsonNumber(c3)) goto number_found3; 843 | strPtr += 4; dataPtr[-1] = c3; 844 | } 845 | } 846 | while(strEnd > strPtr) 847 | { 848 | char c0 = strPtr[0]; if (!isJsonNumber(c0)) goto number_found; dataPtr[0] = c0; 849 | strPtr += 1; 850 | dataPtr += 1; 851 | } 852 | } 853 | version(SSE42){} else 854 | { 855 | number_found3: dataPtr++; strPtr++; 856 | number_found2: dataPtr++; strPtr++; 857 | number_found1: dataPtr++; strPtr++; 858 | number_found0: dataPtr -= 4; 859 | } 860 | number_found: 861 | 862 | auto numberLength = dataPtr - stringAndNumberShift - 1; 863 | if (numberLength > ubyte.max) 864 | goto number_length_unexpectedValue; 865 | *stringAndNumberShift = cast(ubyte) numberLength; 866 | goto next; 867 | } 868 | case '{': 869 | strPtr++; 870 | *dataPtr++ = Asdf.Kind.object; 871 | stack.push(((dataPtr - data.ptr) << 1) ^ 1); 872 | dataPtr += 4; 873 | if (!skipSpaces) 874 | goto object_first_value_start_unexpectedEnd; 875 | if (*strPtr != '}') 876 | goto key_start; 877 | strPtr++; 878 | goto structure_end; 879 | case '[': 880 | strPtr++; 881 | *dataPtr++ = Asdf.Kind.array; 882 | stack.push(((dataPtr - data.ptr) << 1) ^ 0); 883 | dataPtr += 4; 884 | if (!skipSpaces) 885 | goto array_first_value_start_unexpectedEnd; 886 | if (*strPtr != ']') 887 | goto value_start; 888 | strPtr++; 889 | goto structure_end; 890 | foreach (name; AliasSeq!("false", "null", "true")) 891 | { 892 | case name[0]: 893 | if (_expect(strEnd - strPtr >= name.length, true)) 894 | { 895 | static if (!assumeValid) 896 | { 897 | version(X86_Any) 898 | { 899 | enum uint referenceValue = 900 | (uint(name[$ - 4]) << 0x00) ^ 901 | (uint(name[$ - 3]) << 0x08) ^ 902 | (uint(name[$ - 2]) << 0x10) ^ 903 | (uint(name[$ - 1]) << 0x18); 904 | if (*cast(uint*)(strPtr + bool(name.length == 5)) != referenceValue) 905 | { 906 | static if (name == "true") 907 | goto true_unexpectedValue; 908 | else 909 | static if (name == "false") 910 | goto false_unexpectedValue; 911 | else 912 | goto null_unexpectedValue; 913 | } 914 | } 915 | else 916 | { 917 | char[name.length - 1] c = void; 918 | import std.range: iota; 919 | foreach (i; aliasSeqOf!(iota(1, name.length))) 920 | c[i - 1] = strPtr[i]; 921 | foreach (i; aliasSeqOf!(iota(1, name.length))) 922 | { 923 | if (c[i - 1] != name[i]) 924 | { 925 | 926 | static if (name == "true") 927 | goto true_unexpectedValue; 928 | else 929 | static if (name == "false") 930 | goto false_unexpectedValue; 931 | else 932 | goto null_unexpectedValue; 933 | } 934 | } 935 | } 936 | } 937 | static if (name == "null") 938 | *dataPtr++ = Asdf.Kind.null_; 939 | else 940 | static if (name == "false") 941 | *dataPtr++ = Asdf.Kind.false_; 942 | else 943 | *dataPtr++ = Asdf.Kind.true_; 944 | strPtr += name.length; 945 | goto next; 946 | } 947 | else 948 | { 949 | strPtr += 1; 950 | foreach (i; 1 .. name.length) 951 | { 952 | if (strEnd == strPtr && !prepareInput) 953 | { 954 | static if (name == "true") 955 | goto true_unexpectedEnd; 956 | else 957 | static if (name == "false") 958 | goto false_unexpectedEnd; 959 | else 960 | goto null_unexpectedEnd; 961 | } 962 | static if (!assumeValid) 963 | { 964 | if (_expect(strPtr[0] != name[i], false)) 965 | { 966 | static if (name == "true") 967 | goto true_unexpectedValue; 968 | else 969 | static if (name == "false") 970 | goto false_unexpectedValue; 971 | else 972 | goto null_unexpectedValue; 973 | } 974 | } 975 | strPtr++; 976 | } 977 | static if (name == "null") 978 | *dataPtr++ = Asdf.Kind.null_; 979 | else 980 | static if (name == "false") 981 | *dataPtr++ = Asdf.Kind.false_; 982 | else 983 | *dataPtr++ = Asdf.Kind.true_; 984 | goto next; 985 | } 986 | } 987 | default: goto value_unexpectedStart; 988 | } 989 | 990 | string: 991 | debug assert(*strPtr == '"', "Internal ASDF logic error. Please report an issue."); 992 | strPtr += 1; 993 | 994 | StringLoop: { 995 | for(;;) 996 | { 997 | if (strEnd == strPtr && !prepareInput) 998 | goto string_unexpectedEnd; 999 | version(SSE42) 1000 | { 1001 | while (strEnd >= strPtr + 16) 1002 | { 1003 | byte16 str1 = loadUnaligned!byte16(cast(byte*)strPtr); 1004 | size_t ecx = __builtin_ia32_pcmpistri128(str2, str1, 0x04); 1005 | storeUnaligned!byte16(str1, cast(byte*)dataPtr); 1006 | strPtr += ecx; 1007 | dataPtr += ecx; 1008 | if(ecx != 16) 1009 | goto string_found; 1010 | } 1011 | } 1012 | else 1013 | { 1014 | while(strEnd >= strPtr + 4) 1015 | { 1016 | char c0 = strPtr[0]; dataPtr += 4; if (!isPlainJsonCharacter(c0)) goto string_found0; 1017 | char c1 = strPtr[1]; dataPtr[-4] = c0; if (!isPlainJsonCharacter(c1)) goto string_found1; 1018 | char c2 = strPtr[2]; dataPtr[-3] = c1; if (!isPlainJsonCharacter(c2)) goto string_found2; 1019 | char c3 = strPtr[3]; dataPtr[-2] = c2; if (!isPlainJsonCharacter(c3)) goto string_found3; 1020 | strPtr += 4; dataPtr[-1] = c3; 1021 | } 1022 | } 1023 | while(strEnd > strPtr) 1024 | { 1025 | char c0 = strPtr[0]; if (!isPlainJsonCharacter(c0)) goto string_found; dataPtr[0] = c0; 1026 | strPtr += 1; 1027 | dataPtr += 1; 1028 | } 1029 | } 1030 | version(SSE42) {} else 1031 | { 1032 | string_found3: dataPtr++; strPtr++; 1033 | string_found2: dataPtr++; strPtr++; 1034 | string_found1: dataPtr++; strPtr++; 1035 | string_found0: dataPtr -= 4; 1036 | } 1037 | string_found: 1038 | 1039 | uint c = strPtr[0]; 1040 | if (c == '\"') 1041 | { 1042 | strPtr += 1; 1043 | if (currIsKey) 1044 | { 1045 | auto stringLength = dataPtr - stringAndNumberShift - 1; 1046 | if (stringLength > ubyte.max) 1047 | goto key_is_to_large; 1048 | *cast(ubyte*)stringAndNumberShift = cast(ubyte) stringLength; 1049 | if (!skipSpaces) 1050 | goto failed_to_read_after_key; 1051 | if (*strPtr != ':') 1052 | goto unexpected_character_after_key; 1053 | strPtr++; 1054 | goto value; 1055 | } 1056 | else 1057 | { 1058 | auto stringLength = dataPtr - stringAndNumberShift - 4; 1059 | if (stringLength > uint.max) 1060 | goto string_length_is_too_large; 1061 | version(X86_Any) 1062 | *cast(uint*)stringAndNumberShift = cast(uint) stringLength; 1063 | else 1064 | *cast(ubyte[4]*)stringAndNumberShift = cast(ubyte[4]) cast(uint[1]) [cast(uint) stringLength]; 1065 | goto next; 1066 | } 1067 | } 1068 | if (c == '\\') 1069 | { 1070 | strPtr += 1; 1071 | if (strEnd == strPtr && !prepareInput) 1072 | goto string_unexpectedEnd; 1073 | c = *strPtr++; 1074 | switch(c) 1075 | { 1076 | case '/' : 1077 | case '\"': 1078 | case '\\': 1079 | *dataPtr++ = cast(ubyte) c; 1080 | goto StringLoop; 1081 | case 'b' : *dataPtr++ = '\b'; goto StringLoop; 1082 | case 'f' : *dataPtr++ = '\f'; goto StringLoop; 1083 | case 'n' : *dataPtr++ = '\n'; goto StringLoop; 1084 | case 'r' : *dataPtr++ = '\r'; goto StringLoop; 1085 | case 't' : *dataPtr++ = '\t'; goto StringLoop; 1086 | case 'u' : 1087 | uint wur = void; 1088 | dchar d = void; 1089 | if (auto r = (readUnicode(d))) 1090 | { 1091 | if (r == 1) 1092 | goto string_unexpectedEnd; 1093 | goto string_unexpectedValue; 1094 | } 1095 | if (_expect(0xD800 <= d && d <= 0xDFFF, false)) 1096 | { 1097 | if (d >= 0xDC00) 1098 | goto string_unexpectedValue; 1099 | if (strEnd == strPtr && !prepareInput) 1100 | goto string_unexpectedEnd; 1101 | if (*strPtr++ != '\\') 1102 | goto string_unexpectedValue; 1103 | if (strEnd == strPtr && !prepareInput) 1104 | goto string_unexpectedEnd; 1105 | if (*strPtr++ != 'u') 1106 | goto string_unexpectedValue; 1107 | d = (d & 0x3FF) << 10; 1108 | dchar trailing; 1109 | if (auto r = (readUnicode(trailing))) 1110 | { 1111 | if (r == 1) 1112 | goto string_unexpectedEnd; 1113 | goto string_unexpectedValue; 1114 | } 1115 | if (!(0xDC00 <= trailing && trailing <= 0xDFFF)) 1116 | goto invalid_trail_surrogate; 1117 | { 1118 | d |= trailing & 0x3FF; 1119 | d += 0x10000; 1120 | } 1121 | } 1122 | if (!(d < 0xD800 || (d > 0xDFFF && d <= 0x10FFFF))) 1123 | goto invalid_utf_value; 1124 | encodeUTF8(d, dataPtr); 1125 | goto StringLoop; 1126 | default: goto string_unexpectedValue; 1127 | } 1128 | } 1129 | goto string_unexpectedValue; 1130 | } 1131 | 1132 | ret_error: 1133 | location += strPtr - cast(const(ubyte)*)front.ptr; 1134 | dataLength = dataPtr - data.ptr; 1135 | stack.free(); 1136 | goto ret_final; 1137 | unexpectedEnd: 1138 | retCode = AsdfErrorCode.unexpectedEnd; 1139 | goto ret_error; 1140 | unexpectedValue: 1141 | retCode = AsdfErrorCode.unexpectedValue; 1142 | goto ret_error; 1143 | object_key_unexpectedEnd: 1144 | _lastError = "unexpected end of object key"; 1145 | goto unexpectedEnd; 1146 | object_key_start_unexpectedValue: 1147 | _lastError = "expected '\"' when start parsing object key"; 1148 | goto unexpectedValue; 1149 | key_is_to_large: 1150 | _lastError = "key length is limited to 255 characters"; 1151 | goto unexpectedValue; 1152 | object_or_array_is_to_large: 1153 | _lastError = "object or array serialized size is limited to 2^32-1"; 1154 | goto unexpectedValue; 1155 | next_unexpectedEnd: 1156 | stackValue = stack.top; 1157 | _lastError = (stackValue & 1) ? "unexpected end when parsing object" : "unexpected end when parsing array"; 1158 | goto unexpectedEnd; 1159 | next_unexpectedValue: 1160 | stackValue = stack.top; 1161 | _lastError = (stackValue & 1) ? "expected ',' or `}` when parsing object" : "expected ',' or `]` when parsing array"; 1162 | goto unexpectedValue; 1163 | value_unexpectedStart: 1164 | _lastError = "unexpected character when start parsing JSON value"; 1165 | goto unexpectedEnd; 1166 | value_unexpectedEnd: 1167 | _lastError = "unexpected end when start parsing JSON value"; 1168 | goto unexpectedEnd; 1169 | number_length_unexpectedValue: 1170 | _lastError = "number length is limited to 255 characters"; 1171 | goto unexpectedValue; 1172 | object_first_value_start_unexpectedEnd: 1173 | _lastError = "unexpected end of input data after '{'"; 1174 | goto unexpectedEnd; 1175 | array_first_value_start_unexpectedEnd: 1176 | _lastError = "unexpected end of input data after '['"; 1177 | goto unexpectedEnd; 1178 | false_unexpectedEnd: 1179 | _lastError = "unexpected end when parsing 'false'"; 1180 | goto unexpectedEnd; 1181 | false_unexpectedValue: 1182 | _lastError = "unexpected character when parsing 'false'"; 1183 | goto unexpectedValue; 1184 | null_unexpectedEnd: 1185 | _lastError = "unexpected end when parsing 'null'"; 1186 | goto unexpectedEnd; 1187 | null_unexpectedValue: 1188 | _lastError = "unexpected character when parsing 'null'"; 1189 | goto unexpectedValue; 1190 | true_unexpectedEnd: 1191 | _lastError = "unexpected end when parsing 'true'"; 1192 | goto unexpectedEnd; 1193 | true_unexpectedValue: 1194 | _lastError = "unexpected character when parsing 'true'"; 1195 | goto unexpectedValue; 1196 | string_unexpectedEnd: 1197 | _lastError = "unexpected end when parsing string"; 1198 | goto unexpectedEnd; 1199 | string_unexpectedValue: 1200 | _lastError = "unexpected character when parsing string"; 1201 | goto unexpectedValue; 1202 | failed_to_read_after_key: 1203 | _lastError = "unexpected end after object key"; 1204 | goto unexpectedEnd; 1205 | unexpected_character_after_key: 1206 | _lastError = "unexpected character after key"; 1207 | goto unexpectedValue; 1208 | string_length_is_too_large: 1209 | _lastError = "string size is limited to 2^32-1"; 1210 | goto unexpectedValue; 1211 | invalid_trail_surrogate: 1212 | _lastError = "invalid UTF-16 trail surrogate"; 1213 | goto unexpectedValue; 1214 | invalid_utf_value: 1215 | _lastError = "invalid UTF value"; 1216 | goto unexpectedValue; 1217 | } 1218 | } 1219 | 1220 | unittest 1221 | { 1222 | import mir.conv; 1223 | auto asdf_data = parseJson(` [ true, 123 , [ false, 123.0 , "123211" ], "3e23e" ] `); 1224 | auto str = asdf_data.to!string; 1225 | auto str2 = `[true,123,[false,123.0,"123211"],"3e23e"]`; 1226 | assert( str == str2); 1227 | } 1228 | 1229 | pragma(inline, true) 1230 | void encodeUTF8()(dchar c, ref ubyte* ptr) 1231 | { 1232 | if (c < 0x80) 1233 | { 1234 | ptr[0] = cast(ubyte) (c); 1235 | ptr += 1; 1236 | } 1237 | else 1238 | if (c < 0x800) 1239 | { 1240 | ptr[0] = cast(ubyte) (0xC0 | (c >> 6)); 1241 | ptr[1] = cast(ubyte) (0x80 | (c & 0x3F)); 1242 | ptr += 2; 1243 | } 1244 | else 1245 | if (c < 0x10000) 1246 | { 1247 | ptr[0] = cast(ubyte) (0xE0 | (c >> 12)); 1248 | ptr[1] = cast(ubyte) (0x80 | ((c >> 6) & 0x3F)); 1249 | ptr[2] = cast(ubyte) (0x80 | (c & 0x3F)); 1250 | ptr += 3; 1251 | } 1252 | else 1253 | { 1254 | // assert(c < 0x200000); 1255 | ptr[0] = cast(ubyte) (0xF0 | (c >> 18)); 1256 | ptr[1] = cast(ubyte) (0x80 | ((c >> 12) & 0x3F)); 1257 | ptr[2] = cast(ubyte) (0x80 | ((c >> 6) & 0x3F)); 1258 | ptr[3] = cast(ubyte) (0x80 | (c & 0x3F)); 1259 | ptr += 4; 1260 | } 1261 | } 1262 | 1263 | unittest 1264 | { 1265 | auto asdf = "[\"\u007F\"]".parseJson; 1266 | } 1267 | 1268 | unittest 1269 | { 1270 | auto f = `"\uD801\uDC37"`.parseJson; 1271 | assert(f == "\"\U00010437\"".parseJson); 1272 | } 1273 | 1274 | unittest 1275 | { 1276 | import std.string; 1277 | import std.range; 1278 | static immutable str = `"1234567890qwertyuiopasdfghjklzxcvbnm"`; 1279 | auto data = Asdf(str[1..$-1]); 1280 | assert(data == parseJson(str)); 1281 | foreach(i; 1 .. str.length) 1282 | { 1283 | auto s = parseJson(str.representation.chunks(i)); 1284 | assert(data == s); 1285 | } 1286 | } 1287 | 1288 | unittest 1289 | { 1290 | import std.string; 1291 | import std.range; 1292 | static immutable str = `"\t\r\f\b\"\\\/\t\r\f\b\"\\\/\t\r\f\b\"\\\/\t\r\f\b\"\\\/"`; 1293 | auto data = Asdf("\t\r\f\b\"\\/\t\r\f\b\"\\/\t\r\f\b\"\\/\t\r\f\b\"\\/"); 1294 | assert(data == parseJson(str)); 1295 | foreach(i; 1 .. str.length) 1296 | assert(data == parseJson(str.representation.chunks(i))); 1297 | } 1298 | 1299 | unittest 1300 | { 1301 | import std.string; 1302 | import std.range; 1303 | static immutable str = `"\u0026"`; 1304 | auto data = Asdf("&"); 1305 | assert(data == parseJson(str)); 1306 | } 1307 | 1308 | version(unittest) immutable string test_data = 1309 | q{{ 1310 | "coordinates": [ 1311 | { 1312 | "x": 0.29811521136061625, 1313 | "y": 0.47980763779335556, 1314 | "z": 0.1704431616620138, 1315 | "name": "tqxvsg 2780", 1316 | "opts": { 1317 | "1": [ 1318 | 1, 1319 | true 1320 | ] 1321 | } 1322 | } 1323 | ], 1324 | "info": "some info" 1325 | } 1326 | }; 1327 | -------------------------------------------------------------------------------- /source/asdf/outputarray.d: -------------------------------------------------------------------------------- 1 | module asdf.outputarray; 2 | 3 | import asdf.asdf; 4 | 5 | version(X86_64) 6 | version = X86_Any; 7 | else 8 | version(X86) 9 | version = X86_Any; 10 | 11 | package struct OutputArray 12 | { 13 | import core.memory; 14 | 15 | ubyte[] data; 16 | size_t shift; 17 | 18 | pure: 19 | 20 | auto result() 21 | { 22 | return Asdf(data[0 .. shift]); 23 | } 24 | 25 | this(size_t initialLength) 26 | { 27 | assert(initialLength >= 32); 28 | const len = initialLength % 16 ? (initialLength + 16) / 16 * 16 : initialLength; 29 | data = cast(ubyte[]) GC.malloc(len)[0 .. len]; 30 | } 31 | 32 | size_t skip(size_t len) 33 | { 34 | auto ret = shift; 35 | shift += len; 36 | if(shift > data.length) 37 | extend; 38 | return ret; 39 | } 40 | 41 | void put(in char[] str) 42 | { 43 | size_t newShift = shift + str.length; 44 | if(newShift > data.length) 45 | extend(str.length); 46 | data[shift .. newShift] = cast(ubyte[])str; 47 | //assert(newShift > shift); 48 | shift = newShift; 49 | } 50 | 51 | void put1(ubyte b) 52 | { 53 | put1(b, shift); 54 | shift += 1; 55 | } 56 | 57 | void put(char b) 58 | { 59 | put1(cast(ubyte)b); 60 | } 61 | 62 | void put4(uint b) 63 | { 64 | put4(b, shift); 65 | shift += 4; 66 | } 67 | 68 | void put1(ubyte b, size_t sh) 69 | { 70 | assert(sh <= data.length); 71 | if(sh == data.length) 72 | extend; 73 | data[sh] = b; 74 | } 75 | 76 | void put4(uint b, size_t sh) 77 | { 78 | immutable newShift = sh + 4; 79 | if(newShift > data.length) 80 | extend; 81 | version (X86_Any) 82 | *cast(uint*) (data.ptr + sh) = b; 83 | else 84 | *cast(ubyte[4]*) (data.ptr + sh) = cast(ubyte[4]) cast(uint[1]) [b]; 85 | } 86 | 87 | void extend(size_t len) 88 | { 89 | size_t length = (data.length) * 2 + len; 90 | void[] t = data; 91 | t = GC.realloc(t.ptr, length)[0 .. length]; 92 | data = cast(ubyte[])t; 93 | } 94 | 95 | void extend() 96 | { 97 | size_t length = (data.length) * 2; 98 | void[] t = data; 99 | t = GC.realloc(t.ptr, length)[0 .. length]; 100 | data = cast(ubyte[])t; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /source/asdf/package.d: -------------------------------------------------------------------------------- 1 | /++ 2 | $(H2 ASDF Package) 3 | 4 | Publicly imports $(SUBMODULE _asdf), $(SUBMODULE jsonparser), and $(SUBMODULE serialization). 5 | 6 | Copyright: Tamedia Digital, 2016 7 | 8 | Authors: Ilia Ki 9 | 10 | License: MIT 11 | 12 | Macros: 13 | SUBMODULE = $(LINK2 asdf_$1.html, _asdf.$1) 14 | SUBREF = $(LINK2 asdf_$1.html#.$2, $(TT $2))$(NBSP) 15 | T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) 16 | T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4)) 17 | +/ 18 | module asdf; 19 | 20 | public import asdf.asdf; 21 | public import asdf.jsonparser; 22 | public import asdf.serialization; 23 | public import asdf.transform; 24 | -------------------------------------------------------------------------------- /source/asdf/transform.d: -------------------------------------------------------------------------------- 1 | /++ 2 | Mutable ASDF data structure. 3 | The representation can be used to compute a difference between JSON object-trees. 4 | 5 | Copyright: Tamedia Digital, 2016 6 | 7 | Authors: Ilia Ki 8 | 9 | License: BSL-1.0 10 | +/ 11 | module asdf.transform; 12 | 13 | import asdf.asdf; 14 | import asdf.serialization; 15 | import std.exception: enforce; 16 | 17 | /++ 18 | Object-tree structure for mutable Asdf representation. 19 | 20 | `AsdfNode` can be used to construct and manipulate JSON objects. 21 | Each `AsdfNode` can represent either a dynamic JSON object (associative array of `AsdfNode` nodes) or a ASDF JSON value. 22 | JSON arrays can be represented only as JSON values. 23 | +/ 24 | struct AsdfNode 25 | { 26 | /++ 27 | Children nodes. 28 | +/ 29 | AsdfNode[const(char)[]] children; 30 | /++ 31 | Leaf data. 32 | +/ 33 | Asdf data; 34 | 35 | pure: 36 | 37 | /++ 38 | Returns `true` if the node is leaf. 39 | +/ 40 | bool isLeaf() const @safe pure nothrow @nogc 41 | { 42 | return cast(bool) data.data.length; 43 | } 44 | 45 | /++ 46 | Construct `AsdfNode` recursively. 47 | +/ 48 | this(Asdf data) 49 | { 50 | if(data.kind == Asdf.Kind.object) 51 | { 52 | foreach(kv; data.byKeyValue) 53 | { 54 | children[kv.key] = AsdfNode(kv.value); 55 | } 56 | } 57 | else 58 | { 59 | this.data = data; 60 | enforce(isLeaf); 61 | } 62 | } 63 | 64 | /// 65 | ref AsdfNode opIndex(scope const(char)[][] keys...) scope return 66 | { 67 | if(keys.length == 0) 68 | return this; 69 | auto ret = this; 70 | for(;;) 71 | { 72 | auto ptr = keys[0] in ret.children; 73 | enforce(ptr, "AsdfNode.opIndex: keys do not exist"); 74 | keys = keys[1 .. $]; 75 | if(keys.length == 0) 76 | return *ptr; 77 | ret = *ptr; 78 | } 79 | } 80 | 81 | /// 82 | unittest 83 | { 84 | import asdf; 85 | auto text = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`; 86 | auto root = AsdfNode(text.parseJson); 87 | assert(root["inner", "a"].data == `true`.parseJson); 88 | } 89 | 90 | /// 91 | void opIndexAssign(AsdfNode value, scope const(char)[][] keys...) 92 | { 93 | auto root = &this; 94 | foreach(key; keys) 95 | { 96 | L: 97 | auto ptr = key in root.children; 98 | if(ptr) 99 | { 100 | enforce(ptr, "AsdfNode.opIndex: keys do not exist"); 101 | keys = keys[1 .. $]; 102 | root = ptr; 103 | } 104 | else 105 | { 106 | root.children[keys[0]] = AsdfNode.init; 107 | goto L; 108 | } 109 | } 110 | *root = value; 111 | } 112 | 113 | /// 114 | unittest 115 | { 116 | import asdf; 117 | auto text = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`; 118 | auto root = AsdfNode(text.parseJson); 119 | auto value = AsdfNode(`true`.parseJson); 120 | root["inner", "g", "u"] = value; 121 | assert(root["inner", "g", "u"].data == true); 122 | } 123 | 124 | /++ 125 | Params: 126 | value = default value 127 | keys = list of keys 128 | Returns: `[keys]` if any and `value` othervise. 129 | +/ 130 | AsdfNode get(AsdfNode value, in char[][] keys...) 131 | { 132 | auto ret = this; 133 | foreach(key; keys) 134 | if(auto ptr = key in ret.children) 135 | ret = *ptr; 136 | else 137 | { 138 | ret = value; 139 | break; 140 | } 141 | return ret; 142 | } 143 | 144 | /// 145 | unittest 146 | { 147 | import asdf; 148 | auto text = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`; 149 | auto root = AsdfNode(text.parseJson); 150 | auto value = AsdfNode(`false`.parseJson); 151 | assert(root.get(value, "inner", "a").data == true); 152 | assert(root.get(value, "inner", "f").data == false); 153 | } 154 | 155 | /// Serialization primitive 156 | void serialize(ref AsdfSerializer serializer) 157 | { 158 | if(isLeaf) 159 | { 160 | serializer.app.put(cast(const(char)[])data.data); 161 | return; 162 | } 163 | auto state = serializer.structBegin; 164 | foreach(key, ref value; children) 165 | { 166 | serializer.putKey(key); 167 | value.serialize(serializer); 168 | } 169 | serializer.structEnd(state); 170 | } 171 | 172 | /// 173 | Asdf opCast(T : Asdf)() 174 | { 175 | return serializeToAsdf(this); 176 | } 177 | 178 | /// 179 | unittest 180 | { 181 | import asdf; 182 | auto text = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`; 183 | auto root = AsdfNode(text.parseJson); 184 | import std.stdio; 185 | Asdf flat = cast(Asdf) root; 186 | assert(flat["inner", "a"] == true); 187 | } 188 | 189 | /// 190 | bool opEquals(in AsdfNode rhs) const @safe pure nothrow @nogc 191 | { 192 | if(isLeaf) 193 | if(rhs.isLeaf) 194 | return data == rhs.data; 195 | else 196 | return false; 197 | else 198 | if(rhs.isLeaf) 199 | return false; 200 | else 201 | return children == rhs.children; 202 | } 203 | 204 | /// 205 | unittest 206 | { 207 | import asdf; 208 | auto text = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`; 209 | auto root1 = AsdfNode(text.parseJson); 210 | auto root2= AsdfNode(text.parseJson); 211 | assert(root1 == root2); 212 | assert(root1["inner"].children.remove("b")); 213 | assert(root1 != root2); 214 | } 215 | 216 | /// Adds data to the object-tree recursively. 217 | void add(Asdf data) 218 | { 219 | if(data.kind == Asdf.Kind.object) 220 | { 221 | this.data = Asdf.init; 222 | foreach(kv; data.byKeyValue) 223 | { 224 | if(auto nodePtr = kv.key in children) 225 | { 226 | nodePtr.add(kv.value); 227 | } 228 | else 229 | { 230 | children[kv.key] = AsdfNode(kv.value); 231 | } 232 | } 233 | } 234 | else 235 | { 236 | this.data = data; 237 | children = null; 238 | } 239 | } 240 | 241 | /// 242 | unittest 243 | { 244 | import asdf; 245 | auto text = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`; 246 | auto addition = `{"do":"re","inner":{"a":false,"u":2}}`; 247 | auto root = AsdfNode(text.parseJson); 248 | root.add(addition.parseJson); 249 | auto result = `{"do":"re","foo":"bar","inner":{"a":false,"u":2,"b":false,"c":"32323","d":null,"e":{}}}`; 250 | assert(root == AsdfNode(result.parseJson)); 251 | } 252 | 253 | /// Removes keys from the object-tree recursively. 254 | void remove(Asdf data) 255 | { 256 | enforce(children, "AsdfNode.remove: asdf data must be a sub-tree"); 257 | foreach(kv; data.byKeyValue) 258 | { 259 | if(kv.value.kind == Asdf.Kind.object) 260 | { 261 | if(auto nodePtr = kv.key in children) 262 | { 263 | nodePtr.remove(kv.value); 264 | } 265 | } 266 | else 267 | { 268 | children.remove(kv.key); 269 | } 270 | } 271 | } 272 | 273 | /// 274 | unittest 275 | { 276 | import asdf; 277 | auto text = `{"foo":"bar","inner":{"a":true,"b":false,"c":"32323","d":null,"e":{}}}`; 278 | auto rem = `{"do":null,"foo":null,"inner":{"c":null,"e":null}}`; 279 | auto root = AsdfNode(text.parseJson); 280 | root.remove(rem.parseJson); 281 | auto result = `{"inner":{"a":true,"b":false,"d":null}}`; 282 | assert(root == AsdfNode(result.parseJson)); 283 | } 284 | 285 | private void removedImpl(ref AsdfSerializer serializer, AsdfNode node) 286 | { 287 | import std.exception : enforce; 288 | enforce(!isLeaf); 289 | enforce(!node.isLeaf); 290 | auto state = serializer.structBegin; 291 | foreach(key, ref value; children) 292 | { 293 | auto nodePtr = key in node.children; 294 | if(nodePtr && *nodePtr == value) 295 | continue; 296 | serializer.putKey(key); 297 | if(nodePtr && !nodePtr.isLeaf && !value.isLeaf) 298 | value.removedImpl(serializer, *nodePtr); 299 | else 300 | serializer.putValue(null); 301 | } 302 | serializer.structEnd(state); 303 | } 304 | 305 | /++ 306 | Returns the subset of the object-tree which is not represented in `node`. 307 | If a leaf is represented but has a different value then it will be included 308 | in the return value. 309 | Returned value has ASDF format and its leaves are set to `null`. 310 | +/ 311 | Asdf removed(AsdfNode node) 312 | { 313 | auto serializer = asdfSerializer(); 314 | removedImpl(serializer, node); 315 | serializer.flush; 316 | return serializer.app.result; 317 | } 318 | 319 | /// 320 | unittest 321 | { 322 | import asdf; 323 | auto text1 = `{"inner":{"a":true,"b":false,"d":null}}`; 324 | auto text2 = `{"foo":"bar","inner":{"a":false,"b":false,"c":"32323","d":null,"e":{}}}`; 325 | auto node1 = AsdfNode(text1.parseJson); 326 | auto node2 = AsdfNode(text2.parseJson); 327 | auto diff = AsdfNode(node2.removed(node1)); 328 | assert(diff == AsdfNode(`{"foo":null,"inner":{"a":null,"c":null,"e":null}}`.parseJson)); 329 | } 330 | 331 | void addedImpl(ref AsdfSerializer serializer, AsdfNode node) 332 | { 333 | import std.exception : enforce; 334 | enforce(!isLeaf); 335 | enforce(!node.isLeaf); 336 | auto state = serializer.structBegin; 337 | foreach(key, ref value; node.children) 338 | { 339 | auto nodePtr = key in children; 340 | if(nodePtr && *nodePtr == value) 341 | continue; 342 | serializer.putKey(key); 343 | if(nodePtr && !nodePtr.isLeaf && !value.isLeaf) 344 | nodePtr.addedImpl(serializer, value); 345 | else 346 | value.serialize(serializer); 347 | } 348 | serializer.structEnd(state); 349 | } 350 | 351 | /++ 352 | Returns the subset of the node which is not represented in the object-tree. 353 | If a leaf is represented but has a different value then it will be included 354 | in the return value. 355 | Returned value has ASDF format. 356 | +/ 357 | Asdf added(AsdfNode node) 358 | { 359 | auto serializer = asdfSerializer(); 360 | addedImpl(serializer, node); 361 | serializer.flush; 362 | return serializer.app.result; 363 | } 364 | 365 | /// 366 | unittest 367 | { 368 | import asdf; 369 | auto text1 = `{"foo":"bar","inner":{"a":false,"b":false,"c":"32323","d":null,"e":{}}}`; 370 | auto text2 = `{"inner":{"a":true,"b":false,"d":null}}`; 371 | auto node1 = AsdfNode(text1.parseJson); 372 | auto node2 = AsdfNode(text2.parseJson); 373 | auto diff = AsdfNode(node2.added(node1)); 374 | assert(diff == AsdfNode(`{"foo":"bar","inner":{"a":false,"c":"32323","e":{}}}`.parseJson)); 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /source/asdf/utility.d: -------------------------------------------------------------------------------- 1 | module asdf.utility; 2 | 3 | import std.meta; 4 | 5 | package: 6 | 7 | template Iota(size_t i, size_t j) 8 | { 9 | static assert(i <= j, "Iota: i should be less than or equal to j"); 10 | static if (i == j) 11 | alias Iota = AliasSeq!(); 12 | else 13 | alias Iota = AliasSeq!(i, Iota!(i + 1, j)); 14 | } 15 | -------------------------------------------------------------------------------- /subprojects/mir-algorithm.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=mir-algorithm 3 | url=https://github.com/libmir/mir-algorithm.git 4 | revision=head -------------------------------------------------------------------------------- /subprojects/mir-core.wrap: -------------------------------------------------------------------------------- 1 | [wrap-git] 2 | directory=mir-core 3 | url=https://github.com/libmir/mir-core.git 4 | revision=head 5 | -------------------------------------------------------------------------------- /test_parser.sh: -------------------------------------------------------------------------------- 1 | dub build --compiler=ldc2 2 | if [ -d JSONTestSuite ]; then 3 | echo "JSONTestSuite already exist" 4 | else 5 | git clone https://github.com/nst/JSONTestSuite 6 | fi 7 | cp run_tests.py JSONTestSuite/run_tests.py 8 | cd JSONTestSuite 9 | python3 run_tests.py 10 | -------------------------------------------------------------------------------- /test_parser/.gitignore: -------------------------------------------------------------------------------- 1 | JSONTestSuite 2 | dub.selections.json 3 | *.sublime-project -------------------------------------------------------------------------------- /test_parser/dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test_json-asdf", 3 | "authors": [ 4 | "Iaroshenko Ilia" 5 | ], 6 | "dependencies": { 7 | "asdf": {"path": "../"} 8 | }, 9 | "dflags-ldc": ["-mattr=+sse4.2"], 10 | "description": "ASDF Parser tests", 11 | "copyright": "Copyright © 2017, Tamedia Digital", 12 | "license": "BSL-1.0" 13 | } 14 | -------------------------------------------------------------------------------- /test_parser/run_asdf_tests.py: -------------------------------------------------------------------------------- 1 | import run_tests 2 | import os.path 3 | 4 | if __name__ == '__main__': 5 | 6 | restrict_to_path = None 7 | """ 8 | if len(sys.argv) == 2: 9 | restrict_to_path = os.path.join(BASE_DIR, sys.argv[1]) 10 | if not os.path.exists(restrict_to_path): 11 | print("-- file does not exist:", restrict_to_path) 12 | sys.exit(-1) 13 | """ 14 | #restrict_to_program = ["Python 2.7.10", "Python 3.5.2"] 15 | 16 | asdf_config = "D ASDF Parser"; 17 | 18 | run_tests.programs[asdf_config] = { 19 | "url":"https://github.com/libmir/asdf", 20 | "commands":[os.path.join(run_tests.PARSERS_DIR, "../../test_json-asdf")] 21 | } 22 | 23 | import argparse 24 | parser = argparse.ArgumentParser() 25 | parser.add_argument('restrict_to_path', nargs='?', type=str, default=None) 26 | 27 | args = parser.parse_args() 28 | run_tests.run_tests(args.restrict_to_path, [asdf_config]) 29 | 30 | run_tests.generate_report(os.path.join(run_tests.BASE_DIR, "results/parsing.html"), keep_only_first_result_in_set = False) 31 | run_tests.generate_report(os.path.join(run_tests.BASE_DIR, "results/parsing_pruned.html"), keep_only_first_result_in_set = True) 32 | -------------------------------------------------------------------------------- /test_parser/source/app.d: -------------------------------------------------------------------------------- 1 | 2 | import std.stdio; 3 | import std.exception; 4 | 5 | import asdf; 6 | 7 | int main(string[] args) 8 | { 9 | if(args.length < 2) 10 | { 11 | writeln("Usage: test_json-asdf ."); 12 | return -1; 13 | } 14 | auto filename = args[1]; 15 | try 16 | { 17 | auto asdf = File(filename) 18 | .byChunk(4096) 19 | .parseJson(); 20 | } 21 | catch(Exception e) 22 | { 23 | return 1; 24 | } 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /test_parser/test_parser.sh: -------------------------------------------------------------------------------- 1 | dub build --compiler=ldc2 2 | if [ -d JSONTestSuite ]; then 3 | echo "JSONTestSuite already exist" 4 | else 5 | git clone https://github.com/nst/JSONTestSuite 6 | fi 7 | cp run_asdf_tests.py JSONTestSuite/run_asdf_tests.py 8 | cd JSONTestSuite 9 | python3 run_asdf_tests.py 10 | -------------------------------------------------------------------------------- /test_travis.sh: -------------------------------------------------------------------------------- 1 | echo $ARCH 2 | if [ "$ARCH" = "AARCH64" ] 3 | then 4 | dub test --arch=aarch64 --build=unittest 5 | else 6 | dub test --arch=$ARCH --build=unittest 7 | if [ \( "$DC" = "ldc2" \) -o \( "$DC" = "ldmd2" \) ] 8 | then 9 | cd benchmarks/sajson ; dub --build=release-nobounds --compiler=ldmd2 ; cd ../.. 10 | fi 11 | fi 12 | --------------------------------------------------------------------------------