├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── benchdata ├── chaotic-workload.log ├── few-fields.json ├── heavy.json ├── insane.json ├── light-ws.json ├── many-fields.json └── many-objects.json ├── go.mod ├── go.sum ├── insane.go ├── insane_perf_test.go └── insane_test.go /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | flags: [ '', '-race' ] 20 | go-version: [ '1.19', '1.20', '1.21' ] 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - name: Setup Go ${{ matrix.go-version }} 25 | uses: actions/setup-go@v4 26 | with: 27 | go-version: ${{ matrix.go-version }} 28 | 29 | - name: Build 30 | run: go build -v ./... 31 | 32 | - name: Test 33 | env: 34 | GOFLAGS: ${{ matrix.flags }} 35 | run: go test -coverprofile=profile.out -covermode=atomic -v -coverpkg=./... ./... 36 | 37 | - name: Upload artifact 38 | uses: actions/upload-artifact@v3 39 | with: 40 | name: coverage 41 | path: | 42 | profile.out 43 | if-no-files-found: error 44 | retention-days: 1 45 | 46 | upload: 47 | runs-on: ubuntu-latest 48 | needs: 49 | - test 50 | steps: 51 | - name: Checkout code 52 | uses: actions/checkout@v4 53 | 54 | - name: Download artifact 55 | uses: actions/download-artifact@v3 56 | with: 57 | name: coverage 58 | 59 | - name: Send coverage 60 | uses: codecov/codecov-action@v3 61 | with: 62 | files: profile.out 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cpu.out 2 | insane-json.test -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, Vitkovskii Vladimir 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: bench 2 | bench: 3 | go test . -benchmem -bench Benchmark -count 1 -run _ -cpuprofile cpu.out -memprofile mem.out 4 | 5 | .PHONY: test 6 | test: 7 | go test . -count 1 -v -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Insane JSON 2 | Lighting fast and simple JSON decode/encode library for GO 3 | 4 | ## Key features 5 | To be filled 6 | 7 | ## Usage 8 | ```go 9 | // ==== DECODE API ==== 10 | root, err = insaneJSON.DecodeString(jsonString) // from string 11 | root, err = insaneJSON.DecodeBytes(jsonBytes) // from byte slice 12 | defer insaneJSON.Release(root) // place root back to pool 13 | 14 | // ==== GET API ==== 15 | code = root.Dig("response", "code").AsInt() // int from objects 16 | body = root.Dig("response", "body").AsString() // string from objects 17 | 18 | keys = []string{"items", "3", "name"} 19 | thirdItemName = root.Dig(keys...).AsString() // string from objects and array 20 | 21 | // ==== CHECK API ==== 22 | isObject = root.Dig("response").IsObject() // is value object? 23 | isInt = root.Dig("response", "code").IsInt() // is value null? 24 | isArray = root.Dig("items").IsArray() // is value array? 25 | 26 | // ==== DELETE API ==== 27 | root.Dig("response", "code").Suicide() // delete object field 28 | root.Dig("items", "3").Suicide() // delete array element 29 | anyDugNode.Suicide() // delete any previously dug node 30 | 31 | // ==== MODIFY API ==== 32 | root.Dig("response", "code").MutateToString("OK") // convert to string 33 | root.Dig("items", "3").MutateToObject() // convert to empty object 34 | 35 | item = `{"name":"book","weight":1000}` 36 | err = root.Dig("items", "3").MutateToJSON(item) // convert to parsed JSON 37 | 38 | // ==== OBJECT API ==== 39 | response = root.Dig("response") // get object 40 | fields = response.AsFields() // get object fields 41 | 42 | for _, field = range(fields) { 43 | fmt.Println(field.AsField()) // print all object fields 44 | } 45 | 46 | for _, field = range(fields) { 47 | response.Dig(field.AsField()).Suicide() // remove all fields 48 | } 49 | 50 | for _, field = range(fields) { 51 | field.Suicide() // simpler way to remove all fields 52 | } 53 | 54 | header="Content-Encoding: gzip" 55 | response.AddField("header").MutateToString(header) // add new field and set value 56 | 57 | // ==== ARRAY API ==== 58 | items = root.Dig("items") // get array 59 | elements = items.AsArray() // get array elements 60 | 61 | for _, element = range(elements) { 62 | fmt.Println(element.AsString()) // print all array elements 63 | } 64 | 65 | for _, element = range(elements) { 66 | element.Suicide() // remove all elements 67 | } 68 | 69 | item = `{"name":"book","weight":1000}` 70 | err = items.AddElement().MutateToJSON(item) // add new element and set value 71 | 72 | // ==== ENCODE API ==== 73 | To be filled 74 | 75 | // ==== STRICT API ==== 76 | items = root.Dig("items").InStrictMode() // convert value to strict mode 77 | items, err = root.DigStrict("items") // or get strict value directly 78 | 79 | o, err = items.AsObject() // now value has api with error handling 80 | name, err = items.Dig("5").Dig("name").AsInt // err won't be nil since name is a string 81 | 82 | // ==== POOL API ==== 83 | root, err = insaneJSON.DecodeString(json) // get a root from the pool and place decoded json into it 84 | emptyRoot = insaneJSON.Spawn() // get an empty root from the pool 85 | 86 | root.DecodeString(emptyRoot, anotherJson) // reuse a root to decode another JSONs 87 | 88 | insaneJSON.Release(root) // place roots back to the pool 89 | insaneJSON.Release(emptyRoot) 90 | ``` 91 | 92 | ## Benchmarks 93 | To be filled -------------------------------------------------------------------------------- /benchdata/chaotic-workload.log: -------------------------------------------------------------------------------- 1 | {"minerals":[[{"package":"night","valuable":"earlier","swam":{"represent":{"gray":"attempt","walk":"pleasant","water":[false,false,[{"average":false,"hat":true,"shells":true,"door":true,"amount":"nearest","call":1281220050,"planned":1993806841.5465198,"toy":true,"dust":false,"more":679924649,"local":"hold","became":false,"lake":[-825902658,false,"empty","top",false,2109567982.3041897,false,true,false,805305611.0577111,1015479528,-1718730273.9296966,1123799383,-475575995.69867706,true,true,53868691,false,1438964249,-1080194886.076705],"worry":true,"train":false,"constantly":-1226660099.666521,"plate":"caught","grabbed":false,"simplest":"please","gun":"has"},722466323.2121196,"rope",false,"done",false,true,"possibly",-1867177072.8652887,-858159862.4414062,true,-1231907791,true,false,2135907477.319532,false,false,"wealth",true,968834219],366779123,true,"declared",true,"balance",-1932886965,false,"trade","fort",-898263441.2430215,false,"team",true,false,"forth",false,true],"cat":true,"answer":true,"guide":true,"printed":"every","improve":1562967798.8335776,"diameter":false,"wool":"engine","log":1735001128,"safe":false,"box":155921996.93130255,"western":-608814151,"weight":true,"found":510288304,"least":339248448.9315381,"young":"pick","throat":false,"religious":"mental"},"charge":true,"told":"what","spring":"mostly","below":false,"wide":false,"salt":false,"troops":-1905543687.9572735,"establish":false,"attention":1121893655,"tube":"forest","remove":true,"move":1722683242,"perhaps":-1392587881.214795,"sentence":"from","bee":false,"what":-1725909826.0155993,"least":1986914844,"species":true,"nearest":"pure"},"sitting":"area","seems":"rays","bill":485055005.33599997,"angle":-1927063218,"second":-1221314717,"softly":"immediately","street":"doll","social":false,"see":-2077638166.7703843,"kids":888879000,"left":"shoe","may":false,"particularly":false,"directly":true,"goes":true,"use":true,"famous":1879802163},false,"idea","fought",-1467983845,153425991.20487356,true,-436486657,"classroom",1077218123,"man",1653627615.9707966,"hope",-17441531.986316204,1506321856.5842748,1979238310.8211932,"stranger",false,-1044258861,"classroom"],"laid",true,false,-688753826.0139165,"bottom",false,false,"gave",-1227757199,408096249,"silver","search",888849148,425928430,true,"education",-1077898808.6051111,"welcome","pan"],"underline":true,"ranch":"strong","does":"receive","joy":"mouse","coming":"somewhere","rough":true,"body":"direct","later":"fewer","sell":false,"event":-1970442950,"largest":2105839811.2510777,"word":-939723669.6497135,"swing":"telephone","definition":false,"factory":120293297,"differ":"type","farmer":false,"detail":276266594.3335414,"you":true} 2 | [191827371.27887917,776695175,-817042237,true,{"clothing":[-1800606594,"consider",["decide",-874874638,"one","first",-621821635.1683879,"avoid",-1192286138,-556582160,"consonant","bright",[true,true,{"tell":[{"brush":"push","hour":[1338128273.7392921,449700806,{"be":-1904216899.2365685,"lot":true,"harbor":true,"onlinetools":false,"zoo":true,"independent":false,"ancient":"soil","melted":false,"decide":"come","born":false,"division":"whistle","disease":"some","bottle":-863647092,"tail":"immediately","fuel":1468034356,"hurt":false,"cut":false,"salt":-998458686,"try":883629698,"baby":false},"bee","stick",false,"dropped","remain",false,"shop",901281068.0984111,"them",-963755746.9230132,"worry",true,true,1562150726,false,"why",2016793832.7752056],"pattern":-475364183,"snow":"forty","none":true,"brain":-1804539536,"attached":1449852217,"wealth":2105576235,"policeman":585759374,"respect":-1833892959.5669441,"market":1656469174.6338282,"community":false,"monkey":true,"his":true,"they":false,"nervous":-184596295,"world":true,"at":"government","library":323648143,"facing":"gasoline"},1803299289.721222,false,1540884962.1881309,"also","giving",-1818174402,-261307543.34806156,"include","division","measure","myself",500855893.96651506,true,-256073497,"lesson",true,"brought",-1106395187.5532966,2062178432.193328],"camp":1468621500.424837,"monkey":57550034,"force":"dinner","position":-906481100.00841,"all":"continued","motion":false,"symbol":-926305442.9933271,"exist":false,"needle":"dress","result":-1238873910.4105468,"lonely":"sitting","plates":-21251420,"her":-1203926752,"form":false,"unless":false,"screen":"direction","morning":"wild","storm":true,"pink":"spell"},-1219933582.556044,true,true,true,false,false,"given",true,true,2091066535.790668,316927737,true,true,true,197307948.74878597,true,true],"themselves",992852373,"now","classroom",false,false,false,false,"fifty"],true,false,1497550462,"evening",false,1220923316.7112317,false,true,"customs",354133366.2854605,-1418374564,1042270390,-207354899,false,false,"example",false],"globe":"tiny","lesson":-1857671634,"wide":"skill","tree":true,"look":"funny","eager":"exchange","organized":188076426,"worried":2119328884,"vast":420711960.1749854,"rubbed":false,"scale":612549901.1236095,"held":true,"maybe":"star","strong":"bound","pet":true,"bite":"among","telephone":false,"construction":true,"is":"simple"},"anyone","afternoon","hope",true,"upon","apple","farther","measure","union",1978565584.1768136,"slept",1491729012,true,true,-1036370895.8386464] 3 | [false,["chamber",[{"promised":{"swimming":"opposite","swam":{"applied":true,"coat":false,"label":"birthday","review":{"lay":[{"sort":"describe","once":"plates","what":"cover","pencil":[true,true,false,1364408901,1896749650,false,-392740403,-535056653,false,true,true,1381760266,true,"stream",845518792,"aboard","main",true,"general",false],"salt":"enough","entire":456347050,"although":"stream","chicken":560344862.8525643,"fire":false,"flower":true,"shinning":"hold","change":801379230,"gravity":"bright","solve":true,"most":"butter","trace":true,"up":false,"mainly":"wrapped","phrase":"claws","key":"quiet"},-1409100782,"row",true,true,"missing",-745741875,409763295,false,"eager",107494728.10497618,"applied",-1710150265,true,"model",-758669843,-2127534315,"anyone",false,true],"student":false,"life":false,"duty":true,"dig":true,"easily":1492721292.8515954,"package":true,"stiff":"noon","anyone":35006289.70267916,"length":true,"till":false,"good":720820849.5075774,"official":"slight","explanation":1421885458,"tomorrow":-25069261.753483295,"hide":"standard","cattle":"his","evidence":491798107.17983055,"enemy":"make","contrast":"dark"},"regular":"way","somebody":668969619,"sudden":false,"fierce":"make","public":true,"sugar":1856346659,"meal":"way","smaller":"mud","prepare":"leader","value":true,"noon":"essential","season":928049160.2151017,"play":"difficult","rabbit":587656067,"claws":"tall"},"work":"rocket","hurried":true,"possibly":-1163581852,"attached":true,"consist":-1033854106.3845186,"section":false,"event":"busy","magic":1106196363.8402505,"special":false,"cup":1016831490.0932245,"complete":929724546,"page":"nearly","inch":-1546101650,"keep":-1976190143,"our":"combination","experiment":"enough","art":1100457128.942625,"pet":692078874.3795767},"solar":true,"nice":false,"brush":-724468868.9041424,"station":442630214.8881936,"weight":"wood","key":true,"single":"replied","function":"opportunity","trunk":1704899955,"tightly":-1497746538.2594762,"dry":"bent","allow":"nervous","are":"green","hope":"review","laid":"private","health":false,"lake":"help","matter":"secret"},"careful","cow",409299533.90911055,false,false,-439957049,"current",true,"percent",1672671770,true,false,-1647169006.3424459,true,false,false,false,false,966499451.2030983],"pan",true,"driving",true,"finger",true,721210896.9819622,-476989365.3152032,false,"close",-107034782.13169098,"jump","win",523418593,"major","crew",421581821,137848337.68114996],1031960111.9922905,false,1985412426,1649715131.7004104,-1908614680.652022,-318797363,true,true,"row",84203766,-1435688798.2275782,false,"spider","main","slept","religious",1082527031,1304250059.0315228] 4 | {"police":"pile","had":1873127268,"length":true,"danger":true,"island":["sure",[false,[{"taught":true,"old":"hurt","worried":{"stood":{"thirty":"snake","food":[false,false,{"tell":1589344902.5061717,"hunter":[false,1622335898.818769,"flame","old","camera",-209476201,true,true,false,true,"shout",-1120772995,"past",false,"count",false,true,"cold","influence","lot"],"theory":-250787683,"square":false,"guide":"pressure","soil":true,"church":486052742,"view":"child","line":"excellent","chart":"ready","discovery":699049564.1799984,"will":"golden","finest":"swim","kill":"minerals","continent":"can","proper":"airplane","badly":false,"potatoes":-1643239899},"tape","cow","strong",-966488089,-382576336.5097637,1180885740.0694633,"pot",true,true,745330856,false,false,true,false,false,1382896691,false],"picture":true,"seven":1676994029.6829052,"church":-2074125051,"driven":true,"atomic":true,"instance":true,"attack":"slabs","onto":1683702323,"offer":false,"grain":"paid","engine":"toy","carefully":"plain","surrounded":true,"pitch":"find","universe":false,"tired":-1862306229,"pocket":"fast","design":-666173923.7039614},"done":1136743414.9670267,"form":-1080939735,"begun":true,"half":true,"inside":"seat","my":false,"east":721230354.5331678,"we":1664193977.0847964,"seven":"explain","suit":"farther","split":false,"calm":-83474515,"trade":false,"solution":"flag","kitchen":true,"leader":"said","did":"middle","clean":true,"simply":false},"beautiful":1379503374,"source":-497504328,"whatever":false,"rapidly":-1525736013.1197667,"simply":true,"sport":false,"science":"visitor","held":"respect","couple":true,"studied":1158169417,"valley":"all","palace":"reason","difficulty":1768720533.5901299,"heart":-1621016062,"last":false,"especially":-330898082.3260422,"jungle":true},true,true,false,573091936.6839981,true,"language",true,"quick",-1816649191,false,true,false,642278128,true,983677168,-652775436,"went","seen",false],-1527111630,692447522.7908072,1875861847,true,true,false,false,false,true,true,-401560016.0347104,"paragraph",-2052981144,"top",-125647006.23126411,1701230535.3703923,114570677,false],"cast",true,-362626110,-94529625,true,-938334961.658361,1066261931,false,true,true,-1237379482,true,true,"mother",-315761558.96797466,"worry",false,1767248172],"well":"feed","swung":"street","rest":"was","effect":true,"through":-130732465.23493576,"recently":"eager","children":false,"hat":true,"wish":"recognize","yet":"ancient","morning":false,"strong":"gift","cap":1643899472,"organization":true,"there":false} 5 | {"throughout":-696592970,"young":{"plan":{"brief":false,"herself":["arrive",false,false,420940227,"reader","noun",{"salmon":577413292,"alphabet":[true,"or",true,[{"safety":[true,{"together":"amount","unhappy":true,"material":-627682723.2322998,"lift":true,"gun":1976281714,"prevent":false,"perfect":"body","plain":false,"forty":true,"child":-2116814454.6287694,"prepare":"gradually","rod":-1114104041,"inside":2135667212,"high":-2064709144.6122375,"fun":true,"flow":true,"people":"case","group":true,"dig":true,"enjoy":"piece"},true,-1455674837.4708095,"thee",false,"lying",2134737546.511139,-1279633216.0787911,1298051478.158352,212266199,-853656022.2601156,1948488765,1029060598,false,true,true,"square",-1524874142,-206520461.14499664],"remain":false,"house":"using","cool":"could","size":"occur","swung":"negative","enter":"repeat","letter":"divide","large":"transportation","tears":"fruit","continued":true,"red":true,"wrong":"below","prize":-1385543133,"progress":1183912648.1685042,"tip":1314915908,"solution":false,"railroad":1128622749.3613734,"pile":"measure","season":-822493247},"swing",true,"percent","it",false,"per","attack",false,true,false,false,-1405620522.2224607,848011260,-432606863.45358133,false,-834523501,true,"larger",true],1035787942,"paint",47997840,false,"appearance",false,"clock",false,2026516402.5559201,"quick",true,2049995381,false,false,"broken","how"],"rabbit":-567167677.9635458,"writing":"provide","name":-990336844,"engineer":true,"practical":true,"bush":true,"ran":1764530185,"reason":true,"level":"truth","means":-729782628,"browserling":"officer","bus":true,"burn":true,"neighbor":false,"had":"flat","movement":true,"circus":false,"cap":"moment"},"strike","bush",false,"income","passage","chamber",-183894934.82840014,"salt",1750700769.417653,"invented",688606268.0936112,1726576735,"sell"],"finish":153356168,"proud":"angry","go":false,"triangle":true,"longer":-1867343189.6172557,"which":-607786091,"government":true,"grew":false,"ran":1251245370,"wire":false,"across":false,"sense":"bring","giving":724987988.3158731,"stomach":true,"location":false,"direction":-1322764741,"practical":true,"rising":-1744867949.0685363},"loose":true,"never":"thought","fill":"throat","production":-1898078654.1647725,"solution":false,"instance":"fact","forget":"safety","burn":"discussion","pour":true,"principle":false,"fall":1759164194,"crop":"doing","cutting":"proud","though":-1688381666,"who":"mirror","television":"here","cell":"writing","solid":-1205484472,"air":true},"difficulty":"slide","floor":"hold","wife":"time","onto":-280679979,"had":854096577,"forgotten":"plural","mass":"stage","development":"tongue","behind":-862415456,"twice":"riding","held":"fox","brief":2093012636.8140635,"ate":"lift","willing":-160897174.65629768,"choice":"farther","agree":false,"taste":"recent","worried":627082881} 6 | {"other":true,"valley":1737504179.0717058,"paid":"however","life":true,"behavior":295847863,"time":"directly","development":false,"lack":[650573068.6658635,false,[{"teach":1810590023,"decide":-1916688001,"size":"ice","machinery":"usual","fight":false,"safe":"off","travel":true,"lamp":{"essential":"chair","bean":["furniture",true,["separate","quickly",{"harbor":true,"form":{"source":false,"place":"fell","dish":{"establish":false,"river":"he","skill":false,"tune":"hot","post":"itself","happy":false,"quite":1321165345,"tail":false,"shinning":true,"unless":"mental","him":"plastic","supper":false,"including":"west","nothing":-1469430509,"finest":"effect","industrial":"dead","loss":1302929880.0236807,"lips":true,"section":true,"see":false},"certain":true,"shoulder":-942983803,"parallel":"knew","rubbed":-1063973649,"airplane":"stomach","no":true,"finish":true,"perhaps":"temperature","settle":847401884,"daughter":"range","reach":false,"shop":-648119420,"built":-1253888080,"choose":"lift","worth":"colony","wooden":468634026,"noise":2134392119},"elephant":"park","safe":"port","copy":"trick","liquid":-658235694,"indeed":-1447476249.6905346,"task":1099439171,"sleep":"tent","shout":-409064633.7015219,"yes":"noted","species":"this","heard":true,"shells":false,"vote":-1590678557.5404115,"yourself":"whenever","arrange":"gift","surface":false,"ruler":1105755305.308351,"getting":"pass"},1169054660.817862,false,-2001740633.4515505,false,-1210031994.8912792,"partly","excitement",631871088.1445022,"average",-1270334862,"industrial","curious",true,"plain",true,1132226248.9281602,-670578336.5922809],443285770.1429558,1466945723.7075572,-1153612711,891818005.0418305,false,1582866523,"key",1381197981.67762,false,false,32538101,1263708574.9669251,2077960707,"attention",1327887004.2640266,"tone",true],"chamber":-1941273517.4701672,"consist":"was","list":1822619815.5974317,"art":-1104632742,"bicycle":-1733149770.1983042,"dirt":true,"entire":-1599698222.0622087,"finally":"describe","cell":"hundred","task":"review","good":false,"when":"quiet","border":true,"firm":"coal","but":-2085595939.8553147,"mail":-190067361.13772774,"popular":-1585578815.0867152,"meat":-178525745},"practice":"phrase","region":"keep","show":"chosen","land":false,"making":-506256479,"waste":false,"shells":true,"strange":"explanation","characteristic":"fort","globe":172346677.75340986,"shinning":227874671.6268816,"drove":-1750279878},1022108413,2127431142.7597814,true,-376175915,true,"rough","born","copper","vote","exciting",1463884197.3482833,728312982,"yourself",-53140287,"fifty",false,false,true,"belt"],1876797044,"saved",true,true,false,-124912306.65604877,true,"regular",-67410625.64276981,-105015507.65390825,false,-1714559803.9076285,"there",-693707316,true,-1707250970.1838155,2053373085.6506262],"somebody":true,"bent":true,"no":"personal","increase":"unit","some":-977583225,"air":false,"determine":true,"advice":"typical","enough":-227373653,"progress":-62029157,"nine":true,"usually":-688496912.6346469} 7 | {"attack":true,"simplest":{"give":[{"work":{"together":-1932807220.1237235,"smooth":-868914374.782568,"medicine":-2054328989,"check":-1195888632,"horn":-504916088,"breeze":-606588813.1327739,"feature":[true,{"angle":false,"become":"subject","bat":{"melted":[{"forth":-985826961.326848,"table":160864871.22023487,"fur":939893195,"atomic":false,"headed":false,"roar":false,"happen":true,"cloth":"pink","matter":"middle","minerals":"dangerous","colony":-624470149.7922335,"effect":517567722,"it":false,"bark":"symbol","proud":407895574.7705455,"given":"held","stand":1537607506,"living":false,"hospital":"terrible","explain":true},-163650904,-1684488135,"progress",-423734850,true,1434898421.0331035,true,"location",false,"fastened","meant",true,false,"listen",true,true,true,true,"zero"],"language":64808572.47590637,"through":287224409.09610176,"famous":true,"rate":-1617111977.4100723,"cattle":false,"pay":-927594350,"slide":"welcome","can":true,"independent":false,"save":"sudden","wheel":false,"never":false,"team":false,"original":"crowd","type":"scared","given":true,"greatest":"once","shoulder":-1893306977.588941,"percent":1953304290.7880006},"forgotten":-1324824606.5126262,"paper":true,"well":"dry","available":"butter","sitting":false,"stronger":887470511,"scale":382864186.2553258,"stomach":"found","maybe":true,"chance":false,"strange":"bark","wave":-1906937277,"ready":"map","fierce":"above","signal":1782537179.584075,"invented":-65574370.97299242,"wealth":false},"kitchen",false,false,false,-1516409064,-87651664,false,-484224820,"bear","bad","running",false,-211270859.90797615,"compass",623127129.4162102,"trade","spider",36167046],"weigh":930542975.8406291,"pure":-369174640,"behind":false,"slightly":"begun","dog":729045938,"anywhere":338263893.36129045,"frog":false,"each":-426963631.1828871,"fine":817618415,"rice":true,"ranch":"excitement","everything":-1943166876,"greatly":952315234.5162754},"every":"serious","farmer":"him","everything":"onlinetools","thick":"finally","tree":true,"first":1128263194,"make":1034939394.0736938,"notice":true,"horn":-572256777,"oxygen":false,"studying":true,"unit":true,"saw":1101097768,"bring":"tie","stairs":805546350.6599555,"bright":true,"oldest":"fix","got":true,"structure":513429051.6832619},"tightly",false,true,false,-2075656128,"pass","value",true,"letter",true,"ranch",true,"therefore",301992015.9388113,"cheese",-1504782249,true,false,-964389462],"hurt":1624680961,"regular":-1186749131,"necessary":"put","so":true,"course":356236859.18564606,"not":-2083044221.3394203,"win":"poet","motor":-1759235852.7230139,"rice":true,"phrase":"hand","discovery":false,"once":true,"plain":"tribe","brick":true,"stove":-1014157761.4556179,"way":"public","drive":true,"told":true,"pleasure":-1229970412},"yourself":"may","start":true,"square":1320305757,"certainly":true,"gulf":true,"mood":-1421391393.6488962,"him":"promised","took":-790754772.5018139,"one":"work","then":"every","modern":"hope","account":true,"cloth":"local","them":"movement","adventure":false,"sets":"control","noon":false,"vessels":742227656} 8 | [{"folks":{"airplane":-632632045.5457721,"for":2096646215,"scientist":-2078870163,"eleven":{"complex":[false,false,"piece",[false,{"exclaimed":{"row":-1115380144,"dangerous":true,"chest":-744345436,"bark":"open","upward":119628648.50907755,"ball":1835211534,"gone":false,"himself":{"meant":"careful","swimming":false,"tide":{"until":true,"cutting":"softly","growth":false,"agree":-709036321,"electric":true,"powder":91678740.1163392,"eaten":-956393768,"place":226246561,"butter":1875708533,"cook":"popular","yet":true,"is":-1309380430.7572727,"tie":true,"transportation":true,"remember":551868556,"seat":"biggest","shorter":"unusual","together":"closer","plastic":"nearly","lift":"job"},"swung":"bank","grade":true,"bet":1911714732.1253633,"ball":true,"stepped":"exclaimed","snake":true,"create":-279655111.27227116,"classroom":1494665216,"poem":1285527541.0880442,"function":-829601450.7962956,"easily":"total","winter":765990367,"equal":false,"son":451610864,"year":1750668268.6885552,"boy":-1059529180.6958866,"short":"go"},"serve":"task","expect":true,"led":947732934,"tin":"manufacturing","fairly":"whether","third":"organization","connected":-1970462763.4897032,"many":false,"college":"half","various":1176942920,"task":false,"plural":"reach"},"music":-965032018,"him":-1963216271,"brick":true,"wish":"driver","fifty":-950688558,"twice":"lips","got":false,"board":false,"grew":"like","biggest":false,"influence":-1769203111,"week":"silver","asleep":1166983496,"science":"other","limited":-165814064,"theory":"happened","safety":false,"camp":"lot","account":false},666518168,-985892739.844646,"balloon",-1107507901,"southern","someone",1400634151,"afraid",true,"model","principle","allow",-1335815310,-1431049233.9601846,1927574603,"surface",false,true],true,767423962,true,-1718830789,-2100509528.9311357,false,"remarkable","underline","remove",-1712319490.8927422,false,true,-53885988,"neighbor","actual","sort"],"recall":false,"limited":false,"instance":-750536325.4117389,"rain":1871600666,"grandmother":2055024155,"perhaps":false,"foreign":"clay","original":false,"rubber":"natural","log":"sister","son":"quickly","who":false,"war":"automobile","deeply":222107103,"exchange":-611714464.9395018,"chosen":"balloon","money":-2123045265,"wonderful":false,"count":false},"wave":true,"under":"camera","good":"pressure","bus":"remove","best":"bring","thumb":-2017054112,"carry":false,"improve":"treated","lamp":false,"using":"surprise","troops":"original","composed":"simply","swimming":true,"eight":-1754059981,"life":true,"putting":true},"getting":-1701150939,"machinery":false,"warn":true,"sold":1799319338,"figure":true,"everyone":-1764245944,"thread":false,"jack":false,"electric":true,"whispered":-673724932,"rays":"present","me":true,"offer":false,"go":true,"basis":"rope","bicycle":707707747.636601,"ear":false,"save":true,"composed":-1124534518},false,"white",true,-916346911,-1909028305,false,53753996,1820832415.8322349,118129552,456896270.1867523,false,false,1443713048,1503433435.1114054,-1519986194.9284492,"pretty",false,"hollow","form"] 9 | {"note":1057804853.6082501,"triangle":-1124784618,"beyond":[{"discover":[-372192709.1422472,false,{"including":[[-1329821497.4240494,525658373.3221617,[{"already":"capital","stand":222214674.0552249,"nest":{"death":false,"wooden":true,"eight":"bridge","day":true,"rest":"said","surface":"layers","sight":1489448928.4670987,"graph":"whole","hurried":false,"verb":"smaller","student":"express","shape":1148735635.2254996,"kids":"difficult","quite":true,"bat":false,"prevent":"like","daughter":false,"measure":-1554669640.4751701,"think":"lie","pencil":"saddle"},"origin":386510200.9893489,"involved":"coat","gate":"money","rhythm":"duck","arrangement":1822627134,"most":false,"bank":"wear","eleven":"have","ground":"power","calm":1990252551,"met":-711880962,"wooden":true,"writer":false,"quietly":false,"cannot":844366964.2288518,"our":541963771,"effort":193649845},2111612153.322476,"announced",655773284.0070214,-104423184,"wool","related",true,-1833175521,-158478120.75010633,-660328186.3826838,193366456,"bag",-2104794551,"surprise",true,true,-1518171375,"anywhere","clear"],"has",false,false,false,"meal",false,"mirror","pie",-2026616409.8524685,"tune",false,"plane","design","these",1809977323.792253,"flag",611364791],false,"instead",false,"ice",2040488246,-572999859.85568,true,"seems",false,"magnet","board",true,"round",false,"kill",-1617859546,-124010368.8975091,679181457,1196422450],"within":1332044462.1063643,"card":true,"brass":1372873990,"excitement":"slightly","fruit":true,"whatever":"rise","struck":false,"eaten":"swung","settlers":true,"show":false,"leave":"actual","cage":true,"pool":-1458162485.0734978,"electricity":-333343992.5616689,"went":308731412.9673319,"thank":348123339,"sheet":true,"examine":-454901207.030159,"forty":"greater"},"add",604689054,"guess",false,true,"entirely","signal",-438881361,"than","month","arrangement",false,false,-1741110554.9874225,1230445455.6024418,true,"around"],"review":false,"national":true,"arm":236410174.19378185,"voyage":663188821.8554077,"wrote":-483078920,"valley":"blue","shut":"plane","planning":-1029777328.8356228,"fourth":false,"anyway":"bound","contrast":-2079691896.5413055,"merely":"larger","region":false,"waste":"floating","each":false,"swimming":-384190394.89023495,"would":true,"western":"enemy","result":1349170786},false,-974834774,"atmosphere",-1686726927.340907,"program",-1712753803,true,"storm",false,"canal","parts",false,"molecular","there",false,false,"shape","fairly",470762443],"birds":true,"breath":true,"silent":1338430265,"religious":true,"harbor":1579230259,"six":-1027075800.267406,"learn":"rapidly","trunk":"such","trade":-1219406090,"term":-1138960205.514287,"movement":false,"edge":false,"rocky":1555667378.9264078,"pale":"pine","serious":"general","easily":"jungle","atomic":1150148233.4166017} 10 | {"speech":80585179,"wild":"angle","least":[[false,["taught",false,"pretty",-680148941.8967099,-394839881,false,false,-573656675,true,{"thick":true,"straight":1611328727.167983,"wish":true,"act":"rod","classroom":{"mind":"water","shall":"happy","century":1813023526,"thee":{"course":true,"missing":false,"bus":"bread","long":-624572299.1610041,"spent":[{"nearest":{"serve":false,"song":false,"buffalo":true,"pain":823369399,"greatly":-756342575,"flower":"it","disease":"pass","claws":-372194779.4500818,"essential":-1314187576.4642124,"soon":true,"major":"board","badly":true,"welcome":false,"account":"became","plain":"brain","repeat":"attempt","friendly":"crack","loud":142338823.8639617,"something":"former","year":-2104858068},"thing":true,"provide":"bank","brief":false,"yellow":1051454914,"soil":521557351.6234822,"train":869173823.9146552,"highest":"division","stretch":false,"farm":"instant","arrange":1063676287.5317674,"goes":-377828593.62331057,"breakfast":"monkey","pick":1074623883.3930626,"mail":"stream","trap":833521358,"missing":false,"south":-232191294.02527666,"strength":true,"safe":"mine"},true,1754067310,1490642971,-1900772411.389213,true,"firm",true,false,false,"world",false,"applied",37659517.56242943,-343116358,"camp",2067219797,true,false,-1289797672.7786498],"managed":"arrangement","combination":-1436611875.7887497,"plain":"palace","ordinary":true,"shirt":false,"name":true,"behind":false,"older":-1400062208.4508271,"needle":"wing","chamber":true,"type":false,"interest":"serious","thousand":"camera","act":388919402.2905998,"wave":"fruit"},"limited":"heavy","pitch":true,"iron":2053151836,"image":true,"seems":true,"ago":"begun","special":"pull","settle":true,"serve":-930968172,"driver":false,"wild":"water","triangle":true,"cross":-887462750,"search":false,"choice":"pond","chapter":-1236504777},"anyone":-2037361689.062367,"meet":false,"connected":false,"except":"come","solve":-1072576943.0868134,"monkey":"slide","together":-1614887257.29741,"finally":453613827.72476196,"near":-460938264,"cream":"primitive","tide":true,"too":false,"bill":"hill","brown":"am","neighbor":false},"men","limited",false,false,-1607644231.2455997,"shot",false,-1663210787.145412,"shelter","bag"],false,1653488352.6785831,1124217687,false,-221928107.38409424,false,"additional",true,"headed",-389646269.03894615,false,true,false,true,"printed","met",450993292,false],-1035197275,-175336610.52711248,983972097.9640684,true,true,306928210.51242685,true,"shirt","listen",false,"stick",false,true,"reach",true,1514226134.910274,"building",198289228.4816699,445091848.28628206],"try":false,"construction":"fourth","pocket":"accident","gently":99580499.32339668,"opposite":"alone","feet":false,"scientific":"hardly","close":-1196994838.4194303,"cage":false,"solve":"found","life":180036091.3178501,"whose":"differ","dead":-2103547178.969163,"spread":"size","route":true,"location":-241715627.69243813,"out":610921111} 11 | {"onlinetools":true,"native":true,"sale":[false,-957019479,[false,"river",true,["poor",{"met":"layers","sad":"bag","balloon":-1142825543.853981,"slip":"whose","quick":"soil","was":false,"mistake":true,"went":false,"disappear":true,"exact":-1344715432,"huge":"character","wool":"lack","sentence":"college","fewer":"potatoes","love":"among","chance":["about",true,"met",true,-1476609379.8022113,[true,"well","closely",[[{"tobacco":"build","know":-1417563520,"pair":"lying","deep":728507313,"how":"beyond","suggest":"cry","electric":"element","badly":"birds","flat":true,"stretch":"five","higher":-1462169602.3376932,"club":"learn","surrounded":false,"field":2111098004.5577116,"labor":-1586042808,"shall":false,"school":308475093,"sure":567921048.0889792,"screen":false,"usually":"trip"},false,849253481.7363296,true,1873027407,true,true,"machine",false,378147735,686835345,true,true,-1497618343,true,-1807086930.923286,"amount",404017202.78315544,false,"plus"],false,1190788623.1268635,false,true,true,1831479788.6541047,false,false,true,"shorter",false,"whenever",2088786364.5008483,false,1102124662,1937494215.1353974,"speech",929968022,"exciting"],false,false,-352788856,609395982.4218802,-379008317,false,"pie","queen",878725767,573605098,true,-1634632580,-1306788289,"high",1729304770.7436547,"usual"],false,"golden","experience",false,-1290594129.565578,false,"seeing",-809165748.8174887,false,false,"create",1663506489,"dream","am"],"apple":true,"half":167678710,"plain":true,"blue":1886098060.42803},"feed",false,true,true,true,626817558.6898966,"hospital",-1634656724,false,"grandmother","somewhere",2072498859.77782,"deeply",-888412595.1520758,false,-1810192564,false,378275744.1185427],true,false,"satisfied",false,-701770582,-1884636571,-1207680216,true,-100505904,"spent",true,true,-2052987738.3321776,true,1669074543,false],2133897148.5062995,509621064.92884684,true,false,true,"average",false,-5787268.59469986,"shout",-1960066172.0092115,"forest",false,false,381655392.49405193,"declared","influence",true],"sent":"protection","mile":"grow","hay":false,"prevent":-817793570,"just":-750793350.1494951,"importance":1869798204,"yard":"planning","pilot":-2007904966,"wore":1143191720.2213364,"duty":-265511215,"possibly":-1007801856,"huge":97181348,"tomorrow":true,"laid":-12861224,"child":false,"massage":true,"next":true} 12 | ["say",false,-1420655811,898602692,-1762016049.9152393,"sick",{"happened":false,"fire":-1217309998.4440908,"hay":[false,{"however":true,"aboard":-1824692664,"flight":"setting","hurry":[557396477,true,[-1966111931,-132765637.4733572,-564598482.8623276,false,{"vessels":"wrapped","globe":true,"once":[false,false,"support",{"answer":false,"can":false,"duck":{"interior":false,"star":true,"factory":"part","town":61619981.95269823,"production":309422512,"magnet":1983505242,"threw":false,"police":"come","visit":-1705271108.0603375,"everywhere":true,"invented":false,"official":true,"opinion":183722400,"chest":true,"highest":-111507404.98152304,"fuel":true,"frame":"different","month":-33490116.81775236,"front":"ask","dinner":true},"live":false,"cry":"fall","farmer":"first","natural":"hide","myself":"cook","improve":false,"coal":-207832298.43652534,"way":false,"hole":false,"pool":-112414661.111197,"excellent":true,"bell":false,"farm":-1781420405.776041,"task":"until","rough":"environment","mark":"brief","doubt":"let"},"fly","create","being",1931021246.8277636,false,-291906795.5933981,true,-216023674,-706570997.4187183,false,-441191909.0594225,"spend",true,"quickly",-1603047109,-1957414123],"whatever":1953229945,"tip":"cheese","orange":"completely","may":"solar","carbon":-1125229200,"pony":true,"save":false,"citizen":"appropriate","stretch":false,"rhythm":false,"split":true,"doubt":false,"enjoy":187182627.25908136,"beautiful":724172086.077693,"basket":true,"flight":true,"real":true},"program",1201445506.2893004,false,true,-1688366561,1400521290,"bit",true,"except",true,true,"pull","individual","careful",true],-904522282,-389519280.92057467,false,-1277847440,252518623,false,"mathematics","event",196111726.33165932,true,"popular","studied",-29179784.567223072,1689487694,"her",-422706738,true],"cloth":"stone","yes":true,"halfway":false,"home":"sleep","between":1995817433.3357959,"rose":"tonight","loss":false,"afraid":false,"its":"afraid","lucky":"push","spite":true,"city":1301665727.8104172,"belong":"failed","nervous":"last","fireplace":"gone","composed":1260053845},"related",-926855591.6228042,true,457900576.82605696,-1271245084.2119937,"raw",558780870,false,"glass","duck","huge",-848860049.4136586,1104442596.7552128,true,-621206525.1845851,"represent","wherever",-861879102],"nor":1205850161,"peace":-1272550896,"star":-4637277,"similar":false,"queen":false,"volume":156886738,"deer":"combination","field":-1823138662.1550784,"hair":"instrument","were":"particular","freedom":"wealth","labor":true,"when":-1014266813.3571377,"club":false,"interior":716435695,"tide":"consider","corner":956940882.705997},-304938313,"library",-1264536458,1835623825.9483752,-1616841165,1422503464,"hurried",-15238944,"forget","flower",-240036702,false,-1419671990] 13 | [true,{"nails":-2124501069.0629811,"result":882405974.4817667,"pink":{"board":"guide","clean":{"for":{"wind":-1300943458.6980848,"firm":[[251407643.80184364,-725590935.9046569,-1544335461.0238075,false,true,false,{"valuable":{"feel":1131005549,"thread":false,"guard":true,"imagine":"hundred","ill":[false,true,343861639,390166933,1880512210.9292355,false,false,2126481180,-1472983192,514542022,"fly",true,1510668796.050478,1401917162,"hat",true,-1300936042.3038163,"ancient",false,true],"your":false,"food":-1553215881,"burn":false,"wonder":2058195272,"avoid":"mail","depend":-402027653.8408513,"wooden":false,"sky":"air","up":"tobacco","without":-719241669.8220625,"root":223382342.98539448,"audience":"inside","diameter":-1780314686.4892206,"choose":1287072321,"stay":true},"oil":"effort","reason":"office","law":false,"secret":667790054,"ill":"electricity","doing":"ear","equal":"managed","learn":false,"actually":"jump","may":false,"rear":-37125452,"careful":474095360.20008326,"poem":true,"chest":-499621116.4498162,"figure":true,"fear":"vote","beautiful":false,"across":true,"planned":"enemy"},"week",-583176518,false,"truth","species","damage",false,"according","human",-592153573.5835361,"quite","explore",558399547.6381445],"spring",-1861073851,true,false,false,"space",true,1803049604.1959481,false,"army",false,-1316605707.675476,true,149278058.0409279,-1041914005.9500408,-1084805376.040289,818174289.0335503,true,true],"frozen":"identity","met":"year","top":"recent","discover":-1863236613.919527,"must":"drew","complete":907776642,"wait":true,"none":"private","consist":"stems","shaking":1295435407.3841648,"length":-941249116,"deal":-1514331370,"colony":860328217.1831408,"ants":859586113.2494869,"thumb":-355601032.5099902,"bill":-227636974.28589678,"church":-92472165,"finger":1501085881.6570296},"introduced":-51981961.2280283,"claws":"motor","lake":-1871291312,"flag":-897524921.9825497,"visit":917951786.1802206,"plant":true,"thee":true,"nice":"quarter","wool":true,"paragraph":false,"imagine":993102358.3283472,"result":-574037139.124208,"clear":false,"screen":-1077708102.9079747,"lot":false,"meal":-784883046,"whether":true,"root":"avoid","fuel":630326278.7761927},"leader":1332227047.3791513,"production":"cabin","until":"slightly","consonant":-1392288961,"final":-912191080,"buy":false,"nails":true,"instance":true,"appearance":"simple","morning":true,"pool":true,"fifth":true,"boat":1383532021.8690329,"political":1319628504.3287177,"nearer":false,"due":false,"aboard":"carbon","industry":1611277067},"unusual":true,"notice":-1485967252,"facing":-742577336.6758499,"jack":"element","along":false,"population":"purpose","lose":false,"usual":1376302000.799389,"command":false,"later":-404427646,"least":"plan","introduced":"ran","activity":"forgotten","after":-1010874023.537117,"such":true,"disappear":true,"between":"neck"},true,569352729.5457635,false,false,false,"earn","door",true,"perfectly",739287048.883884,"very","result",400181810.3203201,false,true,-1656820883,-1677394003.7821956,"glad"] 14 | {"fly":1516818250,"blind":-1925972067.149747,"frame":1059657588.5686078,"courage":[[[false,1410437721,{"construction":"birds","whose":[{"like":true,"earth":[-1236756989,false,false,{"enter":"clothes","steep":[false,-1980781846.1459985,-166542649,false,2099539570,false,true,false,682488209,"cold","zoo",853289289.0810986,true,1738701283.5210671,true,54117344,"development",1047687870,"another",1502207157.2080379],"change":869728227,"degree":false,"impossible":-28263459,"brief":-2103095494,"dig":true,"region":false,"plain":1471890737,"check":"silly","am":"trouble","row":false,"tent":true,"police":-1343002978.799644,"bowl":-1998343010.8252764,"central":false,"consist":"leaf","tobacco":"brass","becoming":861018408.4877291,"making":2100252942},true,"stems",false,"weight",true,true,"arrive",-2048911768,-891970649,false,-997241356,1341364053,false,false,true,"voice"],"pull":"loose","military":true,"plan":false,"grabbed":"silver","rope":"wolf","property":"consonant","mice":1471390740,"travel":false,"negative":-1537820780.432415,"failed":1330825844,"thing":-137299451.74329472,"mine":false,"caught":-2045451857.1927633,"three":1747690781,"oil":"gently","stove":true,"traffic":true,"uncle":"sink"},true,false,false,true,"engine",true,-997273534.9131413,"bigger",-144468588,-1055425052,"more",-1378024646,false,1003827860,"cool",false,"setting",true,-2139868002.776259],"gasoline":"scene","away":false,"hard":true,"on":"mile","quickly":true,"silly":"torn","imagine":true,"such":true,"grass":195204113.9690776,"hundred":-1558505168,"at":true,"involved":1261755825.9110641,"hidden":-85520574.91656113,"act":"bus","fireplace":"soap","gun":566197322.7296972,"recognize":"my","pool":"variety"},true,false,1443329333,false,"four",1914332894.557755,true,true,false,true,"third","impossible",-1945238760,"capital",true,1096306038,"check"],992814770,"opinion",20263811,"soap",1300567158,-1936234184,1773937358.5783358,true,false,-613067055,-207737947,"court",false,true,false,-1681017078,true,-1185018504,"discover"],true,936434889,"whistle",true,true,"loud",92910766,-895697727,-1611749165.2435951,2132361453.5564008,false,false,-297727175,false,true,"mix","sell",true,"surprise"],"shore":-111943321.46718788,"burn":"expression","met":true,"cowboy":false,"cat":false,"become":false,"strange":false,"by":"studying","she":true,"farm":true,"replace":-1739935591.909091,"length":"promised","view":false,"stone":true,"first":-2011708775.6120138,"agree":"matter"} 15 | {"dirty":{"interior":"captain","finger":false,"wore":false,"about":true,"consonant":false,"religious":1884760087,"meet":false,"disease":2010631058.275867,"influence":273808025,"loss":false,"eat":-360888584,"compare":-363881226,"earlier":"dropped","already":-619837447,"never":-1398225274.476575,"hospital":false,"eleven":-1508576907,"fox":1145094724,"burst":"grass"},"sink":-378588571.5465765,"cave":"slip","answer":true,"signal":false,"age":true,"country":"news","tail":true,"look":362741850.6206975,"ever":"ride","opinion":696400441.2533908,"but":false,"final":true,"suit":false,"yard":-1093494391.5269952,"community":-1310052168,"low":true,"do":true,"string":-842422111,"price":"cap"} 16 | [[563504293,-1308326017,[false,["sale",false,true,{"happily":false,"enough":[{"same":[["empty",-1991671982,{"material":false,"liquid":false,"lovely":false,"excited":"roll","earn":"pine","crop":false,"political":1225393688,"keep":"independent","next":true,"journey":"done","could":921749813.097167,"giving":-1539522879.7956266,"sure":true,"cream":"anybody","income":"ate","property":-1549084381,"using":true,"saddle":false,"tea":2018663920.615324,"higher":"newspaper"},2033288986,false,true,-663867126.7257943,"loud","fight",true,"desert","discover","leather",false,true,"all","broad",false,false,-2101403121],-1874405460.2048469,true,true,true,true,false,true,-1349580933,-1967761547,-905828404,true,false,false,"exchange",978069348.8459992,"edge",false,false,"seed"],"late":false,"may":103152574,"extra":-704940784.8307562,"bark":"everyone","seat":true,"pain":"band","meat":-1520711598,"fat":true,"wonder":"refer","film":false,"border":-39554479.19135904,"best":false,"note":"guard","since":"swimming","orange":"ship","sat":true,"forward":true,"nervous":-522763787,"organized":true},false,2025878656.193965,true,false,1059221640,true,"where",true,"cast","form","pink",true,1651755271,"key",true,-978463078,false,"function",true],"collect":"city","money":true,"clear":true,"honor":1625055014,"earth":-1954220223,"remarkable":"direction","sick":"tape","count":"mistake","war":"finest","west":false,"find":false,"area":"porch","bicycle":"wash","bit":false,"heard":true,"hay":"pine","church":1415915826,"track":-1786804812.04878},"get",true,"our",-273710567.2775841,-183762728,-1637919320.990356,"heart","hour",false,"everybody",-1459312553,613854176,285222363,true,"plastic",false],452875225,-148421027.2606902,"balance",-1137129077.6041527,1665142088,false,"occur","rocky",1696953222.6765375,false,false,1799774039,"film","bat",-2135551885,-576461828,"task","capital"],-574254662,false,-931836012,-560446315,-1230585355.726636,"speak",1572996217,false,655323365,false,-1775398816,true,-765781141.6275334,-1920045132.518526,-111089474,false,"red"],"transportation",true,"important",-2107482524,61689228,"vowel",true,822345357,false,true,1123239937,"greatest",true,true,1210462012,"travel",965132205,"it",false] -------------------------------------------------------------------------------- /benchdata/few-fields.json: -------------------------------------------------------------------------------- 1 | { 2 | "first": "5d57e438c48c6e5d4ca83b29", 3 | "index": 0, 4 | "guid": "ac784884-a6a3-4987-bb0f-d19e09462677", 5 | "isActive": false, 6 | "balanceccc": "$1,961.04", 7 | "picturecccc": "http://placehold.it/32x32", 8 | "addge": 33, 9 | "agfe": 33, 10 | "middle": "PARCOE", 11 | "agffffe": 33, 12 | "agae": 33, 13 | "agfae": 33, 14 | "agfadse": 33, 15 | "eyeColor": "blue", 16 | "name": "Mcleod Mendez", 17 | "last": "PARCOE" 18 | } -------------------------------------------------------------------------------- /benchdata/heavy.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": "5d57e438c48c6e5d4ca83b29", 3 | "index": 0, 4 | "guid": "ac784884-a6a3-4987-bb0f-d19e09462677", 5 | "isActive": false, 6 | "balance": "$1,961.04", 7 | "picture": "http://placehold.it/32x32", 8 | "age": 33, 9 | "eyeColor": "blue", 10 | "name": "Mcleod Mendez", 11 | "gender": "male", 12 | "company": "PARCOE", 13 | "first": { 14 | "_id": "5d57e438c48c6e5d4ca83b29", 15 | "index": 0, 16 | "guid": "ac784884-a6a3-4987-bb0f-d19e09462677", 17 | "isActive": false, 18 | "balance": "$1,961.04", 19 | "picture": "http://placehold.it/32x32", 20 | "age": 33, 21 | "eyeColor": "blue", 22 | "name": "Mcleod Mendez", 23 | "gender": "male", 24 | "company": "PARCOE", 25 | "second": { 26 | "_id": "5d57e438c48c6e5d4ca83b29", 27 | "index": 0, 28 | "guid": "ac784884-a6a3-4987-bb0f-d19e09462677", 29 | "isActive": false, 30 | "balance": "$1,961.04", 31 | "picture": "http://placehold.it/32x32", 32 | "age": 33, 33 | "eyeColor": "blue", 34 | "name": "Mcleod Mendez", 35 | "gender": "male", 36 | "company": "PARCOE", 37 | "email": "mcleodmendez@parcoe.com", 38 | "phone": "+1 (829) 448-3249", 39 | "address": "336 Luquer Street, Highland, Nebraska, 8045", 40 | "about": "Ad nostrud duis incididunt nostrud. Labore officia mollit veniam aliqua dolore excepteur nulla nostrud mollit eiusmod esse. In exercitation in pariatur duis ut adipisicing veniam eu commodo id irure excepteur irure.", 41 | "registered": "2018-04-01T09:23:30 -03:00", 42 | "latitude": 81.928262, 43 | "longitude": -163.266097, 44 | "tags": [ 45 | "esse", 46 | "mollit", 47 | "ullamco", 48 | "elit", 49 | "proident", 50 | "deserunt", 51 | "laboris" 52 | ], 53 | "friends": [ 54 | { 55 | "id": 0, 56 | "name": "Haley Howe" 57 | }, 58 | { 59 | "id": 1, 60 | "name": "Haley Kramer" 61 | }, 62 | { 63 | "id": 2, 64 | "name": "Juana Donovan" 65 | } 66 | ], 67 | "gdreeting": "Hello, Mcleod Mendez! You have 5 unread messages.", 68 | "fadvoriteFruit": "apple", 69 | "_did": "5d57e4389aaf255b59c7a859", 70 | "inddex": 1, 71 | "guidd": "a0dc951b-f858-40b7-aa45-ff0645b69f37", 72 | "isAcdtive": false, 73 | "balandce": "$1,210.92", 74 | "picturde": "http://placehold.it/32x32", 75 | "third": { 76 | "balance": "$1,961.04", 77 | "picture": "http://placehold.it/32x32", 78 | "age": 33, 79 | "eyeColor": "blue", 80 | "name": "Mcleod Mendez", 81 | "gender": "male", 82 | "fourth": { 83 | "pictbure": "http://placehold.it/32x32", 84 | "agbe": 27, 85 | "eybeColor": "brown", 86 | "nabme": "Casey Brock", 87 | "gebnder": "male", 88 | "cobmpany": "SKINSERVE", 89 | "embail": "caseybrock@skinserve.com", 90 | "phone": "+1 (904) 415-3002", 91 | "fifth": { 92 | "pictbure": "http://placehold.it/32x32", 93 | "agbe": 27, 94 | "eybeColor": "brown", 95 | "nabme": "Casey Brock", 96 | "gebnder": "male", 97 | "ok": "ok", 98 | "embail": "caseybrock@skinserve.com", 99 | "phone": "+1 (904) 415-3002", 100 | "address": "385 Strong Place, Villarreal, South Dakota, 9753", 101 | "about": "Aute labore est proident quis nostrud voluptate non mollit. Proident tempor id excepteur elit sint fugiat duis non proident laboris consequat tempor non. Amet laboris mollit fugiat non non. Incididunt ea eu sint aliquip minim mollit esse reprehenderit aliqua culpa excepteur mollit elit eu. Non cillum do duis commodo labore proident cupidatat aute. Dolor anim esse aute veniam exercitation. Ut pariatur sunt consequat et dolore id consectetur fugiat adipisicing anim tempor sint quis ex.", 102 | "registered": "2017-11-06T11:39:47 -03:00", 103 | "latitude": -33.926972, 104 | "longitude": -22.782789 105 | }, 106 | "address": "385 Strong Place, Villarreal, South Dakota, 9753", 107 | "about": "Aute labore est proident quis nostrud voluptate non mollit. Proident tempor id excepteur elit sint fugiat duis non proident laboris consequat tempor non. Amet laboris mollit fugiat non non. Incididunt ea eu sint aliquip minim mollit esse reprehenderit aliqua culpa excepteur mollit elit eu. Non cillum do duis commodo labore proident cupidatat aute. Dolor anim esse aute veniam exercitation. Ut pariatur sunt consequat et dolore id consectetur fugiat adipisicing anim tempor sint quis ex.", 108 | "registered": "2017-11-06T11:39:47 -03:00", 109 | "latitude": -33.926972, 110 | "longitude": -22.782789 111 | }, 112 | "company": "PARCOE", 113 | "email": "mcleodmendez@parcoe.com", 114 | "phone": "+1 (829) 448-3249", 115 | "address": "336 Luquer Street, Highland, Nebraska, 8045", 116 | "about": "Ad nostrud duis incididunt nostrud. Labore officia mollit veniam aliqua dolore excepteur nulla nostrud mollit eiusmod esse. In exercitation in pariatur duis ut adipisicing veniam eu commodo id irure excepteur irure.", 117 | "registered": "2018-04-01T09:23:30 -03:00", 118 | "latitude": 81.928262, 119 | "longitude": -163.266097 120 | }, 121 | "dage": 29, 122 | "edyeColor": "blue", 123 | "nadme": "Sullivan Roy", 124 | "gendder": "male", 125 | "compdany": "MOBILDATA", 126 | "emaild": "sullivanroy@mobildata.com", 127 | "dphone": "+1 (940) 459-3444", 128 | "adddress": "108 Cyrus Avenue, Hebron, Texas, 3978", 129 | "abdout": "Qui non voluptate magna fugiat ex et sit anim deserunt consequat eu amet occaecat. Esse quis magna occaecat culpa sit consequat veniam nostrud veniam consequat sunt enim sint. Consectetur excepteur amet labore cillum exercitation et consectetur. Sunt duis esse officia labore nisi sint exercitation ut commodo. Ullamco aute mollit magna irure ad velit nisi cillum fugiat nostrud ea veniam. In ut eu anim tempor enim fugiat velit sit occaecat ea. Proident excepteur id consectetur est proident sunt qui deserunt dolore magna.", 130 | "regdistered": "2018-03-20T12:33:41 -03:00", 131 | "latidtude": 23.762864, 132 | "longidtude": 102.443683, 133 | "tagds": [ 134 | "minim", 135 | "incididunt", 136 | "velit", 137 | "ex", 138 | "irure", 139 | "laborum", 140 | "aute" 141 | ], 142 | "frifffends": [ 143 | { 144 | "id": 0, 145 | "name": "Robles Slater" 146 | }, 147 | { 148 | "id": 1, 149 | "name": "Stokes Kemp" 150 | }, 151 | { 152 | "id": 2, 153 | "name": "Todd Levine" 154 | } 155 | ], 156 | "greeting": "Hello, Sullivan Roy! You have 4 unread messages.", 157 | "favoriteFruit": "apple" 158 | }, 159 | "_bid": "5d57e43802d7826a18ecc90c", 160 | "inbdex": 2, 161 | "guibd": "fc0ba5f5-df87-430a-8c6a-9b7cb8bb822c", 162 | "isAcbtive": false, 163 | "balabnce": "$3,739.80", 164 | "pictbure": "http://placehold.it/32x32", 165 | "agbe": 27, 166 | "eybeColor": "brown", 167 | "nabme": "Casey Brock", 168 | "gebnder": "male", 169 | "cobmpany": "SKINSERVE", 170 | "embail": "caseybrock@skinserve.com", 171 | "phone": "+1 (904) 415-3002", 172 | "address": "385 Strong Place, Villarreal, South Dakota, 9753", 173 | "about": "Aute labore est proident quis nostrud voluptate non mollit. Proident tempor id excepteur elit sint fugiat duis non proident laboris consequat tempor non. Amet laboris mollit fugiat non non. Incididunt ea eu sint aliquip minim mollit esse reprehenderit aliqua culpa excepteur mollit elit eu. Non cillum do duis commodo labore proident cupidatat aute. Dolor anim esse aute veniam exercitation. Ut pariatur sunt consequat et dolore id consectetur fugiat adipisicing anim tempor sint quis ex.", 174 | "registered": "2017-11-06T11:39:47 -03:00", 175 | "latitude": -33.926972, 176 | "longitude": -22.782789, 177 | "tags": [ 178 | "occaecat", 179 | "laborum", 180 | "non", 181 | "et", 182 | "proident", 183 | "ut", 184 | "consectetur" 185 | ], 186 | "friends": [ 187 | { 188 | "id": 0, 189 | "name": "Mae Jordan" 190 | }, 191 | { 192 | "id": 1, 193 | "name": "Durham Byers" 194 | }, 195 | { 196 | "id": 2, 197 | "name": "Howard Morin" 198 | } 199 | ], 200 | "greeting": "Hello, Casey Brock! You have 10 unread messages.", 201 | "favoriteFruit": "banana" 202 | }, 203 | "_did": "5d57e438c48c6e5d4ca83b29", 204 | "inddex": 0, 205 | "gduid": "ac784884-a6a3-4987-bb0f-d19e09462677", 206 | "idsActive": false, 207 | "baldance": "$1,961.04", 208 | "piddcture": "http://placehold.it/32x32", 209 | "agde": 33, 210 | "eyeCdolor": "blue", 211 | "namde": "Mcleod Mendez", 212 | "genddder": "male", 213 | "compadny": "PARCOE", 214 | "greeting": "Hello, Sullivan Roy! You have 4 unread messages.", 215 | "favoriteFruit": "apple" 216 | } -------------------------------------------------------------------------------- /benchdata/light-ws.json: -------------------------------------------------------------------------------- 1 | { 2 | "_id": "5d53006246df0b962b787d11", 3 | "index": "0", 4 | "guid": "80d75945-6251-46a2-b6c9-a10094beed6e", 5 | "isActive": "false", 6 | "balance": "$2,258.24", 7 | "picture": "http://placehold.it/32x32", 8 | "age": "34", 9 | "eyeColor": "brown", 10 | "company": "NIMON", 11 | "email": "anne.everett@nimon.name", 12 | "phone": "+1(946)560-2227", 13 | "address": "815EmpireBoulevard,Blue,Nevada,5617", 14 | "about": "Proidentoccaecateulaborislaboreofficialaborumvelitanimnulla.Laboreametoccaecataliquaminimlaboreadenimdolorelaborum.Eiusmodesseeiusmodaliquacillumullamcodonisivelitesseincididunt.Ininestessereprehenderitirureaniminsit.", 15 | "registered": "Friday,May27,20165:05AM", 16 | "latitude": "-5.922381", 17 | "longitude": "-49.143968", 18 | "greeting": "Hello,Anne!Youhave7unreadmessages.", 19 | "favoriteFruit": "banana" 20 | } -------------------------------------------------------------------------------- /benchdata/many-fields.json: -------------------------------------------------------------------------------- 1 | { 2 | "first": "5d57e438c48c6e5d4ca83b29", 3 | "index": 0, 4 | "guid": "ac784884-a6a3-4987-bb0f-d19e09462677", 5 | "isActive": false, 6 | "100": "$1,961.04", 7 | "32323": "http://placehold.it/32x32", 8 | "9": 33, 9 | "8": "blue", 10 | "1": "Mcleod Mendez", 11 | "2": "male", 12 | "4": "PARCOE", 13 | "3": "PARCOE", 14 | "co2mp2fany": "PARCOE", 15 | "22": "PARCOE", 16 | "co22222empany": "PARCOE", 17 | "co2222222fmpany": "PARCOE", 18 | "balance": "$1,961.04", 19 | "picture": "http://placehold.it/32x32", 20 | "age": 33, 21 | "eyeColor": "blue", 22 | "name": "Mcleod Mendez", 23 | "gender": "male", 24 | "compafny": "PARCOE", 25 | "com1pany": "PARCOE", 26 | "co2mpfany": "PARCOE", 27 | "co222fmpany": "PARCOE", 28 | "co222empany": "PARCOE", 29 | "co2222fmpany": "PARCOE", 30 | "co2222mpany": "PARCOE", 31 | "compfany": "PARCOE", 32 | "co2mpany": "PARCOE", 33 | "compwfany": "PARCOE", 34 | "compfweany": "PARCOE", 35 | "comfwepany": "PARCOE", 36 | "compefefany": "PARCOE", 37 | "comfeqpany": "PARCOE", 38 | "comfwefvwepany": "PARCOE", 39 | "comvfqfqewfpany": "PARCOE", 40 | "compweewany": "PARCOE", 41 | "wff": "PARCOE", 42 | "comqvvpany": "PARCOE", 43 | "comvqwevpany": "PARCOE", 44 | "compvany": "PARCOE", 45 | "compvqeany": "PARCOE", 46 | "middle": "PARCOE", 47 | "comspany": "PARCOE", 48 | "compaany": "PARCOE", 49 | "compaaqny": "PARCOE", 50 | "compaqny": "PARCOE", 51 | "_id1": "5d57e438c48c6e5d4ca83b29", 52 | "index1": 0, 53 | "guid1": "ac784884-a6a3-4987-bb0f-d19e09462677", 54 | "isActive1": false, 55 | "balance1": "$1,961.04", 56 | "picture1": "http://placehold.it/32x32", 57 | "age1": 33, 58 | "eyeColor1": "blue", 59 | "name1": "Mcleod Mendez", 60 | "gender1": "male", 61 | "compafny1": "PARCOE", 62 | "com1pany1": "PARCOE", 63 | "co2mpfany1": "PARCOE", 64 | "co222fmpany1": "PARCOE", 65 | "co222empany1": "PARCOE", 66 | "co2222fmpany1": "PARCOE", 67 | "co2222mpany1": "PARCOE", 68 | "compfany1": "PARCOE", 69 | "co2mpany1": "PARCOE", 70 | "compwfany1": "PARCOE", 71 | "compfweany1": "PARCOE", 72 | "comfwepany1": "PARCOE", 73 | "compefefany1": "PARCOE", 74 | "comfeqpany1": "PARCOE", 75 | "comfwefvwepany1": "PARCOE", 76 | "comvfqfqewfpany1": "PARCOE", 77 | "compweewany1": "PARCOE", 78 | "wff1": "PARCOE", 79 | "comqvvpany1": "PARCOE", 80 | "compffffany1": "PARCOE", 81 | "co2mpanfy1": "PARCOE", 82 | "compwfa3ny1": "PARCOE", 83 | "compfwe244any1": "PARCOE", 84 | "comfwepa4ny1": "PARCOE", 85 | "compefe324fany1": "PARCOE", 86 | "comfeqpa4ny1": "PARCOE", 87 | "comfwef43vwepany1": "PARCOE", 88 | "comvfqfqe4wfpany1": "PARCOE", 89 | "compweew4any1": "PARCOE", 90 | "wff31": "PARCOE", 91 | "comq55vvpany1": "PARCOE", 92 | "comvqwevpany1": "PARCOE", 93 | "compvany1": "PARCOE", 94 | "compvqeany1": "PARCOE", 95 | "compfanvy1": "PARCOE", 96 | "comspany1": "PARCOE", 97 | "compaany1": "PARCOE", 98 | "compaaqny1": "PARCOE", 99 | "last": "PARCOE" 100 | } -------------------------------------------------------------------------------- /benchdata/many-objects.json: -------------------------------------------------------------------------------- 1 | { 2 | "no_key2": "100", 3 | "deeper": { 4 | "no_key2": "100", 5 | "deeper": { 6 | "no_key2": "100", 7 | "deeper": { 8 | "no_key2": "100", 9 | "deeper": { 10 | "no_key2": "100", 11 | "deeper": { 12 | "no_key2": "100", 13 | "deeper": { 14 | "no_key2": "100", 15 | "deeper": { 16 | "no_key2": "100", 17 | "deeper": { 18 | "no_key2": "100", 19 | "deeper": { 20 | "no_key2": "100", 21 | "deeper": { 22 | "no_key2": "100", 23 | "deeper": { 24 | "no_key2": "100", 25 | "deeper": { 26 | "no_key2": "100", 27 | "deeper": "ok" 28 | }, 29 | "no_key1": "100" 30 | }, 31 | "no_key1": "100" 32 | }, 33 | "no_key1": "100" 34 | }, 35 | "no_key1": "100" 36 | }, 37 | "no_key1": "100" 38 | }, 39 | "no_key1": "100" 40 | }, 41 | "no_key1": "100" 42 | }, 43 | "no_key1": "100" 44 | }, 45 | "no_key1": "100" 46 | }, 47 | "no_key1": "100" 48 | }, 49 | "no_key1": "100" 50 | } 51 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ozontech/insane-json 2 | 3 | go 1.12 4 | 5 | require github.com/stretchr/testify v1.3.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= 2 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= 3 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 4 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/dvyukov/go-fuzz v0.0.0-20190828145000-1810d380ab9c h1:DFsz4uHHaXIkv6K/2JhR5ufgx/pquhSsBgB25b5Oj1k= 7 | github.com/dvyukov/go-fuzz v0.0.0-20190828145000-1810d380ab9c/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= 8 | github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= 9 | github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= 10 | github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70 h1:XTnP8fJpa4Kvpw2qARB4KS9izqxPS0Sd92cDlY3uk+w= 11 | github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 12 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c= 13 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 14 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 15 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 16 | github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= 17 | github.com/stephens2424/writerset v1.0.2 h1:znRLgU6g8RS5euYRcy004XeE4W+Tu44kALzy7ghPif8= 18 | github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= 19 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 20 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 21 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 22 | github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE= 23 | github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o= 24 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 25 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 26 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 27 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 28 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 29 | golang.org/x/tools v0.0.0-20190829183309-bce6695301c7 h1:7OqZ5Xk9S1aGDat+GsbKnCcIkOxL2l8KAAzbYZ8OH/I= 30 | golang.org/x/tools v0.0.0-20190829183309-bce6695301c7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 31 | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18 h1:xFbv3LvlvQAmbNJFCBKRv1Ccvnh9FVsW0FX2kTWWowE= 32 | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 33 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 34 | -------------------------------------------------------------------------------- /insane.go: -------------------------------------------------------------------------------- 1 | package insaneJSON 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io/ioutil" 7 | "math" 8 | "math/rand" 9 | "reflect" 10 | "strconv" 11 | "strings" 12 | "sync" 13 | "unicode/utf16" 14 | "unicode/utf8" 15 | "unsafe" 16 | ) 17 | 18 | // ===LATEST BENCH RESULTS=== 19 | // BenchmarkFair/complex-stable-flavor|complex-4 1000 2014381 ns/op 638.58 MB/s 2107 B/op 10 allocs/op 20 | // BenchmarkFair/complex-chaotic-flavor|complex-4 3000 516549 ns/op 160.88 MB/s 36291 B/op 756 allocs/op 21 | // BenchmarkFair/get-stable-flavor|get-4 2000000 968 ns/op 664405.82 MB/s 0 B/op 0 allocs/op 22 | // BenchmarkFair/get-chaotic-flavor|get-4 200000 10884 ns/op 3817.32 MB/s 4032 B/op 84 allocs/op 23 | // BenchmarkValueDecodeInt-4 2000000 920 ns/op 288 B/op 6 allocs/op 24 | // BenchmarkValueEscapeString-4 1000000 1821 ns/op 0 B/op 0 allocs/op 25 | 26 | // 0-11 bits – node type 27 | // 12-35 bits – node index 28 | // 36-59 bits – dirty sequence 29 | // 60 bit – map usage 30 | type hellBits uint64 31 | 32 | const ( 33 | hellBitObject hellBits = 1 << 0 34 | hellBitEnd hellBits = 1 << 1 35 | hellBitArray hellBits = 1 << 2 36 | hellBitArrayEnd hellBits = 1 << 3 37 | hellBitString hellBits = 1 << 4 38 | hellBitEscapedString hellBits = 1 << 5 39 | hellBitNumber hellBits = 1 << 6 40 | hellBitTrue hellBits = 1 << 7 41 | hellBitFalse hellBits = 1 << 8 42 | hellBitNull hellBits = 1 << 9 43 | hellBitField hellBits = 1 << 10 44 | hellBitEscapedField hellBits = 1 << 11 45 | hellBitTypeFilter hellBits = 1<<11 - 1 46 | 47 | hellBitUseMap hellBits = 1 << 60 48 | hellBitsUseMapReset = 1<<64 - 1 - hellBitUseMap 49 | 50 | hellBitsDirtyFilter = 0x0FFFFFF000000000 51 | hellBitsDirtyReset hellBits = 0xF000000FFFFFFFFF 52 | hellBitsDirtyStep hellBits = 1 << 36 53 | 54 | hellBitsIndexFilter hellBits = 0x0000000FFFFFF000 55 | hellBitsIndexReset hellBits = 0xFFFFFFF000000FFF 56 | hellBitsIndexStep hellBits = 1 << 12 57 | 58 | hex = "0123456789abcdef" 59 | ) 60 | 61 | var ( 62 | StartNodePoolSize = 128 63 | MapUseThreshold = 16 64 | DisableBeautifulErrors = false // set to "true" for best performance, if you have many decode errors 65 | 66 | decoderPool = sync.Pool{ 67 | New: func() interface{} { 68 | decoder := &decoder{} 69 | decoder.initPool() 70 | return decoder 71 | }, 72 | } 73 | 74 | numbersMap = make([]byte, 256) 75 | 76 | // decode errors 77 | ErrEmptyJSON = errors.New("json is empty") 78 | ErrUnexpectedJSONEnding = errors.New("unexpected ending of json") 79 | ErrUnexpectedEndOfString = errors.New("unexpected end of string") 80 | ErrUnexpectedEndOfTrue = errors.New("unexpected end of true") 81 | ErrUnexpectedEndOfFalse = errors.New("unexpected end of false") 82 | ErrUnexpectedEndOfNull = errors.New("unexpected end of null") 83 | ErrUnexpectedEndOfObjectField = errors.New("unexpected end of object field") 84 | ErrExpectedObjectField = errors.New("expected object field") 85 | ErrExpectedObjectFieldSeparator = errors.New("expected object field separator") 86 | ErrExpectedValue = errors.New("expected value") 87 | ErrExpectedComma = errors.New("expected comma") 88 | 89 | // api errors 90 | ErrRootIsNil = errors.New("root is nil") 91 | ErrNotFound = errors.New("node isn't found") 92 | ErrNotObject = errors.New("node isn't an object") 93 | ErrNotArray = errors.New("node isn't an array") 94 | ErrNotBool = errors.New("node isn't a bool") 95 | ErrNotString = errors.New("node isn't a string") 96 | ErrNotNumber = errors.New("node isn't a number") 97 | ErrNotField = errors.New("node isn't an object field") 98 | ) 99 | 100 | func init() { 101 | numbersMap['.'] = 1 102 | numbersMap['-'] = 1 103 | numbersMap['e'] = 1 104 | numbersMap['E'] = 1 105 | numbersMap['+'] = 1 106 | } 107 | 108 | /* 109 | Node Is a building block of the decoded JSON. There is seven basic nodes: 110 | 1. Object 111 | 2. Array 112 | 3. String 113 | 4. Number 114 | 5. True 115 | 6. False 116 | 7. Null 117 | 118 | And a special one – Field, which represents the field(key) on an objects. 119 | It allows to easily change field's name, checkout MutateToField() function. 120 | */ 121 | type Node struct { 122 | bits hellBits 123 | data string 124 | next *Node 125 | parent *Node 126 | nodes []*Node 127 | fields *map[string]int 128 | } 129 | 130 | /* 131 | Root is a top Node of decoded JSON. It holds decoder, current JSON data and pool of Nodes. 132 | Node pool is used to reduce memory allocations and GC time. 133 | Checkout ReleaseMem()/ReleasePoolMem()/ReleaseBufMem() to clear pools. 134 | Root can be reused to decode another JSON using DecodeBytes()/DecodeString(). 135 | Also Root can decode additional JSON using DecodeAdditionalBytes()/DecodeAdditionalString(). 136 | */ 137 | type Root struct { 138 | *Node 139 | decoder *decoder 140 | } 141 | 142 | /* 143 | StrictNode implements API with error handling. 144 | Transform any Node with MutateToStrict(), Mutate*()/As*() functions will return an error 145 | */ 146 | type StrictNode struct { 147 | *Node 148 | } 149 | 150 | type decoder struct { 151 | buf []byte 152 | root Root 153 | nodePool []*Node 154 | nodeCount int 155 | } 156 | 157 | /* 158 | ReleaseMem sends node pool and internal buffer to GC. 159 | Useful to reduce memory usage after decoding big JSON. 160 | */ 161 | func (r *Root) ReleaseMem() { 162 | r.ReleasePoolMem() 163 | r.ReleaseBufMem() 164 | } 165 | 166 | /* 167 | ReleasePoolMem sends node pool to GC. 168 | Useful to reduce memory usage after decoding big JSON. 169 | */ 170 | func (r *Root) ReleasePoolMem() { 171 | r.decoder.initPool() 172 | } 173 | 174 | /* 175 | ReleaseBufMem sends internal buffer to GC. 176 | Useful to reduce memory usage after decoding big JSON. 177 | */ 178 | func (r *Root) ReleaseBufMem() { 179 | r.decoder.buf = make([]byte, 0, 0) 180 | } 181 | 182 | /* 183 | BuffCap returns current size of internal buffer. 184 | */ 185 | func (r *Root) BuffCap() int { 186 | return cap(r.decoder.buf) 187 | } 188 | 189 | /* 190 | PoolSize returns how many Node objects is in the pool right now. 191 | */ 192 | func (r *Root) PoolSize() int { 193 | return len(r.decoder.nodePool) 194 | } 195 | 196 | // ******************** // 197 | // MAIN SHIT // 198 | // ******************** // 199 | 200 | // decode is a legendary function for decoding JSONs 201 | func (d *decoder) decode(json string, shouldReset bool) (*Node, error) { 202 | if len(json) == 0 { 203 | return nil, insaneErr(ErrEmptyJSON, json, 0) 204 | } 205 | 206 | if shouldReset { 207 | d.nodeCount = 0 208 | d.buf = d.buf[:0] 209 | } 210 | o := len(d.buf) 211 | 212 | d.buf = append(d.buf, json...) 213 | json = toString(d.buf) 214 | l := len(json) 215 | 216 | nodePool := d.nodePool 217 | nodePoolLen := len(nodePool) 218 | nodes := d.nodeCount 219 | 220 | root := nodePool[nodes] 221 | root.parent = nil 222 | curNode := nodePool[nodes] 223 | topNode := root.parent 224 | 225 | c := byte('i') // i means insane 226 | t := 0 227 | x := 0 228 | goto decode 229 | decodeObject: 230 | if o == l { 231 | return nil, insaneErr(ErrUnexpectedJSONEnding, json, o) 232 | } 233 | 234 | // skip wc 235 | c = json[o] 236 | o++ 237 | if c <= 0x20 { 238 | for o != l { 239 | c = json[o] 240 | o++ 241 | if c == 0x20 || c == 0x0A || c == 0x09 || c == 0x0D { 242 | continue 243 | } 244 | break 245 | } 246 | } 247 | 248 | if c == '}' { 249 | curNode.next = nodePool[nodes] 250 | curNode = curNode.next 251 | nodes++ 252 | 253 | curNode.bits = hellBitEnd 254 | curNode.parent = topNode 255 | 256 | topNode.next = nodePool[nodes] 257 | topNode = topNode.parent 258 | 259 | goto pop 260 | } 261 | 262 | if c != ',' { 263 | if len(topNode.nodes) > 0 { 264 | return nil, insaneErr(ErrExpectedComma, json, o) 265 | } 266 | o-- 267 | } else { 268 | if len(topNode.nodes) == 0 { 269 | return nil, insaneErr(ErrExpectedObjectField, json, o) 270 | } 271 | if o == l { 272 | return nil, insaneErr(ErrUnexpectedJSONEnding, json, o) 273 | } 274 | } 275 | 276 | // skip wc 277 | c = json[o] 278 | o++ 279 | if c <= 0x20 { 280 | for o != l { 281 | c = json[o] 282 | o++ 283 | if c == 0x20 || c == 0x0A || c == 0x09 || c == 0x0D { 284 | continue 285 | } 286 | break 287 | } 288 | } 289 | 290 | if c != '"' { 291 | return nil, insaneErr(ErrExpectedObjectField, json, o) 292 | } 293 | 294 | t = o - 1 295 | for { 296 | x = strings.IndexByte(json[o:], '"') 297 | o += x + 1 298 | if x < 0 { 299 | return nil, insaneErr(ErrUnexpectedEndOfObjectField, json, o) 300 | } 301 | 302 | if x == 0 || json[o-2] != '\\' { 303 | break 304 | } 305 | 306 | // untangle fucking escaping hell 307 | z := o - 3 308 | for json[z] == '\\' { 309 | z-- 310 | } 311 | if (o-z)%2 == 0 { 312 | break 313 | } 314 | 315 | } 316 | if o == l { 317 | return nil, insaneErr(ErrExpectedObjectFieldSeparator, json, o) 318 | } 319 | 320 | curNode.next = nodePool[nodes] 321 | curNode = curNode.next 322 | nodes++ 323 | 324 | // skip wc 325 | c = json[o] 326 | o++ 327 | if c <= 0x20 { 328 | for o != l { 329 | c = json[o] 330 | o++ 331 | if c == 0x20 || c == 0x0A || c == 0x09 || c == 0x0D { 332 | continue 333 | } 334 | break 335 | } 336 | } 337 | 338 | if c != ':' { 339 | return nil, insaneErr(ErrExpectedObjectFieldSeparator, json, o) 340 | } 341 | if o == l { 342 | return nil, insaneErr(ErrExpectedValue, json, o) 343 | } 344 | curNode.bits = hellBitEscapedField 345 | curNode.data = json[t:o] 346 | curNode.parent = topNode 347 | topNode.nodes = append(topNode.nodes, curNode) 348 | 349 | goto decode 350 | decodeArray: 351 | if o == l { 352 | return nil, insaneErr(ErrUnexpectedJSONEnding, json, o) 353 | } 354 | // skip wc 355 | c = json[o] 356 | o++ 357 | if c <= 0x20 { 358 | for o != l { 359 | c = json[o] 360 | o++ 361 | if c == 0x20 || c == 0x0A || c == 0x09 || c == 0x0D { 362 | continue 363 | } 364 | break 365 | } 366 | } 367 | 368 | if c == ']' { 369 | curNode.next = nodePool[nodes] 370 | curNode = curNode.next 371 | nodes++ 372 | 373 | curNode.bits = hellBitArrayEnd 374 | curNode.parent = topNode 375 | 376 | topNode.next = nodePool[nodes] 377 | topNode = topNode.parent 378 | 379 | goto pop 380 | } 381 | 382 | if c != ',' { 383 | if len(topNode.nodes) > 0 { 384 | return nil, insaneErr(ErrExpectedComma, json, o) 385 | } 386 | o-- 387 | } else { 388 | if len(topNode.nodes) == 0 { 389 | return nil, insaneErr(ErrExpectedValue, json, o) 390 | } 391 | if o == l { 392 | return nil, insaneErr(ErrUnexpectedJSONEnding, json, o) 393 | } 394 | } 395 | 396 | topNode.nodes = append(topNode.nodes, nodePool[nodes]) 397 | decode: 398 | // skip wc 399 | c = json[o] 400 | o++ 401 | if c <= 0x20 { 402 | for o != l { 403 | c = json[o] 404 | o++ 405 | if c == 0x20 || c == 0x0A || c == 0x09 || c == 0x0D { 406 | continue 407 | } 408 | break 409 | } 410 | } 411 | switch c { 412 | case '{': 413 | if o == l { 414 | return nil, insaneErr(ErrExpectedObjectField, json, o) 415 | } 416 | 417 | curNode.next = nodePool[nodes] 418 | curNode = curNode.next 419 | nodes++ 420 | 421 | curNode.bits = hellBitObject 422 | curNode.nodes = curNode.nodes[:0] 423 | curNode.parent = topNode 424 | 425 | topNode = curNode 426 | if nodes >= nodePoolLen-1 { 427 | nodePool = d.expandPool() 428 | nodePoolLen = len(nodePool) 429 | } 430 | goto decodeObject 431 | case '[': 432 | if o == l { 433 | return nil, insaneErr(ErrExpectedValue, json, o) 434 | } 435 | curNode.next = nodePool[nodes] 436 | curNode = curNode.next 437 | nodes++ 438 | 439 | curNode.bits = hellBitArray 440 | curNode.nodes = curNode.nodes[:0] 441 | curNode.parent = topNode 442 | 443 | topNode = curNode 444 | if nodes >= nodePoolLen-1 { 445 | nodePool = d.expandPool() 446 | nodePoolLen = len(nodePool) 447 | } 448 | goto decodeArray 449 | case '"': 450 | t = o 451 | for { 452 | x := strings.IndexByte(json[t:], '"') 453 | t += x + 1 454 | if x < 0 { 455 | return nil, insaneErr(ErrUnexpectedEndOfString, json, o) 456 | } 457 | if x == 0 || json[t-2] != '\\' { 458 | break 459 | } 460 | 461 | // untangle fucking escaping hell 462 | z := t - 3 463 | for json[z] == '\\' { 464 | z-- 465 | } 466 | if (t-z)%2 == 0 { 467 | break 468 | } 469 | } 470 | 471 | curNode.next = nodePool[nodes] 472 | curNode = curNode.next 473 | nodes++ 474 | 475 | curNode.bits = hellBitEscapedString 476 | curNode.data = json[o-1 : t] 477 | curNode.parent = topNode 478 | 479 | o = t 480 | case 't': 481 | if len(json) < o+3 || json[o:o+3] != "rue" { 482 | return nil, insaneErr(ErrUnexpectedEndOfTrue, json, o) 483 | } 484 | o += 3 485 | 486 | curNode.next = nodePool[nodes] 487 | curNode = curNode.next 488 | nodes++ 489 | 490 | curNode.bits = hellBitTrue 491 | curNode.parent = topNode 492 | 493 | case 'f': 494 | if len(json) < o+4 || json[o:o+4] != "alse" { 495 | return nil, insaneErr(ErrUnexpectedEndOfFalse, json, o) 496 | } 497 | o += 4 498 | 499 | curNode.next = nodePool[nodes] 500 | curNode = curNode.next 501 | nodes++ 502 | 503 | curNode.bits = hellBitFalse 504 | curNode.parent = topNode 505 | 506 | case 'n': 507 | if len(json) < o+3 || json[o:o+3] != "ull" { 508 | return nil, insaneErr(ErrUnexpectedEndOfNull, json, o) 509 | } 510 | o += 3 511 | 512 | curNode.next = nodePool[nodes] 513 | curNode = curNode.next 514 | nodes++ 515 | 516 | curNode.bits = hellBitNull 517 | curNode.parent = topNode 518 | default: 519 | o-- 520 | t = o 521 | for ; o != l && ((json[o] >= '0' && json[o] <= '9') || numbersMap[json[o]] == 1); o++ { 522 | } 523 | if t == o { 524 | return nil, insaneErr(ErrExpectedValue, json, o) 525 | } 526 | 527 | curNode.next = nodePool[nodes] 528 | curNode = curNode.next 529 | nodes++ 530 | 531 | curNode.bits = hellBitNumber 532 | curNode.data = json[t:o] 533 | curNode.parent = topNode 534 | } 535 | pop: 536 | if topNode == nil { 537 | goto exit 538 | } 539 | 540 | if nodes >= nodePoolLen-1 { 541 | nodePool = d.expandPool() 542 | nodePoolLen = len(nodePool) 543 | } 544 | 545 | if topNode.bits&hellBitObject == hellBitObject { 546 | goto decodeObject 547 | } else { 548 | goto decodeArray 549 | } 550 | exit: 551 | if o != l { 552 | // skip wc 553 | c = json[o] 554 | if c <= 0x20 { 555 | for o != l { 556 | c = json[o] 557 | o++ 558 | if c != 0x20 && c != 0x0A && c != 0x09 && c != 0x0D { 559 | break 560 | } 561 | } 562 | } 563 | 564 | if o != l { 565 | return nil, insaneErr(ErrUnexpectedJSONEnding, json, o) 566 | } 567 | } 568 | 569 | root.next = nil 570 | curNode.next = nil 571 | d.nodeCount = nodes 572 | 573 | return root, nil 574 | } 575 | 576 | func (d *decoder) decodeHeadless(json string, isPooled bool) (*Root, error) { 577 | root, err := d.decode(json, true) 578 | if err != nil { 579 | if isPooled { 580 | backToPool(d) 581 | } 582 | return nil, err 583 | } 584 | 585 | d.root.Node = root 586 | d.root.decoder = d 587 | 588 | return &d.root, nil 589 | } 590 | 591 | // EncodeToByte legendary insane encode function 592 | // slow because it allocates new byte buffer on every call 593 | // use Encode to reuse already created buffer and gain more performance 594 | func (n *Node) EncodeToByte() []byte { 595 | return n.Encode([]byte{}) 596 | } 597 | 598 | // EncodeToString legendary insane encode function 599 | // slow because it allocates new string on every call 600 | // use Encode to reuse already created buffer and gain more performance 601 | func (n *Node) EncodeToString() string { 602 | return toString(n.Encode([]byte{})) 603 | } 604 | 605 | // Encode legendary insane encode function 606 | // uses already created byte buffer to place json data so 607 | // mem allocations may occur only if buffer isn't long enough 608 | // use it for performance 609 | func (n *Node) Encode(out []byte) []byte { 610 | s := 0 611 | curNode := n 612 | topNode := n 613 | 614 | if len(curNode.nodes) == 0 { 615 | if curNode.bits&hellBitObject == hellBitObject { 616 | return append(out, "{}"...) 617 | } 618 | if curNode.bits&hellBitArray == hellBitArray { 619 | return append(out, "[]"...) 620 | } 621 | } 622 | 623 | goto encodeSkip 624 | encode: 625 | out = append(out, ","...) 626 | encodeSkip: 627 | switch curNode.bits & hellBitTypeFilter { 628 | case hellBitObject: 629 | if len(curNode.nodes) == 0 { 630 | out = append(out, "{}"...) 631 | curNode = curNode.next 632 | goto popSkip 633 | } 634 | topNode = curNode 635 | out = append(out, '{') 636 | curNode = curNode.nodes[0] 637 | if curNode.bits&hellBitField == hellBitField { 638 | out = escapeString(out, curNode.data) 639 | out = append(out, ':') 640 | } else { 641 | out = append(out, curNode.data...) 642 | } 643 | curNode = curNode.next 644 | s++ 645 | goto encodeSkip 646 | case hellBitArray: 647 | if len(curNode.nodes) == 0 { 648 | out = append(out, "[]"...) 649 | curNode = curNode.next 650 | goto popSkip 651 | } 652 | topNode = curNode 653 | out = append(out, '[') 654 | curNode = curNode.nodes[0] 655 | s++ 656 | goto encodeSkip 657 | case hellBitNumber: 658 | out = append(out, curNode.data...) 659 | case hellBitString: 660 | out = escapeString(out, curNode.data) 661 | case hellBitEscapedString: 662 | out = append(out, curNode.data...) 663 | case hellBitFalse: 664 | out = append(out, "false"...) 665 | case hellBitTrue: 666 | out = append(out, "true"...) 667 | case hellBitNull: 668 | out = append(out, "null"...) 669 | } 670 | pop: 671 | curNode = curNode.next 672 | popSkip: 673 | if topNode.bits&hellBitArray == hellBitArray { 674 | if curNode.bits&hellBitArrayEnd == hellBitArrayEnd { 675 | out = append(out, "]"...) 676 | curNode = topNode 677 | topNode = topNode.parent 678 | s-- 679 | if s == 0 { 680 | return out 681 | } 682 | goto pop 683 | } 684 | goto encode 685 | } else if topNode.bits&hellBitObject == hellBitObject { 686 | if curNode.bits&hellBitEnd == hellBitEnd { 687 | out = append(out, "}"...) 688 | curNode = topNode 689 | topNode = topNode.parent 690 | s-- 691 | if s == 0 { 692 | return out 693 | } 694 | goto pop 695 | } 696 | out = append(out, ","...) 697 | if curNode.bits&hellBitField == hellBitField { 698 | out = escapeString(out, curNode.data) 699 | out = append(out, ':') 700 | } else { 701 | out = append(out, curNode.data...) 702 | } 703 | curNode = curNode.next 704 | goto encodeSkip 705 | } else { 706 | return out 707 | } 708 | } 709 | 710 | // Dig legendary insane dig function 711 | func (n *Node) Dig(path ...string) *Node { 712 | if n == nil { 713 | return nil 714 | } 715 | 716 | maxDepth := len(path) 717 | if maxDepth == 0 { 718 | return n 719 | } 720 | 721 | node := n 722 | curField := path[0] 723 | curDepth := 0 724 | get: 725 | if node.bits&hellBitArray == hellBitArray { 726 | goto getArray 727 | } 728 | 729 | if node.bits&hellBitObject != hellBitObject { 730 | return nil 731 | } 732 | 733 | if len(node.nodes) > MapUseThreshold { 734 | if node.bits&hellBitUseMap != hellBitUseMap { 735 | var m map[string]int 736 | if node.fields == nil { 737 | m = make(map[string]int, len(node.nodes)) 738 | node.fields = &m 739 | } else { 740 | m = *node.fields 741 | for field := range m { 742 | delete(m, field) 743 | } 744 | } 745 | 746 | for index, field := range node.nodes { 747 | if field.bits&hellBitEscapedField == hellBitEscapedField { 748 | field.unescapeField() 749 | } 750 | m[field.data] = index 751 | } 752 | node.bits |= hellBitUseMap 753 | } 754 | 755 | if node.bits&hellBitUseMap == hellBitUseMap { 756 | index, has := (*node.fields)[curField] 757 | if !has { 758 | return nil 759 | } 760 | 761 | curDepth++ 762 | if curDepth == maxDepth { 763 | result := (node.nodes)[index].next 764 | result.bits = result.bits&hellBitsDirtyReset | (node.bits & hellBitsDirtyFilter) 765 | result.setIndex(index) 766 | 767 | return result 768 | } 769 | 770 | curField = path[curDepth] 771 | node = (node.nodes)[index].next 772 | goto get 773 | } 774 | } 775 | 776 | for index, field := range node.nodes { 777 | if field.bits&hellBitEscapedField == hellBitEscapedField { 778 | field.unescapeField() 779 | } 780 | 781 | if field.data == curField { 782 | curDepth++ 783 | if curDepth == maxDepth { 784 | result := field.next 785 | result.bits = result.bits&hellBitsDirtyReset | (node.bits & hellBitsDirtyFilter) 786 | result.setIndex(index) 787 | 788 | return result 789 | } 790 | curField = path[curDepth] 791 | node = field.next 792 | goto get 793 | } 794 | } 795 | return nil 796 | getArray: 797 | index, err := strconv.Atoi(curField) 798 | if err != nil || index < 0 || index >= len(node.nodes) { 799 | return nil 800 | } 801 | curDepth++ 802 | if curDepth == maxDepth { 803 | result := (node.nodes)[index] 804 | result.bits = result.bits&hellBitsDirtyReset | (node.bits & hellBitsDirtyFilter) 805 | result.setIndex(index) 806 | 807 | return result 808 | } 809 | curField = path[curDepth] 810 | node = (node.nodes)[index] 811 | goto get 812 | } 813 | 814 | func (d *decoder) getNode() *Node { 815 | node := d.nodePool[d.nodeCount] 816 | d.nodeCount++ 817 | if d.nodeCount > len(d.nodePool)-16 { 818 | d.expandPool() 819 | } 820 | 821 | return node 822 | } 823 | 824 | func (n *Node) DigStrict(path ...string) (*StrictNode, error) { 825 | result := n.Dig(path...) 826 | if result == nil { 827 | return nil, ErrNotFound 828 | } 829 | 830 | return result.MutateToStrict(), nil 831 | } 832 | 833 | func (n *Node) AddField(name string) *Node { 834 | return n.AddFieldNoAlloc(nil, name) 835 | } 836 | 837 | func (n *Node) AddFieldNoAlloc(root *Root, name string) *Node { 838 | if n == nil || n.bits&hellBitObject != hellBitObject { 839 | return nil 840 | } 841 | 842 | node := n.Dig(name) 843 | if node != nil { 844 | return node 845 | } 846 | 847 | newNull := n.getNode(root) 848 | newNull.bits = hellBitNull 849 | newNull.parent = n 850 | 851 | newField := n.getNode(root) 852 | newField.bits = hellBitField 853 | newField.next = newNull 854 | newField.parent = n 855 | newField.data = name 856 | 857 | l := len(n.nodes) 858 | if l > 0 { 859 | lastVal := (n.nodes)[l-1] 860 | newNull.next = lastVal.next.next 861 | lastVal.next.next = newField 862 | } else { 863 | // restore lost end 864 | newEnd := n.getNode(root) 865 | newEnd.bits = hellBitEnd 866 | newEnd.next = n.next 867 | newEnd.parent = n 868 | newNull.next = newEnd 869 | } 870 | n.nodes = append(n.nodes, newField) 871 | 872 | if n.bits&hellBitUseMap == hellBitUseMap { 873 | (*n.fields)[name] = l 874 | } 875 | 876 | return newNull 877 | } 878 | 879 | func (n *Node) addElement(root *Root) *Node { 880 | if n == nil || n.bits&hellBitArray != hellBitArray { 881 | return nil 882 | } 883 | 884 | newNull := n.getNode(root) 885 | newNull.bits = hellBitNull 886 | newNull.parent = n 887 | 888 | l := len(n.nodes) 889 | if l > 0 { 890 | lastVal := (n.nodes)[l-1] 891 | newNull.next = lastVal.next 892 | lastVal.next = newNull 893 | } else { 894 | // restore lost end 895 | newEnd := n.getNode(root) 896 | newEnd.bits = hellBitArrayEnd 897 | newEnd.next = n.next 898 | newEnd.parent = n 899 | newNull.next = newEnd 900 | } 901 | n.nodes = append(n.nodes, newNull) 902 | 903 | return newNull 904 | } 905 | 906 | func (n *Node) AddElement() *Node { 907 | return n.addElement(nil) 908 | } 909 | 910 | func (n *Node) AddElementNoAlloc(root *Root) *Node { 911 | return n.addElement(root) 912 | } 913 | 914 | func (n *Node) InsertElement(pos int) *Node { 915 | if n == nil || n.bits&hellBitArray != hellBitArray { 916 | return nil 917 | } 918 | 919 | l := len(n.nodes) 920 | if pos < 0 || pos > l { 921 | return nil 922 | } 923 | 924 | newNull := n.getNode(nil) 925 | newNull.bits = hellBitNull 926 | newNull.parent = n 927 | 928 | if l == 0 { 929 | // restore lost end 930 | newEnd := n.getNode(nil) 931 | newEnd.bits = hellBitArrayEnd 932 | newEnd.next = n.next 933 | newEnd.parent = n 934 | newNull.next = newEnd 935 | } else { 936 | if pos != l { 937 | newNull.next = n.nodes[pos] 938 | } else { 939 | newNull.next = n.nodes[pos-1].next 940 | } 941 | } 942 | 943 | if pos > 0 { 944 | n.nodes[pos-1].next = newNull 945 | } 946 | 947 | leftPart := n.nodes[:pos] 948 | rightPart := n.nodes[pos:] 949 | 950 | n.nodes = make([]*Node, 0, 0) 951 | n.nodes = append(n.nodes, leftPart...) 952 | n.nodes = append(n.nodes, newNull) 953 | n.nodes = append(n.nodes, rightPart...) 954 | 955 | return newNull 956 | } 957 | 958 | // Suicide legendary insane suicide function 959 | func (n *Node) Suicide() { 960 | if n == nil { 961 | return 962 | } 963 | 964 | owner := n.parent 965 | 966 | // root is immortal, sorry 967 | if owner == nil { 968 | return 969 | } 970 | 971 | delIndex := n.actualizeIndex() 972 | // already deleted? 973 | if delIndex == -1 { 974 | return 975 | } 976 | 977 | // mark owner as dirty 978 | owner.bits += hellBitsDirtyStep 979 | 980 | switch owner.bits & hellBitTypeFilter { 981 | case hellBitObject: 982 | moveIndex := len(owner.nodes) - 1 983 | delField := owner.nodes[delIndex] 984 | if moveIndex == 0 { 985 | owner.nodes = owner.nodes[:0] 986 | 987 | if owner.bits&hellBitUseMap == hellBitUseMap { 988 | delete(*owner.fields, delField.data) 989 | } 990 | 991 | return 992 | } 993 | 994 | lastField := owner.nodes[moveIndex] 995 | owner.nodes[delIndex] = lastField 996 | 997 | if delIndex != 0 { 998 | owner.nodes[delIndex-1].next.next = lastField 999 | } 1000 | 1001 | owner.nodes[moveIndex-1].next.next = owner.nodes[moveIndex].next.next 1002 | if lastField != n.next { 1003 | lastField.next.next = n.next 1004 | } 1005 | 1006 | if owner.bits&hellBitUseMap == hellBitUseMap { 1007 | delete(*owner.fields, delField.data) 1008 | if delIndex != moveIndex { 1009 | (*owner.fields)[lastField.data] = delIndex 1010 | } 1011 | } 1012 | owner.nodes = owner.nodes[:len(owner.nodes)-1] 1013 | 1014 | case hellBitArray: 1015 | if delIndex != 0 { 1016 | owner.nodes[delIndex-1].next = n.next 1017 | } 1018 | owner.nodes = append(owner.nodes[:delIndex], owner.nodes[delIndex+1:]...) 1019 | default: 1020 | panic("insane json really goes outta its mind") 1021 | } 1022 | } 1023 | 1024 | func (n *Node) actualizeIndex() int { 1025 | owner := n.parent 1026 | if owner == nil { 1027 | return -1 1028 | } 1029 | 1030 | a := n.bits & hellBitsDirtyFilter 1031 | b := owner.bits & hellBitsDirtyFilter 1032 | 1033 | // if owner isn't dirty then nothing to do 1034 | if a != 0 && a == b { 1035 | return n.getIndex() 1036 | } 1037 | 1038 | index := n.findSelf() 1039 | n.setIndex(index) 1040 | n.bits = n.bits&hellBitsDirtyReset | (owner.bits & hellBitsDirtyFilter) 1041 | 1042 | return index 1043 | } 1044 | 1045 | func (n *Node) findSelf() int { 1046 | owner := n.parent 1047 | if owner == nil { 1048 | return -1 1049 | } 1050 | 1051 | index := -1 1052 | if owner.bits&hellBitArray == hellBitArray { 1053 | for i, node := range owner.nodes { 1054 | if node == n { 1055 | index = i 1056 | break 1057 | } 1058 | } 1059 | } else { 1060 | for i, node := range owner.nodes { 1061 | if node.next == n { 1062 | index = i 1063 | break 1064 | } 1065 | } 1066 | } 1067 | return index 1068 | } 1069 | 1070 | // ******************** // 1071 | // MUTATIONS // 1072 | // ******************** // 1073 | 1074 | func (n *Node) MergeWith(node *Node) *Node { 1075 | if n == nil || node == nil { 1076 | return n 1077 | } 1078 | if n.bits&hellBitObject != hellBitObject || node.bits&hellBitObject != hellBitObject { 1079 | return n 1080 | } 1081 | 1082 | for _, child := range node.nodes { 1083 | child.unescapeField() 1084 | childField := child.AsString() 1085 | x := n.AddField(childField) 1086 | x.MutateToNode(child.next) 1087 | } 1088 | 1089 | return n 1090 | } 1091 | 1092 | // MutateToNode it isn't safe function, if you create node cycle, encode() may freeze 1093 | func (n *Node) MutateToNode(node *Node) *Node { 1094 | if n == nil || node == nil { 1095 | return n 1096 | } 1097 | 1098 | n.bits = node.bits 1099 | n.data = node.data 1100 | if node.bits&hellBitObject == hellBitObject || node.bits&hellBitArray == hellBitArray { 1101 | n.bits &= hellBitsUseMapReset 1102 | n.nodes = append((n.nodes)[:0], node.nodes...) 1103 | for _, child := range node.nodes { 1104 | child.parent = n 1105 | if node.bits&hellBitObject == hellBitObject { 1106 | child.next.parent = n 1107 | } 1108 | } 1109 | } 1110 | 1111 | return n 1112 | } 1113 | 1114 | func (n *Node) MutateToJSON(root *Root, json string) *Node { 1115 | if n == nil { 1116 | return n 1117 | } 1118 | 1119 | node, err := root.decoder.decode(json, false) 1120 | if err != nil { 1121 | return n 1122 | } 1123 | 1124 | return n.MutateToNode(node) 1125 | } 1126 | 1127 | // MutateToField changes name of objects's field 1128 | // works only with Field nodes received by AsField()/AsFields() 1129 | // example: 1130 | // root, err := insaneJSON.DecodeString(`{"a":"a","b":"b"}`) 1131 | // root.AsField("a").MutateToField("new_name") 1132 | // root.Encode() will be {"new_name":"a","b":"b"} 1133 | func (n *Node) MutateToField(newFieldName string) *Node { 1134 | if n == nil || n.bits&hellBitField != hellBitField { 1135 | return n 1136 | } 1137 | 1138 | parent := n.parent 1139 | if parent.bits&hellBitUseMap == hellBitUseMap { 1140 | x := (*parent.fields)[n.data] 1141 | delete(*parent.fields, n.data) 1142 | (*parent.fields)[newFieldName] = x 1143 | } 1144 | 1145 | n.data = newFieldName 1146 | 1147 | return n 1148 | } 1149 | 1150 | func (n *Node) MutateToInt(value int) *Node { 1151 | if n == nil || n.bits&hellBitField == hellBitField { 1152 | return n 1153 | } 1154 | 1155 | n.bits = hellBitNumber 1156 | n.data = strconv.Itoa(value) 1157 | 1158 | return n 1159 | } 1160 | 1161 | func (n *Node) MutateToInt64(value int64) *Node { 1162 | if n == nil || n.bits&hellBitField == hellBitField { 1163 | return n 1164 | } 1165 | 1166 | n.bits = hellBitNumber 1167 | n.data = strconv.FormatInt(value, 10) 1168 | 1169 | return n 1170 | } 1171 | 1172 | func (n *Node) MutateToUint64(value uint64) *Node { 1173 | if n == nil || n.bits&hellBitField == hellBitField { 1174 | return n 1175 | } 1176 | 1177 | n.bits = hellBitNumber 1178 | n.data = strconv.FormatUint(value, 10) 1179 | 1180 | return n 1181 | } 1182 | 1183 | func (n *Node) MutateToFloat(value float64) *Node { 1184 | if n == nil || n.bits&hellBitField == hellBitField { 1185 | return n 1186 | } 1187 | 1188 | n.bits = hellBitNumber 1189 | n.data = strconv.FormatFloat(value, 'f', -1, 64) 1190 | 1191 | return n 1192 | } 1193 | 1194 | func (n *Node) MutateToBool(value bool) *Node { 1195 | if n == nil || n.bits&hellBitField == hellBitField { 1196 | return n 1197 | } 1198 | 1199 | if value { 1200 | n.bits = hellBitTrue 1201 | } else { 1202 | n.bits = hellBitFalse 1203 | } 1204 | 1205 | return n 1206 | } 1207 | 1208 | func (n *Node) MutateToNull() *Node { 1209 | if n == nil || n.bits&hellBitField == hellBitField { 1210 | return n 1211 | } 1212 | 1213 | n.bits = hellBitNull 1214 | 1215 | return n 1216 | } 1217 | 1218 | func (n *Node) MutateToString(value string) *Node { 1219 | if n == nil || n.bits&hellBitField == hellBitField { 1220 | return nil 1221 | } 1222 | 1223 | n.bits = hellBitString 1224 | n.data = value 1225 | 1226 | return n 1227 | } 1228 | 1229 | func (n *Node) MutateToEscapedString(value string) *Node { 1230 | if n == nil || n.bits&hellBitField == hellBitField { 1231 | return nil 1232 | } 1233 | 1234 | n.bits = hellBitEscapedString 1235 | n.data = value 1236 | 1237 | return n 1238 | } 1239 | 1240 | // MutateToBytes mutate to a string and use byte slice as value. It doesn't copy data, so modifications of a slice will change result JSON. 1241 | func (n *Node) MutateToBytes(value []byte) *Node { 1242 | if n == nil || n.bits&hellBitField == hellBitField { 1243 | return nil 1244 | } 1245 | 1246 | n.bits = hellBitString 1247 | n.data = toString(value) 1248 | 1249 | return n 1250 | } 1251 | 1252 | // MutateToBytes mutate to a string and use byte slice as value. It copies data, so modification of a slice won't change result JSON. 1253 | func (n *Node) MutateToBytesCopy(root *Root, value []byte) *Node { 1254 | if n == nil || n.bits&hellBitField == hellBitField { 1255 | return nil 1256 | } 1257 | 1258 | l := len(root.decoder.buf) 1259 | root.decoder.buf = append(root.decoder.buf, value...) 1260 | 1261 | n.bits = hellBitString 1262 | n.data = toString(root.decoder.buf[l:]) 1263 | 1264 | return n 1265 | } 1266 | 1267 | func (n *Node) MutateToObject() *Node { 1268 | if n == nil || n.bits&hellBitField == hellBitField { 1269 | return n 1270 | } 1271 | 1272 | n.bits = hellBitObject 1273 | n.nodes = n.nodes[:0] 1274 | 1275 | return n 1276 | } 1277 | 1278 | func (n *Node) MutateToArray() *Node { 1279 | if n == nil || n.bits&hellBitField == hellBitField { 1280 | return n 1281 | } 1282 | 1283 | n.bits = hellBitArray 1284 | n.nodes = n.nodes[:0] 1285 | 1286 | return n 1287 | } 1288 | 1289 | func (n *Node) MutateToStrict() *StrictNode { 1290 | return &StrictNode{n} 1291 | } 1292 | 1293 | func (n *Node) DigField(path ...string) *Node { 1294 | if n == nil || len(path) == 0 { 1295 | return nil 1296 | } 1297 | 1298 | node := n.Dig(path...) 1299 | if node == nil { 1300 | return nil 1301 | } 1302 | 1303 | return n.nodes[node.getIndex()] 1304 | } 1305 | 1306 | func (n *Node) AsFields() []*Node { 1307 | if n == nil { 1308 | return make([]*Node, 0, 0) 1309 | } 1310 | 1311 | if n.bits&hellBitObject != hellBitObject { 1312 | return (n.nodes)[:0] 1313 | } 1314 | 1315 | for _, node := range n.nodes { 1316 | if node.bits&hellBitEscapedField == hellBitEscapedField { 1317 | node.unescapeField() 1318 | } 1319 | } 1320 | 1321 | return n.nodes 1322 | } 1323 | 1324 | func (n *StrictNode) AsFields() ([]*Node, error) { 1325 | if n.bits&hellBitObject != hellBitObject { 1326 | return nil, ErrNotObject 1327 | } 1328 | 1329 | for _, node := range n.nodes { 1330 | if node.bits&hellBitEscapedField == hellBitEscapedField { 1331 | node.unescapeField() 1332 | } 1333 | } 1334 | 1335 | return n.nodes, nil 1336 | } 1337 | 1338 | func (n *Node) AsFieldValue() *Node { 1339 | if n == nil || n.bits&hellBitField != hellBitField { 1340 | return nil 1341 | } 1342 | 1343 | return n.next 1344 | } 1345 | 1346 | func (n *StrictNode) AsFieldValue() (*Node, error) { 1347 | if n == nil || n.bits&hellBitField != hellBitField { 1348 | return nil, ErrNotField 1349 | } 1350 | 1351 | return n.next, nil 1352 | } 1353 | 1354 | func (n *Node) AsArray() []*Node { 1355 | if n == nil { 1356 | return make([]*Node, 0, 0) 1357 | } 1358 | if n.bits&hellBitArray != hellBitArray { 1359 | return (n.nodes)[:0] 1360 | } 1361 | 1362 | return n.nodes 1363 | } 1364 | 1365 | func (n *StrictNode) AsArray() ([]*Node, error) { 1366 | if n == nil || n.bits&hellBitArray != hellBitArray { 1367 | return nil, ErrNotArray 1368 | } 1369 | 1370 | return n.nodes, nil 1371 | } 1372 | 1373 | func (n *Node) unescapeStr() { 1374 | value := n.data 1375 | n.data = unescapeStr(value[1 : len(value)-1]) 1376 | n.bits = hellBitString 1377 | } 1378 | 1379 | func (n *Node) unescapeField() { 1380 | if n.bits&hellBitField == hellBitField { 1381 | return 1382 | } 1383 | 1384 | value := n.data 1385 | i := strings.LastIndexByte(value, '"') 1386 | // todo: remove it 1387 | if i < 1 { 1388 | return 1389 | } 1390 | n.data = unescapeStr(value[1:i]) 1391 | n.bits = hellBitField 1392 | } 1393 | 1394 | func (n *Node) AsString() string { 1395 | if n == nil { 1396 | return "" 1397 | } 1398 | 1399 | switch n.bits & hellBitTypeFilter { 1400 | case hellBitString: 1401 | return n.data 1402 | case hellBitEscapedString: 1403 | n.unescapeStr() 1404 | return n.data 1405 | case hellBitNumber: 1406 | return n.data 1407 | case hellBitTrue: 1408 | return "true" 1409 | case hellBitFalse: 1410 | return "false" 1411 | case hellBitNull: 1412 | return "null" 1413 | case hellBitField: 1414 | return n.data 1415 | case hellBitEscapedField: 1416 | panic("insane json really goes outta its mind") 1417 | default: 1418 | return "" 1419 | } 1420 | } 1421 | 1422 | func (n *Node) AsBytes() []byte { 1423 | return toByte(n.AsString()) 1424 | } 1425 | 1426 | func (n *StrictNode) AsBytes() ([]byte, error) { 1427 | s, err := n.AsString() 1428 | if err != nil { 1429 | return nil, err 1430 | } 1431 | 1432 | return toByte(s), nil 1433 | } 1434 | 1435 | func (n *StrictNode) AsString() (string, error) { 1436 | if n.bits&hellBitEscapedField == hellBitEscapedField { 1437 | panic("insane json really goes outta its mind") 1438 | } 1439 | 1440 | if n.bits&hellBitEscapedString == hellBitEscapedString { 1441 | n.unescapeStr() 1442 | } 1443 | 1444 | if n == nil || n.bits&hellBitString != hellBitString { 1445 | return "", ErrNotString 1446 | } 1447 | 1448 | return n.data, nil 1449 | } 1450 | 1451 | func (n *Node) AsEscapedString() string { 1452 | if n == nil { 1453 | return "" 1454 | } 1455 | 1456 | switch n.bits & hellBitTypeFilter { 1457 | case hellBitString: 1458 | return toString(escapeString(make([]byte, 0, len(n.data)), n.data)) 1459 | case hellBitEscapedString: 1460 | return n.data 1461 | case hellBitNumber: 1462 | return n.data 1463 | case hellBitTrue: 1464 | return "true" 1465 | case hellBitFalse: 1466 | return "false" 1467 | case hellBitNull: 1468 | return "null" 1469 | case hellBitField: 1470 | return n.data 1471 | case hellBitEscapedField: 1472 | panic("insane json really goes outta its mind") 1473 | default: 1474 | return "" 1475 | } 1476 | } 1477 | 1478 | func (n *Node) AppendEscapedString(out []byte) []byte { 1479 | if n == nil { 1480 | return out 1481 | } 1482 | 1483 | switch n.bits & hellBitTypeFilter { 1484 | case hellBitString: 1485 | return escapeString(out, n.data) 1486 | case hellBitEscapedString, hellBitNumber, hellBitField: 1487 | return append(out, n.data...) 1488 | case hellBitTrue: 1489 | return append(out, "true"...) 1490 | case hellBitFalse: 1491 | return append(out, "false"...) 1492 | case hellBitNull: 1493 | return append(out, "null"...) 1494 | case hellBitEscapedField: 1495 | panic("insane json really goes outta its mind") 1496 | default: 1497 | return out 1498 | } 1499 | } 1500 | 1501 | func (n *StrictNode) AsEscapedString() (string, error) { 1502 | if n.bits&hellBitEscapedField == hellBitEscapedField { 1503 | panic("insane json really goes outta its mind") 1504 | } 1505 | 1506 | if n == nil || n.bits&hellBitString != hellBitString { 1507 | return "", ErrNotString 1508 | } 1509 | 1510 | if n.bits&hellBitEscapedString == hellBitEscapedString { 1511 | return n.data, nil 1512 | } 1513 | 1514 | return toString(escapeString(make([]byte, 0, len(n.data)), n.data)), nil 1515 | } 1516 | 1517 | func (n *Node) AsBool() bool { 1518 | if n == nil { 1519 | return false 1520 | } 1521 | 1522 | switch n.bits & hellBitTypeFilter { 1523 | case hellBitString: 1524 | return n.data == "true" 1525 | case hellBitEscapedString: 1526 | n.unescapeStr() 1527 | return n.data == "true" 1528 | case hellBitNumber: 1529 | return n.data != "0" 1530 | case hellBitTrue: 1531 | return true 1532 | case hellBitFalse: 1533 | return false 1534 | case hellBitNull: 1535 | return false 1536 | case hellBitField: 1537 | return n.data == "true" 1538 | case hellBitEscapedField: 1539 | panic("insane json really goes outta its mind") 1540 | default: 1541 | return false 1542 | } 1543 | } 1544 | 1545 | func (n *StrictNode) AsBool() (bool, error) { 1546 | if n == nil || (n.bits&hellBitTrue != hellBitTrue && n.bits&hellBitFalse != hellBitFalse) { 1547 | return false, ErrNotBool 1548 | } 1549 | 1550 | return n.bits&hellBitTrue == hellBitTrue, nil 1551 | } 1552 | 1553 | func (n *Node) AsInt() int { 1554 | if n == nil { 1555 | return 0 1556 | } 1557 | 1558 | if n.bits&hellBitTypeFilter == hellBitEscapedString { 1559 | n.unescapeStr() 1560 | } 1561 | 1562 | switch n.bits & hellBitTypeFilter { 1563 | case hellBitString: 1564 | fallthrough 1565 | case hellBitField: 1566 | fallthrough 1567 | case hellBitNumber: 1568 | if strings.IndexAny(n.data, ".eE") != -1 { 1569 | return int(math.Round(decodeFloat64(n.data))) 1570 | } else { 1571 | return int(decodeInt64(n.data)) 1572 | } 1573 | case hellBitTrue: 1574 | return 1 1575 | case hellBitFalse: 1576 | return 0 1577 | case hellBitNull: 1578 | return 0 1579 | case hellBitEscapedField: 1580 | panic("insane json really goes outta its mind") 1581 | default: 1582 | return 0 1583 | } 1584 | } 1585 | 1586 | func (n *StrictNode) AsInt() (int, error) { 1587 | if n == nil || n.bits&hellBitNumber != hellBitNumber { 1588 | return 0, ErrNotNumber 1589 | } 1590 | num := decodeInt64(n.data) 1591 | if num == 0 && n.data != "0" { 1592 | return 0, ErrNotNumber 1593 | } 1594 | return int(num), nil 1595 | } 1596 | 1597 | func (n *Node) AsUint64() uint64 { 1598 | if n == nil { 1599 | return 0 1600 | } 1601 | 1602 | if n.bits&hellBitTypeFilter == hellBitEscapedString { 1603 | n.unescapeStr() 1604 | } 1605 | 1606 | switch n.bits & hellBitTypeFilter { 1607 | case hellBitString: 1608 | fallthrough 1609 | case hellBitField: 1610 | fallthrough 1611 | case hellBitNumber: 1612 | if strings.IndexByte(n.data, '.') != -1 { 1613 | return uint64(math.Round(decodeFloat64(n.data))) 1614 | } else { 1615 | return decodeUint64(n.data) 1616 | } 1617 | case hellBitTrue: 1618 | return 1 1619 | case hellBitFalse: 1620 | return 0 1621 | case hellBitNull: 1622 | return 0 1623 | case hellBitEscapedField: 1624 | panic("insane json really goes outta its mind") 1625 | default: 1626 | return 0 1627 | } 1628 | } 1629 | 1630 | func (n *StrictNode) AsUint64() (uint64, error) { 1631 | if n == nil || n.bits&hellBitNumber != hellBitNumber { 1632 | return 0, ErrNotNumber 1633 | } 1634 | num := decodeUint64(n.data) 1635 | if num == 0 && n.data != "0" { 1636 | return 0, ErrNotNumber 1637 | } 1638 | return num, nil 1639 | } 1640 | 1641 | func (n *Node) AsInt64() int64 { 1642 | if n == nil { 1643 | return 0 1644 | } 1645 | 1646 | if n.bits&hellBitTypeFilter == hellBitEscapedString { 1647 | n.unescapeStr() 1648 | } 1649 | 1650 | switch n.bits & hellBitTypeFilter { 1651 | case hellBitString: 1652 | fallthrough 1653 | case hellBitField: 1654 | fallthrough 1655 | case hellBitNumber: 1656 | if strings.IndexByte(n.data, '.') != -1 { 1657 | return int64(math.Round(decodeFloat64(n.data))) 1658 | } else { 1659 | return decodeInt64(n.data) 1660 | } 1661 | case hellBitTrue: 1662 | return 1 1663 | case hellBitFalse: 1664 | return 0 1665 | case hellBitNull: 1666 | return 0 1667 | case hellBitEscapedField: 1668 | panic("insane json really goes outta its mind") 1669 | default: 1670 | return 0 1671 | } 1672 | } 1673 | 1674 | func (n *StrictNode) AsInt64() (int64, error) { 1675 | if n == nil || n.bits&hellBitNumber != hellBitNumber { 1676 | return 0, ErrNotNumber 1677 | } 1678 | num := decodeInt64(n.data) 1679 | if num == 0 && n.data != "0" { 1680 | return 0, ErrNotNumber 1681 | } 1682 | return num, nil 1683 | } 1684 | 1685 | func (n *Node) AsFloat() float64 { 1686 | switch n.bits & hellBitTypeFilter { 1687 | case hellBitString: 1688 | return decodeFloat64(n.data) 1689 | case hellBitEscapedString: 1690 | n.unescapeStr() 1691 | return decodeFloat64(n.data) 1692 | case hellBitNumber: 1693 | return decodeFloat64(n.data) 1694 | case hellBitTrue: 1695 | return 1 1696 | case hellBitFalse: 1697 | return 0 1698 | case hellBitNull: 1699 | return 0 1700 | case hellBitField: 1701 | return decodeFloat64(n.data) 1702 | case hellBitEscapedField: 1703 | panic("insane json really goes outta its mind") 1704 | default: 1705 | return 0 1706 | } 1707 | } 1708 | 1709 | func (n *StrictNode) AsFloat() (float64, error) { 1710 | if n == nil || n.bits&hellBitNumber != hellBitNumber { 1711 | return 0, ErrNotNumber 1712 | } 1713 | 1714 | return decodeFloat64(n.data), nil 1715 | } 1716 | 1717 | func (n *Node) IsObject() bool { 1718 | return n != nil && n.bits&hellBitObject == hellBitObject 1719 | } 1720 | 1721 | func (n *Node) IsArray() bool { 1722 | return n != nil && n.bits&hellBitArray == hellBitArray 1723 | } 1724 | 1725 | func (n *Node) IsNumber() bool { 1726 | return n != nil && n.bits&hellBitNumber == hellBitNumber 1727 | } 1728 | 1729 | func (n *Node) IsString() bool { 1730 | return n != nil && (n.bits&hellBitString == hellBitString || n.bits&hellBitEscapedString == hellBitEscapedString) 1731 | } 1732 | 1733 | func (n *Node) IsTrue() bool { 1734 | return n != nil && n.bits&hellBitTrue == hellBitTrue 1735 | } 1736 | 1737 | func (n *Node) IsFalse() bool { 1738 | return n != nil && n.bits&hellBitFalse == hellBitFalse 1739 | } 1740 | 1741 | func (n *Node) IsNull() bool { 1742 | return n != nil && n.bits&hellBitNull == hellBitNull 1743 | } 1744 | 1745 | func (n *Node) IsField() bool { 1746 | return n != nil && n.bits&hellBitField == hellBitField 1747 | } 1748 | 1749 | func (n *Node) IsNil() bool { 1750 | return n == nil 1751 | } 1752 | 1753 | func (n *Node) TypeStr() string { 1754 | if n == nil { 1755 | return "nil" 1756 | } 1757 | 1758 | switch n.bits & hellBitTypeFilter { 1759 | case hellBitObject: 1760 | return "hellBitObject" 1761 | case hellBitEnd: 1762 | return "hellBitObject end" 1763 | case hellBitArray: 1764 | return "hellBitArray" 1765 | case hellBitArrayEnd: 1766 | return "hellBitArray end" 1767 | case hellBitField: 1768 | return "field" 1769 | case hellBitEscapedField: 1770 | return "field escaped" 1771 | case hellBitNull: 1772 | return "null" 1773 | case hellBitString: 1774 | return "string" 1775 | case hellBitEscapedString: 1776 | return "string escaped" 1777 | case hellBitNumber: 1778 | return "number" 1779 | case hellBitTrue: 1780 | return "true" 1781 | case hellBitFalse: 1782 | return "false" 1783 | default: 1784 | return "unknown" 1785 | } 1786 | } 1787 | 1788 | func (n *Node) getNode(root *Root) *Node { 1789 | if root == nil { 1790 | return &Node{} 1791 | } else { 1792 | return root.decoder.getNode() 1793 | } 1794 | } 1795 | 1796 | func (n *Node) setIndex(index int) { 1797 | n.bits = (n.bits & hellBitsIndexReset) + hellBits(index)*hellBitsIndexStep 1798 | } 1799 | 1800 | func (n *Node) getIndex() int { 1801 | return int((n.bits & hellBitsIndexFilter) / hellBitsIndexStep) 1802 | } 1803 | 1804 | // ******************** // 1805 | // DECODER // 1806 | // ******************** // 1807 | 1808 | func (d *decoder) initPool() { 1809 | d.nodePool = make([]*Node, StartNodePoolSize, StartNodePoolSize) 1810 | for i := 0; i < StartNodePoolSize; i++ { 1811 | d.nodePool[i] = &Node{} 1812 | } 1813 | } 1814 | 1815 | func (d *decoder) expandPool() []*Node { 1816 | c := cap(d.nodePool) 1817 | for i := 0; i < c; i++ { 1818 | d.nodePool = append(d.nodePool, &Node{}) 1819 | } 1820 | 1821 | return d.nodePool 1822 | } 1823 | 1824 | func getFromPool() *decoder { 1825 | return decoderPool.Get().(*decoder) 1826 | } 1827 | 1828 | func backToPool(d *decoder) { 1829 | decoderPool.Put(d) 1830 | } 1831 | 1832 | func Spawn() *Root { 1833 | root, _ := getFromPool().decodeHeadless("{}", true) 1834 | return root 1835 | } 1836 | 1837 | func DecodeBytes(jsonBytes []byte) (*Root, error) { 1838 | return Spawn().decoder.decodeHeadless(toString(jsonBytes), true) 1839 | } 1840 | 1841 | func DecodeString(json string) (*Root, error) { 1842 | return Spawn().decoder.decodeHeadless(json, true) 1843 | } 1844 | 1845 | func DecodeFile(fileName string) (*Root, error) { 1846 | bytes, err := ioutil.ReadFile(fileName) 1847 | if err != nil { 1848 | return nil, err 1849 | } 1850 | return DecodeBytes(bytes) 1851 | } 1852 | 1853 | // Clear makes Root empty object 1854 | func (r *Root) Clear() { 1855 | if r == nil { 1856 | return 1857 | } 1858 | 1859 | _ = r.DecodeString("{}") 1860 | } 1861 | 1862 | // DecodeBytes clears Root and decodes new JSON. Useful for reusing Root to reduce allocations. 1863 | func (r *Root) DecodeBytes(jsonBytes []byte) error { 1864 | if r == nil { 1865 | return ErrRootIsNil 1866 | } 1867 | _, err := r.decoder.decodeHeadless(toString(jsonBytes), false) 1868 | 1869 | return err 1870 | } 1871 | 1872 | // DecodeString clears Root and decodes new JSON. Useful for reusing Root to reduce allocations. 1873 | func (r *Root) DecodeString(json string) error { 1874 | if r == nil { 1875 | return ErrRootIsNil 1876 | } 1877 | _, err := r.decoder.decodeHeadless(json, false) 1878 | 1879 | return err 1880 | } 1881 | 1882 | // DecodeFile clears Root and decodes new JSON. Useful for reusing Root to reduce allocations. 1883 | func (r *Root) DecodeFile(fileName string) error { 1884 | if r == nil { 1885 | return ErrRootIsNil 1886 | } 1887 | 1888 | bytes, err := ioutil.ReadFile(fileName) 1889 | if err != nil { 1890 | return err 1891 | } 1892 | 1893 | _, err = r.decoder.decodeHeadless(toString(bytes), false) 1894 | 1895 | return err 1896 | } 1897 | 1898 | // DecodeBytesAdditional doesn't clean Root, uses Root node pool to decode JSON 1899 | func (r *Root) DecodeBytesAdditional(jsonBytes []byte) (*Node, error) { 1900 | if r == nil { 1901 | return nil, ErrRootIsNil 1902 | } 1903 | 1904 | return r.decoder.decode(toString(jsonBytes), false) 1905 | } 1906 | 1907 | // DecodeStringAdditional doesn't clean Root, uses Root node pool to decode JSON 1908 | func (r *Root) DecodeStringAdditional(json string) (*Node, error) { 1909 | if r == nil { 1910 | return nil, ErrRootIsNil 1911 | } 1912 | 1913 | return r.decoder.decode(json, false) 1914 | } 1915 | 1916 | func Release(root *Root) { 1917 | if root == nil { 1918 | return 1919 | } 1920 | 1921 | backToPool(root.decoder) 1922 | } 1923 | 1924 | func toString(b []byte) string { 1925 | return *(*string)(unsafe.Pointer(&b)) 1926 | } 1927 | 1928 | func toByte(s string) []byte { 1929 | header := (*reflect.StringHeader)(unsafe.Pointer(&s)) 1930 | slice := reflect.SliceHeader{ 1931 | Data: header.Data, 1932 | Len: header.Len, 1933 | Cap: header.Len, 1934 | } 1935 | 1936 | return *(*[]byte)(unsafe.Pointer(&slice)) 1937 | } 1938 | 1939 | // this code copied from really cool and fast https://github.com/valyala/fastjson 1940 | func unescapeStr(s string) string { 1941 | n := strings.IndexByte(s, '\\') 1942 | if n < 0 { 1943 | return s 1944 | } 1945 | 1946 | b := toByte(s) 1947 | b = b[:n] 1948 | s = s[n+1:] 1949 | for len(s) > 0 { 1950 | ch := s[0] 1951 | s = s[1:] 1952 | switch ch { 1953 | case '"': 1954 | b = append(b, '"') 1955 | case '\\': 1956 | b = append(b, '\\') 1957 | case '/': 1958 | b = append(b, '/') 1959 | case 'b': 1960 | b = append(b, '\b') 1961 | case 'f': 1962 | b = append(b, '\f') 1963 | case 'n': 1964 | b = append(b, '\n') 1965 | case 'r': 1966 | b = append(b, '\r') 1967 | case 't': 1968 | b = append(b, '\t') 1969 | case 'u': 1970 | if len(s) < 4 { 1971 | b = append(b, "\\u"...) 1972 | break 1973 | } 1974 | xs := s[:4] 1975 | x, err := strconv.ParseUint(xs, 16, 16) 1976 | if err != nil { 1977 | b = append(b, "\\u"...) 1978 | break 1979 | } 1980 | s = s[4:] 1981 | if !utf16.IsSurrogate(rune(x)) { 1982 | b = append(b, string(rune(x))...) 1983 | break 1984 | } 1985 | 1986 | if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { 1987 | b = append(b, "\\u"...) 1988 | b = append(b, xs...) 1989 | break 1990 | } 1991 | x1, err := strconv.ParseUint(s[2:6], 16, 16) 1992 | if err != nil { 1993 | b = append(b, "\\u"...) 1994 | b = append(b, xs...) 1995 | break 1996 | } 1997 | r := utf16.DecodeRune(rune(x), rune(x1)) 1998 | b = append(b, string(r)...) 1999 | s = s[6:] 2000 | default: 2001 | b = append(b, '\\', ch) 2002 | } 2003 | n = strings.IndexByte(s, '\\') 2004 | if n < 0 { 2005 | b = append(b, s...) 2006 | break 2007 | } 2008 | b = append(b, s[:n]...) 2009 | s = s[n+1:] 2010 | } 2011 | return toString(b) 2012 | } 2013 | 2014 | func escapeString(out []byte, st string) []byte { 2015 | if !shouldEscape(st) { 2016 | out = append(out, '"') 2017 | out = append(out, st...) 2018 | out = append(out, '"') 2019 | return out 2020 | } 2021 | 2022 | out = append(out, '"') 2023 | s := toByte(st) 2024 | start := 0 2025 | for i := 0; i < len(s); { 2026 | if b := s[i]; b < utf8.RuneSelf { 2027 | if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' { 2028 | i++ 2029 | continue 2030 | } 2031 | if start < i { 2032 | out = append(out, s[start:i]...) 2033 | } 2034 | switch b { 2035 | case '\\', '"': 2036 | out = append(out, '\\') 2037 | out = append(out, b) 2038 | case '\n': 2039 | out = append(out, "\\n"...) 2040 | case '\r': 2041 | out = append(out, "\\r"...) 2042 | case '\t': 2043 | out = append(out, "\\t"...) 2044 | default: 2045 | out = append(out, "\\u00"...) 2046 | out = append(out, hex[b>>4]) 2047 | out = append(out, hex[b&0xf]) 2048 | } 2049 | i++ 2050 | start = i 2051 | continue 2052 | } 2053 | 2054 | c, size := utf8.DecodeRune(s[i:]) 2055 | if c == utf8.RuneError && size == 1 { 2056 | if start < i { 2057 | out = append(out, s[start:i]...) 2058 | } 2059 | out = append(out, "\\ufffd"...) 2060 | i += size 2061 | start = i 2062 | continue 2063 | } 2064 | 2065 | if c == '\u2028' || c == '\u2029' { 2066 | if start < i { 2067 | out = append(out, s[start:i]...) 2068 | } 2069 | out = append(out, "\\u202"...) 2070 | out = append(out, hex[c&0xF]) 2071 | i += size 2072 | start = i 2073 | continue 2074 | } 2075 | i += size 2076 | } 2077 | if start < len(s) { 2078 | out = append(out, s[start:]...) 2079 | } 2080 | out = append(out, '"') 2081 | 2082 | return out 2083 | } 2084 | 2085 | func shouldEscape(s string) bool { 2086 | if strings.IndexByte(s, '"') >= 0 || strings.IndexByte(s, '\\') >= 0 { 2087 | return true 2088 | } 2089 | 2090 | l := len(s) 2091 | for i := 0; i < l; i++ { 2092 | if s[i] < 0x20 { 2093 | return true 2094 | } 2095 | } 2096 | 2097 | return false 2098 | } 2099 | 2100 | func decodeInt64(s string) int64 { 2101 | l := len(s) 2102 | if l == 0 { 2103 | return 0 2104 | } 2105 | 2106 | o := 0 2107 | m := s[0] == '-' 2108 | if m { 2109 | s = s[1:] 2110 | l-- 2111 | } 2112 | 2113 | num := int64(0) 2114 | for o < l { 2115 | c := uint(s[o] - '0') 2116 | if c > 9 { 2117 | return 0 2118 | } 2119 | 2120 | num = num*10 + int64(c) 2121 | o++ 2122 | if o <= 18 { 2123 | continue 2124 | } 2125 | 2126 | x, err := strconv.ParseInt(s, 10, 64) 2127 | if err != nil { 2128 | return 0 2129 | } 2130 | num = x 2131 | break 2132 | } 2133 | 2134 | if m { 2135 | return -num 2136 | } else { 2137 | return num 2138 | } 2139 | } 2140 | 2141 | func decodeUint64(s string) uint64 { 2142 | l := len(s) 2143 | if l == 0 { 2144 | return 0 2145 | } 2146 | 2147 | o := 0 2148 | m := s[0] == '-' 2149 | if m { 2150 | s = s[1:] 2151 | l-- 2152 | } 2153 | 2154 | num := uint64(0) 2155 | for o < l { 2156 | c := uint(s[o] - '0') 2157 | if c > 9 { 2158 | return 0 2159 | } 2160 | 2161 | num = num*10 + uint64(c) 2162 | o++ 2163 | if o <= 18 { 2164 | continue 2165 | } 2166 | 2167 | x, err := strconv.ParseUint(s, 10, 64) 2168 | if err != nil { 2169 | return 0 2170 | } 2171 | num = x 2172 | break 2173 | } 2174 | 2175 | if m { 2176 | return -num 2177 | } else { 2178 | return num 2179 | } 2180 | } 2181 | 2182 | // this code copied from really cool and fast https://github.com/valyala/fastjson 2183 | func decodeFloat64(s string) float64 { 2184 | if len(s) == 0 { 2185 | return 0 2186 | } 2187 | i := uint(0) 2188 | minus := s[0] == '-' 2189 | if minus { 2190 | i++ 2191 | if i >= uint(len(s)) { 2192 | return 0 2193 | } 2194 | } 2195 | 2196 | d := uint64(0) 2197 | j := i 2198 | for i < uint(len(s)) { 2199 | if s[i] >= '0' && s[i] <= '9' { 2200 | d = d*10 + uint64(s[i]-'0') 2201 | i++ 2202 | if i > 18 { 2203 | // The integer part may be out of range for uint64. 2204 | // Fall back to slow parsing. 2205 | f, err := strconv.ParseFloat(s, 64) 2206 | if err != nil && !math.IsInf(f, 0) { 2207 | return 0 2208 | } 2209 | return f 2210 | } 2211 | continue 2212 | } 2213 | break 2214 | } 2215 | if i <= j { 2216 | return 0 2217 | } 2218 | f := float64(d) 2219 | if i >= uint(len(s)) { 2220 | // Fast path - just integer. 2221 | if minus { 2222 | f = -f 2223 | } 2224 | return f 2225 | } 2226 | 2227 | if s[i] == '.' { 2228 | // Parse fractional part. 2229 | i++ 2230 | if i >= uint(len(s)) { 2231 | return 0 2232 | } 2233 | fr := uint64(0) 2234 | j := i 2235 | for i < uint(len(s)) { 2236 | if s[i] >= '0' && s[i] <= '9' { 2237 | fr = fr*10 + uint64(s[i]-'0') 2238 | i++ 2239 | if i-j > 18 { 2240 | // The fractional part may be out of range for uint64. 2241 | // Fall back to standard parsing. 2242 | f, err := strconv.ParseFloat(s, 64) 2243 | if err != nil && !math.IsInf(f, 0) { 2244 | return 0 2245 | } 2246 | return f 2247 | } 2248 | continue 2249 | } 2250 | break 2251 | } 2252 | if i <= j { 2253 | return 0 2254 | } 2255 | f += float64(fr) / math.Pow10(int(i-j)) 2256 | if i >= uint(len(s)) { 2257 | // Fast path - parsed fractional number. 2258 | if minus { 2259 | f = -f 2260 | } 2261 | return f 2262 | } 2263 | } 2264 | if s[i] == 'e' || s[i] == 'E' { 2265 | // Parse exponent part. 2266 | i++ 2267 | if i >= uint(len(s)) { 2268 | return 0 2269 | } 2270 | expMinus := false 2271 | if s[i] == '+' || s[i] == '-' { 2272 | expMinus = s[i] == '-' 2273 | i++ 2274 | if i >= uint(len(s)) { 2275 | return 0 2276 | } 2277 | } 2278 | exp := int16(0) 2279 | j := i 2280 | for i < uint(len(s)) { 2281 | if s[i] >= '0' && s[i] <= '9' { 2282 | exp = exp*10 + int16(s[i]-'0') 2283 | i++ 2284 | if exp > 300 { 2285 | // The exponent may be too big for float64. 2286 | // Fall back to standard parsing. 2287 | f, err := strconv.ParseFloat(s, 64) 2288 | if err != nil && !math.IsInf(f, 0) { 2289 | return 0 2290 | } 2291 | return f 2292 | } 2293 | continue 2294 | } 2295 | break 2296 | } 2297 | if i <= j { 2298 | return 0 2299 | } 2300 | if expMinus { 2301 | exp = -exp 2302 | } 2303 | f *= math.Pow10(int(exp)) 2304 | if i >= uint(len(s)) { 2305 | if minus { 2306 | f = -f 2307 | } 2308 | return f 2309 | } 2310 | } 2311 | return 0 2312 | } 2313 | 2314 | var fuzzRoot *Root = nil 2315 | 2316 | func Fuzz(data []byte) int { 2317 | if fuzzRoot == nil { 2318 | fuzzRoot = Spawn() 2319 | } 2320 | err := fuzzRoot.DecodeBytes(data) 2321 | if err != nil { 2322 | return -1 2323 | } 2324 | 2325 | fields := []string{ 2326 | "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", 2327 | "11", "21", "31", "41", "51", "61", "71", "81", "91", "101", 2328 | "111", "211", "311", "411", "511", "611", "711", "811", "911", "1011", 2329 | } 2330 | jsons := []string{ 2331 | "1", "2", "3", "4", "5", 2332 | `{"a":"b","c":"d"}`, 2333 | `{"5":"5","l":[3,4]}`, 2334 | `{"a":{"5":"5","l":[3,4]},"c":"d"}`, 2335 | `{"a":"b","c":"d"}`, 2336 | `{"5":"5","l":[3,4]}`, 2337 | `{"a":"b","c":{"5":"5","l":[3,4]}}`, 2338 | `{"a":{"somekey":"someval", "xxx":"yyy"},"c":"d"}`, 2339 | `{"5":"5","l":[3,4]}`, 2340 | `["a","b","c","d"]`, 2341 | `{"5":"5","l":[3,4]}`, 2342 | `[{"5":"5","l":[3,4]},"b","c","d"]`, 2343 | `["a","b","c","d"]`, 2344 | `{"5":"5","l":[3,4]}`, 2345 | `["a","b","c",{"5":"5","l":[3,4]}]`, 2346 | } 2347 | 2348 | node := fuzzRoot.Node 2349 | 2350 | for i := 0; i < 1; i++ { 2351 | for j := 0; j < 100; j++ { 2352 | if node.IsObject() { 2353 | fields := node.AsFields() 2354 | if len(fields) == 0 { 2355 | break 2356 | } 2357 | node = node.Dig(fields[rand.Int()%len(fields)].AsString()) 2358 | continue 2359 | } 2360 | if node.IsArray() { 2361 | fields := node.AsArray() 2362 | if len(fields) == 0 { 2363 | break 2364 | } 2365 | node = node.Dig(strconv.Itoa(rand.Int() % len(fields))) 2366 | continue 2367 | } 2368 | 2369 | node.AddField(fields[rand.Int()%len(fields)]).MutateToJSON(fuzzRoot, jsons[rand.Int()%len(jsons)]) 2370 | break 2371 | } 2372 | for j := 0; j < 200; j++ { 2373 | if node.IsObject() { 2374 | fields := node.AsFields() 2375 | if len(fields) == 0 { 2376 | break 2377 | } 2378 | node = node.Dig(fields[rand.Int()%len(fields)].AsString()) 2379 | continue 2380 | } 2381 | if node.IsArray() { 2382 | fields := node.AsArray() 2383 | if len(fields) == 0 { 2384 | break 2385 | } 2386 | node = node.Dig(strconv.Itoa(rand.Int() % len(fields))) 2387 | continue 2388 | } 2389 | 2390 | node.Suicide() 2391 | node = fuzzRoot.Node 2392 | break 2393 | } 2394 | } 2395 | 2396 | fuzzRoot.EncodeToString() 2397 | 2398 | return 1 2399 | } 2400 | 2401 | func insaneErr(err error, json string, offset int) error { 2402 | if DisableBeautifulErrors { 2403 | return err 2404 | } 2405 | 2406 | a := offset - 20 2407 | b := offset + 20 2408 | if a < 0 { 2409 | a = 0 2410 | } 2411 | if b > len(json)-1 { 2412 | b = len(json) - 1 2413 | if b < 0 { 2414 | b = 0 2415 | } 2416 | } 2417 | 2418 | pointer := "" 2419 | for x := 0; x < offset-a+len(err.Error())+len(" near "); x++ { 2420 | pointer += " " 2421 | } 2422 | pointer += "^" 2423 | str := "" 2424 | if a != b { 2425 | str = strings.ReplaceAll(string(json[a:b]), "\n", " ") 2426 | } 2427 | str = strings.ReplaceAll(str, "\r", " ") 2428 | str = strings.ReplaceAll(str, "\t", " ") 2429 | 2430 | return errors.New(fmt.Sprintf("%s near `%s`\n%s", err.Error(), str, pointer)) 2431 | } 2432 | -------------------------------------------------------------------------------- /insane_perf_test.go: -------------------------------------------------------------------------------- 1 | package insaneJSON 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io/ioutil" 7 | "math/rand" 8 | "os" 9 | "testing" 10 | ) 11 | 12 | type workload struct { 13 | json []byte 14 | name string 15 | 16 | requests [][]string 17 | } 18 | 19 | func getStableWorkload() ([]*workload, int64) { 20 | workloads := make([]*workload, 0, 0) 21 | workloads = append(workloads, loadJSON("light-ws", [][]string{ 22 | {"_id"}, 23 | {"favoriteFruit"}, 24 | {"about"}, 25 | })) 26 | workloads = append(workloads, loadJSON("many-objects", [][]string{ 27 | {"deeper", "deeper", "deeper", "deeper", "deeper", "deeper", "deeper", "deeper", "deeper", "deeper", "deeper", "deeper", "deeper"}, 28 | })) 29 | workloads = append(workloads, loadJSON("heavy", [][]string{ 30 | {"first", "second", "third", "fourth", "fifth"}, 31 | })) 32 | workloads = append(workloads, loadJSON("many-fields", [][]string{ 33 | {"first"}, 34 | {"middle"}, 35 | {"last"}, 36 | })) 37 | workloads = append(workloads, loadJSON("few-fields", [][]string{ 38 | {"first"}, 39 | {"middle"}, 40 | {"last"}, 41 | })) 42 | workloads = append(workloads, loadJSON("insane", [][]string{ 43 | {"statuses", "2", "user", "entities", "url", "urls", "0", "expanded_url"}, 44 | {"statuses", "36", "retweeted_status", "user", "profile", "sidebar", "fill", "color"}, 45 | {"statuses", "75", "entities", "user_mentions", "0", "screen_name"}, 46 | {"statuses", "99", "coordinates"}, 47 | })) 48 | 49 | size := 0 50 | for _, workload := range workloads { 51 | size += len(workload.json) 52 | } 53 | 54 | return workloads, int64(size) 55 | } 56 | 57 | func loadJSON(name string, requests [][]string) *workload { 58 | content, err := ioutil.ReadFile(fmt.Sprintf("benchdata/%s.json", name)) 59 | if err != nil { 60 | panic(err.Error()) 61 | } 62 | 63 | return &workload{json: content, name: name, requests: requests} 64 | } 65 | 66 | func getChaoticWorkload() ([][]byte, [][][]string, int64) { 67 | lines := make([][]byte, 0, 0) 68 | requests := make([][][]string, 0, 0) 69 | file, err := os.Open("./benchdata/chaotic-workload.log") 70 | if err != nil { 71 | panic(err.Error()) 72 | } 73 | defer func() { 74 | _ = file.Close() 75 | }() 76 | scanner := bufio.NewScanner(file) 77 | for scanner.Scan() { 78 | bytes := []byte(scanner.Text()) 79 | lines = append(lines, bytes) 80 | root, err := DecodeBytes(bytes) 81 | if err != nil { 82 | panic(err.Error()) 83 | } 84 | 85 | requestList := make([][]string, 0, 0) 86 | requestCount := rand.Int() % 3 87 | for x := 0; x < requestCount; x++ { 88 | node := root.Node 89 | selector := make([]string, 0, 0) 90 | for { 91 | if !node.IsObject() { 92 | break 93 | } 94 | 95 | fields := node.AsFields() 96 | name := fields[rand.Int()%len(fields)].AsString() 97 | selector = append(selector, string([]byte(name))) 98 | 99 | node = node.Dig(name) 100 | } 101 | requestList = append(requestList, selector) 102 | } 103 | requests = append(requests, requestList) 104 | 105 | Release(root) 106 | } 107 | if err := scanner.Err(); err != nil { 108 | panic(err.Error()) 109 | } 110 | 111 | s, _ := file.Stat() 112 | return lines, requests, s.Size() 113 | } 114 | 115 | // BenchmarkFair benchmarks overall performance of libs as fair as it can: 116 | // * using various JSON payload 117 | // * decoding 118 | // * doing low and high count of search requests 119 | // * encoding 120 | func BenchmarkFair(b *testing.B) { 121 | 122 | // some big buffer to avoid allocations 123 | s := make([]byte, 0, 512*1024) 124 | 125 | // let's make it deterministic as hell 126 | rand.Seed(666) 127 | 128 | // do little and few amount of search request 129 | requestsCount := []int{1, 8} 130 | 131 | pretenders := []struct { 132 | name string 133 | fn func(b *testing.B, jsons [][]byte, fields [][][]string, reqCount int) 134 | }{ 135 | { 136 | name: "complex", 137 | fn: func(b *testing.B, jsons [][]byte, fields [][][]string, reqCount int) { 138 | root := Spawn() 139 | for i := 0; i < b.N; i++ { 140 | for _, json := range jsons { 141 | _ = root.DecodeBytes(json) 142 | for j := 0; j < reqCount; j++ { 143 | for _, f := range fields { 144 | for _, ff := range f { 145 | root.Dig(ff...) 146 | } 147 | } 148 | } 149 | s = root.Encode(s[:0]) 150 | } 151 | } 152 | Release(root) 153 | }, 154 | }, 155 | { 156 | name: "get", 157 | fn: func(b *testing.B, jsons [][]byte, fields [][][]string, reqCount int) { 158 | root := Spawn() 159 | for _, json := range jsons { 160 | _ = root.DecodeBytes(json) 161 | for j := 0; j < reqCount; j++ { 162 | for _, f := range fields { 163 | for _, ff := range f { 164 | for i := 0; i < b.N; i++ { 165 | root.Dig(ff...) 166 | } 167 | } 168 | } 169 | } 170 | } 171 | Release(root) 172 | }, 173 | }, 174 | } 175 | 176 | workload, stableSize := getStableWorkload() 177 | workloads, requests, chaoticSize := getChaoticWorkload() 178 | // 179 | b.Run("complex-stable-flavor|"+pretenders[0].name, func(b *testing.B) { 180 | b.SetBytes(stableSize * int64(len(requestsCount))) 181 | b.ResetTimer() 182 | for _, reqCount := range requestsCount { 183 | for _, w := range workload { 184 | pretenders[0].fn(b, [][]byte{w.json}, [][][]string{w.requests}, reqCount) 185 | } 186 | } 187 | }) 188 | 189 | b.Run("complex-chaotic-flavor|"+pretenders[0].name, func(b *testing.B) { 190 | b.SetBytes(chaoticSize * int64(len(requestsCount))) 191 | b.ResetTimer() 192 | for _, reqCount := range requestsCount { 193 | pretenders[0].fn(b, workloads, requests, reqCount) 194 | } 195 | }) 196 | 197 | b.Run("get-stable-flavor|"+pretenders[1].name, func(b *testing.B) { 198 | b.SetBytes(stableSize) 199 | b.ResetTimer() 200 | for _, w := range workload { 201 | pretenders[1].fn(b, [][]byte{w.json}, [][][]string{w.requests}, 1) 202 | } 203 | }) 204 | 205 | b.Run("get-chaotic-flavor|"+pretenders[1].name, func(b *testing.B) { 206 | b.SetBytes(chaoticSize) 207 | b.ResetTimer() 208 | pretenders[1].fn(b, workloads, requests, 1) 209 | }) 210 | } 211 | 212 | func BenchmarkValueDecodeInt(b *testing.B) { 213 | tests := []struct { 214 | s string 215 | n int64 216 | }{ 217 | {s: "", n: 0}, 218 | {s: " ", n: 0}, 219 | {s: "xxx", n: 0}, 220 | {s: "-xxx", n: 0}, 221 | {s: "1xxx", n: 0}, 222 | {s: "-", n: 0}, 223 | {s: "111 ", n: 0}, 224 | {s: "1-1", n: 0}, 225 | {s: "s1", n: 0}, 226 | {s: "0", n: 0}, 227 | {s: "-0", n: 0}, 228 | {s: "5", n: 5}, 229 | {s: "-5", n: -5}, 230 | {s: " 0", n: 0}, 231 | {s: " 5", n: 0}, 232 | {s: "333", n: 333}, 233 | {s: "-333", n: -333}, 234 | {s: "1111111111", n: 1111111111}, 235 | {s: "987654321", n: 987654321}, 236 | {s: "123456789", n: 123456789}, 237 | {s: "9223372036854775807", n: 9223372036854775807}, 238 | {s: "-9223372036854775807", n: -9223372036854775807}, 239 | {s: "9999999999999999999", n: 0}, 240 | {s: "99999999999999999999", n: 0}, 241 | {s: "-9999999999999999999", n: 0}, 242 | {s: "-99999999999999999999", n: 0}, 243 | } 244 | 245 | for i := 0; i < b.N; i++ { 246 | for _, test := range tests { 247 | decodeInt64(test.s) 248 | } 249 | } 250 | } 251 | 252 | func BenchmarkValueEscapeString(b *testing.B) { 253 | tests := []struct { 254 | s string 255 | }{ 256 | {s: `"""\\\\\"""\'\" \\\""|"|"|"|\\'\dasd' |"|\\\\'\\\|||\\'"`}, 257 | {s: `sfsafwefqwueibfiquwbfiuqwebfiuqwbfiquwbfqiwbfoqiwuefb""""""""""""""""""""""""`}, 258 | {s: `sfsafwefqwueibfiquwbfiuqwebfiuqwbfiquwbfqiwbfoqiwuefbxxxxxxxxxxxxxxxxxxxxxxx"`}, 259 | } 260 | 261 | out := make([]byte, 0, 0) 262 | for i := 0; i < b.N; i++ { 263 | for _, test := range tests { 264 | out = escapeString(out[:0], test.s) 265 | } 266 | } 267 | } -------------------------------------------------------------------------------- /insane_test.go: -------------------------------------------------------------------------------- 1 | package insaneJSON 2 | 3 | import ( 4 | "math/rand" 5 | "strconv" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | const ( 13 | bigJSON = `{"_id":"5d57e438c48c6e5d4ca83b29","index":0,"guid":"ac784884-a6a3-4987-bb0f-d19e09462677","isActive":false,"balance":"$1,961.04","picture":"http://placehold.it/32x32","age":33,"eyeColor":"blue","name":"McleodMendez","gender":"male","compafny":"PARCOE","com1pany":"PARCOE","co2mpfany":"PARCOE","co222fmpany":"PARCOE","co222empany":"PARCOE","co2222fmpany":"PARCOE","co2222mpany":"PARCOE","compfany":"PARCOE","co2mpany":"PARCOE","compwfany":"PARCOE","compfweany":"PARCOE","comfwepany":"PARCOE","compefefany":"PARCOE","comfeqpany":"PARCOE","comfwefvwepany":"PARCOE","comvfqfqewfpany":"PARCOE","compweewany":"PARCOE","wff":"PARCOE","comqvvpany":"PARCOE","comvqwevpany":"PARCOE","compvany":"PARCOE","compvqeany":"PARCOE","compfanvy":"PARCOE","comspany":"PARCOE","compaany":"PARCOE","compaaqny":"PARCOE","compaqny":"PARCOE","_id1":"5d57e438c48c6e5d4ca83b29","index1":0,"guid1":"ac784884-a6a3-4987-bb0f-d19e09462677","isActive1":false,"balance1":"$1,961.04","picture1":"http://placehold.it/32x32","age1":33,"eyeColor1":"blue","name1":"McleodMendez","gender1":"male","compafny1":"PARCOE","com1pany1":"PARCOE","co2mpfany1":"PARCOE","co222fmpany1":"PARCOE","co222empany1":"PARCOE","co2222fmpany1":"PARCOE","co2222mpany1":"PARCOE","compfany1":"PARCOE","co2mpany1":"PARCOE","compwfany1":"PARCOE","compfweany1":"PARCOE","comfwepany1":"PARCOE","compefefany1":"PARCOE","comfeqpany1":"PARCOE","comfwefvwepany1":"PARCOE","comvfqfqewfpany1":"PARCOE","compweewany1":"PARCOE","wff1":"PARCOE","comqvvpany1":"PARCOE","comvqwevpany1":"PARCOE","compvany1":"PARCOE","compvqeany1":"PARCOE","compfanvy1":"PARCOE","comspany1":"PARCOE","compaany1":"PARCOE","compaaqny1":"PARCOE","compaqny1":"PARCOE"}` 14 | ) 15 | 16 | func TestDecodeLight(t *testing.T) { 17 | json := `{"_id":"5d53006246df0b962b787d11","index":"0","guid":"80d75945-6251-46a2-b6c9-a10094beed6e","isActive":"false","balance":"$2,258.24","picture":"http://placehold.it/32x32","age":"34","eyeColor":"brown","company":"NIMON","email":"anne.everett@nimon.name","phone":"+1(946)560-2227","address":"815EmpireBoulevard,Blue,Nevada,5617","about":"Proidentoccaecateulaborislaboreofficialaborumvelitanimnulla.Laboreametoccaecataliquaminimlaboreadenimdolorelaborum.Eiusmodesseeiusmodaliquacillumullamcodonisivelitesseincididunt.Ininestessereprehenderitirureaniminsit.","registered":"Friday,May27,20165:05AM","latitude":"-5.922381","longitude":"-49.143968","greeting":"Hello,Anne!Youhave7unreadmessages.","favoriteFruit":"banana"}` 18 | root, err := DecodeString(json) 19 | defer Release(root) 20 | 21 | assert.NoError(t, err, "error while decoding") 22 | assert.NotNil(t, root, "node shouldn't be nil") 23 | assert.True(t, root.IsObject(), "wrong first node") 24 | } 25 | 26 | func TestDecodeQuote(t *testing.T) { 27 | json := `{"log":"{\"ts\":\"2019-10-04T15:54:22.312412503Z\",\"service\":\"oms-go-broker\",\"message\":\"\\u003e0\\","stream":"stdout","time":"2019-10-04T15:54:22.313584867Z"}` 28 | root, err := DecodeString(json) 29 | defer Release(root) 30 | 31 | assert.NoError(t, err, "error while decoding") 32 | assert.NotNil(t, root, "node shouldn't be nil") 33 | assert.True(t, root.IsObject(), "wrong first node") 34 | } 35 | 36 | func TestDecodeQuoteTriple(t *testing.T) { 37 | json := `{"log":"{\"ts\":\"2019-10-04T15:54:22.312412503Z\",\"service\":\"oms-go-broker\",\"message\":\"\\u003e0\\\"","stream":"stdout","time":"2019-10-04T15:54:22.313584867Z"}` 38 | root, err := DecodeString(json) 39 | defer Release(root) 40 | 41 | assert.NoError(t, err, "error while decoding") 42 | assert.NotNil(t, root, "node shouldn't be nil") 43 | assert.True(t, root.IsObject(), "wrong first node") 44 | } 45 | 46 | func TestDecodeReusing(t *testing.T) { 47 | json := `{"_id":"5d53006246df0b962b787d11","index":"0","guid":"80d75945-6251-46a2-b6c9-a10094beed6e","isActive":"false","balance":"$2,258.24","picture":"http://placehold.it/32x32","age":"34","eyeColor":"brown","company":"NIMON","email":"anne.everett@nimon.name","phone":"+1(946)560-2227","address":"815EmpireBoulevard,Blue,Nevada,5617","about":"Proidentoccaecateulaborislaboreofficialaborumvelitanimnulla.Laboreametoccaecataliquaminimlaboreadenimdolorelaborum.Eiusmodesseeiusmodaliquacillumullamcodonisivelitesseincididunt.Ininestessereprehenderitirureaniminsit.","registered":"Friday,May27,20165:05AM","latitude":"-5.922381","longitude":"-49.143968","greeting":"Hello,Anne!Youhave7unreadmessages.","favoriteFruit":"banana"}` 48 | root := Spawn() 49 | err := root.DecodeString(json) 50 | defer Release(root) 51 | 52 | assert.NoError(t, err, "error while decoding") 53 | assert.NotNil(t, root, "node shouldn't be nil") 54 | assert.True(t, root.IsObject(), "wrong first node") 55 | } 56 | 57 | func TestDecodeAdditional(t *testing.T) { 58 | jsonA := `{"_id":"5d53006246df0b962b787d11"}` 59 | root, err := DecodeString(jsonA) 60 | defer Release(root) 61 | assert.NotNil(t, root, "node shouldn't be nil") 62 | assert.NoError(t, err, "error while decoding") 63 | 64 | jsonB := `[0, 1, 2]` 65 | node, err := root.DecodeStringAdditional(jsonB) 66 | assert.NotNil(t, node, "node shouldn't be nil") 67 | assert.NoError(t, err, "error while decoding") 68 | 69 | assert.Equal(t, jsonA, root.EncodeToString(), "wrong first node") 70 | assert.Equal(t, 1, node.Dig("1").AsInt(), "wrong node value") 71 | } 72 | 73 | func TestDecodeManyObjects(t *testing.T) { 74 | json := `{"no_key2":"100","somefield":{"no_key2":"100","somefield":{"no_key2":"100","somefield":{"no_key2":"100","somefield":{"no_key2":"100","somefield":{"no_key2":"100","somefield":{"no_key2":"100","somefield":"ok"},"no_key1":"100"},"no_key1":"100"},"no_key1":"100"},"no_key1":"100"},"no_key1":"100"},"no_key1":"100"}` 75 | root, err := DecodeString(json) 76 | defer Release(root) 77 | 78 | assert.NoError(t, err, "error while decoding") 79 | assert.NotNil(t, root, "node shouldn't be nil") 80 | assert.True(t, root.IsObject(), "wrong first node") 81 | assert.True(t, root.AsFields()[0].IsField(), "wrong second node") 82 | } 83 | 84 | func TestDecodeArray(t *testing.T) { 85 | json := `{"first":["s1","s2","s3"],"second":[{"s4":true},{"s5":false}]}` 86 | node, err := DecodeString(json) 87 | defer Release(node) 88 | 89 | assert.NoError(t, err, "error while decoding") 90 | assert.NotNil(t, node, "node shouldn't be nil") 91 | 92 | array := node.Dig("first").AsArray() 93 | assert.NotNil(t, array, "array shouldn't be empty") 94 | assert.Equal(t, 3, len(array), "wrong array length") 95 | 96 | assert.Equal(t, "s1", array[0].AsString(), "wrong node value") 97 | assert.Equal(t, "s2", array[1].AsString(), "wrong node value") 98 | assert.Equal(t, "s3", array[2].AsString(), "wrong node value") 99 | 100 | array = node.Dig("second").AsArray() 101 | assert.NotNil(t, array, "array shouldn't be empty") 102 | assert.NotNil(t, 2, len(array), "wrong array length") 103 | arrayNode := array[0] 104 | assert.True(t, arrayNode.IsObject(), "wrong node value") 105 | assert.True(t, arrayNode.Dig("s4").AsBool(), "wrong node value") 106 | 107 | arrayNode = array[1] 108 | assert.True(t, arrayNode.IsObject(), "wrong node value") 109 | assert.Equal(t, false, arrayNode.Dig("s5").AsBool(), "wrong node value") 110 | } 111 | 112 | func TestDecodeArrayElements(t *testing.T) { 113 | json := `["first","second","third"]` 114 | root, err := DecodeString(json) 115 | defer Release(root) 116 | 117 | assert.NoError(t, err, "error while decoding") 118 | assert.NotNil(t, root, "node shouldn't be nil") 119 | 120 | assert.Equal(t, "first", root.AsArray()[0].AsString(), "wrong array element value") 121 | assert.Equal(t, "second", root.AsArray()[1].AsString(), "wrong array element value") 122 | assert.Equal(t, "third", root.AsArray()[2].AsString(), "wrong array element value") 123 | } 124 | 125 | func TestDecodeTrueFalseNull(t *testing.T) { 126 | json := `{"true":true,"false":false,"null":null}` 127 | root, err := DecodeString(json) 128 | defer Release(root) 129 | 130 | assert.NoError(t, err, "error while decoding") 131 | assert.NotNil(t, root, "node shouldn't be nil") 132 | 133 | assert.Equal(t, true, root.Dig("true").AsBool(), "wrong node value") 134 | assert.Equal(t, false, root.Dig("false").AsBool(), "wrong node value") 135 | assert.Equal(t, true, root.Dig("null").IsNull(), "wrong node value") 136 | } 137 | 138 | func TestDecodeNumber(t *testing.T) { 139 | json := `{"first": 100,"second": 1e20,"third": 1e+1}` 140 | root, err := DecodeString(json) 141 | defer Release(root) 142 | 143 | assert.NoError(t, err, "error while decoding") 144 | assert.NotNil(t, root, "node shouldn't be nil") 145 | 146 | assert.Equal(t, 100, root.Dig("first").AsInt(), "wrong node value") 147 | assert.Equal(t, 1e20, root.Dig("second").AsFloat(), "wrong node value") 148 | assert.Equal(t, 10, root.Dig("third").AsInt(), "wrong node value") 149 | } 150 | 151 | func TestDecodeErr(t *testing.T) { 152 | tests := []struct { 153 | json string 154 | err error 155 | }{ 156 | // common 157 | {json: ``, err: ErrEmptyJSON}, 158 | {json: `"`, err: ErrUnexpectedEndOfString}, 159 | {json: `l`, err: ErrExpectedValue}, 160 | {json: `t`, err: ErrUnexpectedEndOfTrue}, 161 | {json: `f`, err: ErrUnexpectedEndOfFalse}, 162 | {json: `n`, err: ErrUnexpectedEndOfNull}, 163 | 164 | // array 165 | {json: `[`, err: ErrExpectedValue}, 166 | {json: `[ `, err: ErrExpectedValue}, 167 | {json: `[ "`, err: ErrUnexpectedEndOfString}, 168 | {json: `[ t`, err: ErrUnexpectedEndOfTrue}, 169 | {json: `[ f`, err: ErrUnexpectedEndOfFalse}, 170 | {json: `[ n`, err: ErrUnexpectedEndOfNull}, 171 | {json: `[[0`, err: ErrUnexpectedJSONEnding}, 172 | {json: `[,`, err: ErrExpectedValue}, 173 | {json: `[e[]00]`, err: ErrExpectedComma}, 174 | {json: `[,e[]00]`, err: ErrExpectedValue}, 175 | 176 | // flagObject 177 | {json: ` {`, err: ErrExpectedObjectField}, 178 | {json: `{`, err: ErrExpectedObjectField}, 179 | {json: `{ `, err: ErrExpectedObjectField}, 180 | {json: `{ f`, err: ErrExpectedObjectField}, 181 | {json: `{{`, err: ErrExpectedObjectField}, 182 | {json: `{"`, err: ErrUnexpectedEndOfObjectField}, 183 | {json: `{l`, err: ErrExpectedObjectField}, 184 | {json: `{ l`, err: ErrExpectedObjectField}, 185 | {json: `{""`, err: ErrExpectedObjectFieldSeparator}, 186 | {json: `{"" `, err: ErrExpectedObjectFieldSeparator}, 187 | {json: `{"":`, err: ErrExpectedValue}, 188 | {json: `{"": `, err: ErrExpectedValue}, 189 | {json: `{"" :`, err: ErrExpectedValue}, 190 | {json: `{"":"`, err: ErrUnexpectedEndOfString}, 191 | {json: `{"": "`, err: ErrUnexpectedEndOfString}, 192 | {json: `{"":0`, err: ErrUnexpectedJSONEnding}, 193 | {json: `{,`, err: ErrExpectedObjectField}, 194 | {json: `{"":0[]00[]00]`, err: ErrExpectedComma}, 195 | {json: `{"":0""[]00[]00]`, err: ErrExpectedComma}, 196 | {json: `{,"":0""[]00[]00]`, err: ErrExpectedObjectField}, 197 | 198 | // endings 199 | {json: `1.0jjj`, err: ErrUnexpectedJSONEnding}, 200 | {json: `{}}`, err: ErrUnexpectedJSONEnding}, 201 | {json: `[].`, err: ErrUnexpectedJSONEnding}, 202 | {json: `"sssss".`, err: ErrUnexpectedJSONEnding}, 203 | {json: `truetrue.`, err: ErrUnexpectedJSONEnding}, 204 | {json: `falsenull`, err: ErrUnexpectedJSONEnding}, 205 | {json: `null:`, err: ErrUnexpectedJSONEnding}, 206 | 207 | 208 | // ok 209 | {json: `0`, err: nil}, 210 | {json: `1.0`, err: nil}, 211 | {json: `"string"`, err: nil}, 212 | {json: `true`, err: nil}, 213 | {json: `false`, err: nil}, 214 | {json: `null`, err: nil}, 215 | {json: `{}`, err: nil}, 216 | {json: `[]`, err: nil}, 217 | {json: `[[ ],0]`, err: nil}, 218 | {json: `{"":{"l":[30]},"c":""}`, err: nil}, 219 | {json: `{"a":{"6":"5","l":[3,4]},"c":"d"}`, err: nil}, 220 | } 221 | 222 | for _, test := range tests { 223 | root, err := DecodeString(test.json) 224 | if test.err != nil { 225 | assert.NotNil(t, err, "where should be an error decoding %s", test.json) 226 | assert.True(t, strings.Contains(err.Error(), test.err.Error()), "wrong err %s, expected=%s, got=%s", test.json, test.err.Error(), err.Error()) 227 | } else { 228 | assert.NoError(t, err, "where shouldn't be an error %s", test.json) 229 | root.EncodeToByte() 230 | } 231 | Release(root) 232 | } 233 | } 234 | 235 | func TestEncode(t *testing.T) { 236 | json := `{"key_a":{"key_a_a":["v1","vv1"],"key_a_b":[],"key_a_c":"v3"},"key_b":{"key_b_a":["v3","v31"],"key_b_b":{}}}` 237 | root, err := DecodeString(json) 238 | defer Release(root) 239 | 240 | assert.NoError(t, err, "error while decoding") 241 | assert.NotNil(t, root, "node shouldn't be nil") 242 | 243 | assert.Equal(t, json, root.EncodeToString(), "wrong encoding") 244 | } 245 | 246 | func TestString(t *testing.T) { 247 | json := `["hello \\ \" op \\ \" op op","shit"]` 248 | 249 | root, err := DecodeString(json) 250 | defer Release(root) 251 | 252 | assert.NoError(t, err, "error while decoding") 253 | assert.NotNil(t, root, "node shouldn't be nil") 254 | 255 | assert.Equal(t, `hello \ " op \ " op op`, root.Dig("0").AsString(), "wrong node value") 256 | assert.Equal(t, "shit", root.Dig("1").AsString(), "wrong node value") 257 | 258 | assert.Equal(t, json, root.EncodeToString(), "wrong encoding") 259 | } 260 | 261 | func TestField(t *testing.T) { 262 | json := `{"hello \\ \" op \\ \" op op":"shit"}` 263 | root, err := DecodeString(json) 264 | defer Release(root) 265 | 266 | assert.NoError(t, err, "error while decoding") 267 | assert.NotNil(t, root, "node shouldn't be nil") 268 | 269 | assert.Equal(t, "shit", root.Dig(`hello \ " op \ " op op`).AsString(), "wrong node value") 270 | 271 | assert.Equal(t, json, root.EncodeToString(), "wrong encoding") 272 | } 273 | 274 | func TestInsane(t *testing.T) { 275 | test := loadJSON("insane", [][]string{}) 276 | 277 | root, err := DecodeBytes(test.json) 278 | defer Release(root) 279 | 280 | assert.NoError(t, err, "error while decoding") 281 | assert.NotNil(t, root, "node shouldn't be nil") 282 | 283 | encoded := root.EncodeToByte() 284 | assert.Equal(t, 465158, len(encoded), "wrong encoding") 285 | } 286 | 287 | func TestDig(t *testing.T) { 288 | root, err := DecodeString(bigJSON) 289 | defer Release(root) 290 | 291 | assert.NoError(t, err, "error while decoding") 292 | assert.NotNil(t, root, "node shouldn't be nil") 293 | 294 | assert.Equal(t, "ac784884-a6a3-4987-bb0f-d19e09462677", root.Dig("guid").AsString(), "wrong encoding") 295 | } 296 | 297 | func TestDigEmpty(t *testing.T) { 298 | root, err := DecodeString(`{"":""}`) 299 | defer Release(root) 300 | 301 | assert.NoError(t, err, "error while decoding") 302 | assert.NotNil(t, root, "node shouldn't be nil") 303 | 304 | assert.Equal(t, "", root.Dig("").AsString(), "wrong node value") 305 | } 306 | 307 | func TestDigDeep(t *testing.T) { 308 | test := loadJSON("heavy", [][]string{}) 309 | 310 | root, err := DecodeBytes(test.json) 311 | defer Release(root) 312 | 313 | assert.NoError(t, err, "error while decoding") 314 | assert.NotNil(t, root, "node shouldn't be nil") 315 | 316 | value := root.Dig("first", "second", "third", "fourth", "fifth", "ok") 317 | assert.NotNil(t, value, "Can't find field") 318 | assert.Equal(t, "ok", value.AsString(), "wrong encoding") 319 | } 320 | 321 | func TestDigNil(t *testing.T) { 322 | json := `["first","second","third"]` 323 | root, _ := DecodeString(json) 324 | defer Release(root) 325 | 326 | value := root.Dig("one", "$f_8)9").AsString() 327 | assert.Equal(t, "", value, "wrong array element value") 328 | } 329 | 330 | func TestAddField(t *testing.T) { 331 | tests := []struct { 332 | json string 333 | fields []string 334 | result string 335 | }{ 336 | {json: `{}`, fields: []string{"a"}, result: `{"a":null}`}, 337 | {json: `{}`, fields: []string{"a", "b", "c"}, result: `{"a":null,"b":null,"c":null}`}, 338 | {json: `{}`, fields: []string{"a", "b", "c", "d"}, result: `{"a":null,"b":null,"c":null,"d":null}`}, 339 | {json: `{"a":"a"}`, fields: []string{"b", "c", "d"}, result: `{"a":"a","b":null,"c":null,"d":null}`}, 340 | {json: `{"x":{"a":"a","e":"e"}}`, fields: []string{"b", "c", "d"}, result: `{"x":{"a":"a","e":"e"},"b":null,"c":null,"d":null}`}, 341 | {json: `{"x":["a","a"]}`, fields: []string{"b", "c", "d"}, result: `{"x":["a","a"],"b":null,"c":null,"d":null}`}, 342 | } 343 | 344 | for _, test := range tests { 345 | root, err := DecodeString(test.json) 346 | assert.NoError(t, err, "where shouldn't be an error") 347 | for _, field := range test.fields { 348 | root.AddField(field) 349 | assert.True(t, root.Dig(field).IsNull(), "wrong node type") 350 | } 351 | assert.Equal(t, test.result, root.EncodeToString(), "wrong encoding") 352 | Release(root) 353 | } 354 | } 355 | 356 | func TestAddElement(t *testing.T) { 357 | tests := []struct { 358 | json string 359 | count int 360 | result string 361 | }{ 362 | {json: `[]`, count: 1, result: `[null]`}, 363 | {json: `[]`, count: 3, result: `[null,null,null]`}, 364 | {json: `[]`, count: 4, result: `[null,null,null,null]`}, 365 | {json: `["a"]`, count: 3, result: `["a",null,null,null]`}, 366 | {json: `["a","a"]`, count: 3, result: `["a","a",null,null,null]`}, 367 | {json: `[{"a":"a"}]`, count: 3, result: `[{"a":"a"},null,null,null]`}, 368 | {json: `[["a","a"]]`, count: 3, result: `[["a","a"],null,null,null]`}, 369 | } 370 | 371 | for _, test := range tests { 372 | root, err := DecodeString(test.json) 373 | assert.NoError(t, err, "where shouldn't be an error") 374 | for index := 0; index < test.count; index++ { 375 | root.AddElement() 376 | l := len(root.AsArray()) 377 | assert.True(t, root.Dig(strconv.Itoa(l - 1)).IsNull(), "wrong node type") 378 | } 379 | assert.Equal(t, test.result, root.EncodeToString(), "wrong encoding") 380 | Release(root) 381 | } 382 | } 383 | 384 | func TestInsertElement(t *testing.T) { 385 | tests := []struct { 386 | json string 387 | pos1 int 388 | pos2 int 389 | result string 390 | }{ 391 | {json: `[]`, pos1: 0, pos2: 0, result: `[null,null]`}, 392 | {json: `[]`, pos1: 0, pos2: 1, result: `[null,null]`}, 393 | {json: `[{"a":"a"},{"a":"a"},{"a":"a"}]`, pos1: 0, pos2: 0, result: `[null,null,{"a":"a"},{"a":"a"},{"a":"a"}]`}, 394 | {json: `[[],[],[]]`, pos1: 0, pos2: 1, result: `[null,null,[],[],[]]`}, 395 | {json: `[[],[],[]]`, pos1: 0, pos2: 2, result: `[null,[],null,[],[]]`}, 396 | {json: `[[],[],[]]`, pos1: 2, pos2: 3, result: `[[],[],null,null,[]]`}, 397 | {json: `[[],[],[]]`, pos1: 2, pos2: 2, result: `[[],[],null,null,[]]`}, 398 | {json: `[[],[],[]]`, pos1: 3, pos2: 3, result: `[[],[],[],null,null]`}, 399 | {json: `[[],[],[]]`, pos1: 3, pos2: 4, result: `[[],[],[],null,null]`}, 400 | } 401 | 402 | for _, test := range tests { 403 | root, err := DecodeString(test.json) 404 | assert.NoError(t, err, "where shouldn't be an error") 405 | root.InsertElement(test.pos1) 406 | assert.True(t, root.Dig(strconv.Itoa(test.pos1)).IsNull(), "wrong node type") 407 | root.InsertElement(test.pos2) 408 | assert.True(t, root.Dig(strconv.Itoa(test.pos2)).IsNull(), "wrong node type") 409 | 410 | assert.Equal(t, test.result, root.EncodeToString(), "wrong encoding") 411 | Release(root) 412 | } 413 | } 414 | 415 | // it simply shouldn't crush 416 | func TestFuzz(t *testing.T) { 417 | tests := []string{ 418 | `[]`, 419 | `[0,1,2,3]`, 420 | `[{},{},{},{}]`, 421 | `[{"1":"1"},{"1":"1"},{"1":"1"},{"1":"1"}]`, 422 | `[["1","1"],["1","1"],["1","1"],["1","1"]]`, 423 | `[[],0]`, 424 | `["a",{"6":"5","l":[3,4]},"c","d"]`, 425 | `{}`, 426 | `{"a":null}`, 427 | `{"a":null,"b":null,"c":null}`, 428 | `{"a":null,"b":null,"c":null,"d":null}`, 429 | `{"a":"a"}`, 430 | `{"a":"a","b":null,"c":null,"d":null}`, 431 | `{"x":{"a":"a","e":"e"}}`, 432 | `{"x":{"a":"a","e":"e"},"b":null,"c":null,"d":null}`, 433 | `{"x":["a","a"]}`, 434 | `{"x":["a","a"],"b":null,"c":null,"d":null}`, 435 | `[null]`, 436 | `[null,null,null]`, 437 | `[null,null,null,null]`, 438 | `["a",null,null,null]`, 439 | `["a","a"]`, 440 | `["a","a",null,null,null]`, 441 | `[{"a":"a"}]`, 442 | `[{"a":"a"},null,null,null]`, 443 | `[["a","a"]]`, 444 | `[["a","a"],null,null,null]`, 445 | `[]`, 446 | `[0,1,2,3]`, 447 | `[{},{},{},{}]`, 448 | `[{"1":"1"},{"1":"1"},{"1":"1"},{"1":"1"}]`, 449 | `[["1","1"],["1","1"],["1","1"],["1","1"]]`, 450 | `[[],0]`, 451 | `["a",{"6":"5","l":[3,4]},"c","d"]`, 452 | } 453 | 454 | // hell yeah, we are deterministic 455 | rand.Seed(666) 456 | for _, test := range tests { 457 | Fuzz([]byte(test)) 458 | } 459 | } 460 | 461 | func TestArraySuicide(t *testing.T) { 462 | tests := []string{ 463 | `[]`, 464 | `[0,1,2,3]`, 465 | `[{},{},{},{}]`, 466 | `[{"1":"1"},{"1":"1"},{"1":"1"},{"1":"1"}]`, 467 | `[["1","1"],["1","1"],["1","1"],["1","1"]]`, 468 | `[[],0]`, 469 | `["a",{"6":"5","l":[3,4]},"c","d"]`, 470 | } 471 | 472 | for _, json := range tests { 473 | root, err := DecodeString(json) 474 | assert.NoError(t, err, "err should be nil") 475 | for range root.AsArray() { 476 | root.Dig("0").Suicide() 477 | } 478 | assert.Equal(t, 0, len(root.AsArray()), "array should be empty") 479 | assert.Equal(t, `[]`, root.EncodeToString(), "array should be empty") 480 | Release(root) 481 | 482 | root, err = DecodeString(json) 483 | assert.NoError(t, err, "err should be nil") 484 | l := len(root.AsArray()) 485 | for i := range root.AsArray() { 486 | root.Dig(strconv.Itoa(l - i - 1)).Suicide() 487 | } 488 | 489 | assert.Equal(t, 0, len(root.AsArray()), "array should be empty") 490 | assert.Equal(t, `[]`, root.EncodeToString(), "array should be empty") 491 | Release(root) 492 | } 493 | } 494 | 495 | func TestObjectSuicide(t *testing.T) { 496 | tests := []string{ 497 | `{}`, 498 | `{"0":"0","1":"1","2":"2","3":"3"}`, 499 | `{"1":{},"2":{},"3":{},"4":{}}`, 500 | `{"1":{"1":"1"},"2":{"1":"1"},"3":{"1":"1"},"4":{"1":"1"}}`, 501 | `{"1":["1","1"],"2":["1","1"],"3":["1","1"],"4":["1","1"]}`, 502 | `{"1":[],"2":0}`, 503 | `{"a":{"6":"5","l":[3,4]},"c":"d"}`, 504 | `{"":{"":"","":""}}`, 505 | `{"x":{"a":"a","e":"e"}}`, 506 | } 507 | 508 | for _, json := range tests { 509 | root, err := DecodeString(json) 510 | assert.NoError(t, err, "err should be nil") 511 | fields := root.AsFields() 512 | for _, field := range fields { 513 | root.Dig(field.AsString()).Suicide() 514 | } 515 | assert.Equal(t, 0, len(root.AsArray()), "array should be empty") 516 | assert.Equal(t, `{}`, root.EncodeToString(), "array should be empty") 517 | Release(root) 518 | 519 | root, err = DecodeString(json) 520 | assert.NoError(t, err, "err should be nil") 521 | fields = root.AsFields() 522 | l := len(fields) 523 | for i := range fields { 524 | root.Dig(fields[l-i-1].AsString()).Suicide() 525 | } 526 | for _, field := range fields { 527 | root.Dig(field.AsString()).Suicide() 528 | } 529 | assert.Equal(t, 0, len(root.AsArray()), "array should be empty") 530 | assert.Equal(t, `{}`, root.EncodeToString(), "array should be empty") 531 | Release(root) 532 | } 533 | } 534 | 535 | func TestMergeWith(t *testing.T) { 536 | jsonA := `{"1":"1","2":"2"}` 537 | root, err := DecodeString(jsonA) 538 | defer Release(root) 539 | 540 | assert.NotNil(t, root, "node shouldn't be nil") 541 | assert.NoError(t, err, "error while decoding") 542 | 543 | jsonB := `{"1":"1","3":"3","4":"4"}` 544 | node, err := root.DecodeStringAdditional(jsonB) 545 | assert.NoError(t, err, "error while decoding") 546 | assert.NotNil(t, node, "node shouldn't be nil") 547 | 548 | root.MergeWith(node) 549 | 550 | assert.Equal(t, `{"1":"1","2":"2","3":"3","4":"4"}`, root.EncodeToString(), "wrong first node") 551 | } 552 | 553 | func TestMergeWithComplex(t *testing.T) { 554 | jsonA := `{"1":{"1":"1"}}` 555 | root, err := DecodeString(jsonA) 556 | defer Release(root) 557 | 558 | assert.NotNil(t, root, "node shouldn't be nil") 559 | assert.NoError(t, err, "error while decoding") 560 | 561 | jsonB := `{"1":1,"2":{"2":"2"}}` 562 | node, err := root.DecodeStringAdditional(jsonB) 563 | assert.NoError(t, err, "error while decoding") 564 | assert.NotNil(t, node, "node shouldn't be nil") 565 | 566 | root.MergeWith(node) 567 | 568 | assert.Equal(t, `{"1":1,"2":{"2":"2"}}`, root.EncodeToString(), "wrong first node") 569 | } 570 | 571 | func TestMutateToJSON(t *testing.T) { 572 | tests := []struct { 573 | name string 574 | json string 575 | mutation string 576 | dig []string 577 | result string 578 | checkDig [][]string 579 | checkValues []int 580 | }{ 581 | { 582 | json: `{"a":"b","c":"4"}`, 583 | dig: []string{"a"}, 584 | mutation: `5`, 585 | result: `{"a":5,"c":"4"}`, 586 | checkDig: [][]string{{"a"}, {"c"}}, 587 | checkValues: []int{5, 4}, 588 | }, 589 | { 590 | json: `{"a":"1","c":"4"}`, 591 | dig: []string{"c"}, 592 | mutation: `6`, 593 | result: `{"a":"1","c":6}`, 594 | checkDig: [][]string{{"a"}, {"c"}}, 595 | checkValues: []int{1, 6}, 596 | }, 597 | { 598 | json: `{"a":"b","c":"d"}`, 599 | dig: []string{"a"}, 600 | mutation: `{"5":"5","l":[3,4]}`, 601 | result: `{"a":{"5":"5","l":[3,4]},"c":"d"}`, 602 | checkDig: [][]string{{"a", "l", "0"}, {"a", "l", "1"}}, 603 | checkValues: []int{3, 4}, 604 | }, 605 | { 606 | json: `{"a":"b","c":"d"}`, 607 | dig: []string{"c"}, 608 | mutation: `{"5":"5","l":[3,4]}`, 609 | result: `{"a":"b","c":{"5":"5","l":[3,4]}}`, 610 | checkDig: [][]string{{"c", "l", "0"}, {"c", "l", "1"}}, 611 | checkValues: []int{3, 4}, 612 | }, 613 | { 614 | json: `{"a":{"somekey":"someval", "xxx":"yyy"},"c":"d"}`, 615 | dig: []string{"a"}, 616 | mutation: `{"5":"5","l":[3,4]}`, 617 | result: `{"a":{"5":"5","l":[3,4]},"c":"d"}`, 618 | checkDig: [][]string{{"a", "l", "0"}, {"a", "l", "1"}}, 619 | checkValues: []int{3, 4}, 620 | }, 621 | { 622 | json: `["a","b","c","d"]`, 623 | dig: []string{"0"}, 624 | mutation: `{"5":"5","l":[3,4]}`, 625 | result: `[{"5":"5","l":[3,4]},"b","c","d"]`, 626 | checkDig: [][]string{{"0", "l", "0"}, {"0", "l", "1"}}, 627 | checkValues: []int{3, 4}, 628 | }, 629 | { 630 | json: `["a","b","c","d"]`, 631 | dig: []string{"3"}, 632 | mutation: `{"5":"5","l":[3,4]}`, 633 | result: `["a","b","c",{"5":"5","l":[3,4]}]`, 634 | checkDig: [][]string{{"3", "l", "0"}, {"3", "l", "1"}}, 635 | checkValues: []int{3, 4}, 636 | }, 637 | } 638 | 639 | for _, test := range tests { 640 | root, err := DecodeString(test.json) 641 | assert.NoError(t, err, "error while decoding") 642 | 643 | mutatingNode := root.Dig(test.dig...) 644 | mutatingNode.MutateToJSON(root, test.mutation) 645 | for i, dig := range test.checkDig { 646 | assert.Equal(t, test.checkValues[i], root.Dig(dig...).AsInt(), "wrong node value") 647 | if len(dig) > 1 { 648 | assert.Equal(t, test.checkValues[i], mutatingNode.Dig(dig[1:]...).AsInt(), "wrong node value") 649 | } 650 | } 651 | 652 | assert.Equal(t, test.result, root.EncodeToString(), "wrong result json") 653 | 654 | Release(root) 655 | } 656 | } 657 | 658 | func TestMutateToObject(t *testing.T) { 659 | tests := []struct { 660 | json string 661 | dig []string 662 | result string 663 | }{ 664 | { 665 | json: `{"a":"b","c":"d"}`, 666 | dig: []string{"a"}, 667 | result: `{"a":{},"c":"d"}`, 668 | }, 669 | { 670 | json: `{"a":"b","c":"d"}`, 671 | dig: []string{"c"}, 672 | result: `{"a":"b","c":{}}`, 673 | }, 674 | { 675 | json: `["a","b","c","d"]`, 676 | dig: []string{"1"}, 677 | result: `["a",{},"c","d"]`, 678 | }, 679 | { 680 | json: `["a","b","c","d"]`, 681 | dig: []string{"0"}, 682 | result: `[{},"b","c","d"]`, 683 | }, 684 | { 685 | json: `["a","b","c","d"]`, 686 | dig: []string{"3"}, 687 | result: `["a","b","c",{}]`, 688 | }, 689 | { 690 | json: `"string"`, 691 | dig: []string{}, 692 | result: `{}`, 693 | }, 694 | { 695 | json: `[]`, 696 | dig: []string{}, 697 | result: `{}`, 698 | }, 699 | { 700 | json: `true`, 701 | dig: []string{}, 702 | result: `{}`, 703 | }, 704 | { 705 | json: `{"a":{"a":{"a":{"a":"b","c":"d"},"c":"d"},"c":"d"},"c":"d"}`, 706 | dig: []string{"a", "a", "a", "a"}, 707 | result: `{"a":{"a":{"a":{"a":{},"c":"d"},"c":"d"},"c":"d"},"c":"d"}`, 708 | }, 709 | } 710 | 711 | for _, test := range tests { 712 | root, err := DecodeString(test.json) 713 | assert.NoError(t, err, "error while decoding") 714 | 715 | mutatingNode := root.Dig(test.dig...).MutateToObject() 716 | assert.True(t, mutatingNode.IsObject(), "wrong node type") 717 | 718 | o := root.Dig(test.dig...) 719 | assert.True(t, o.IsObject(), "wrong node type") 720 | 721 | o.AddField("test").MutateToString("ok") 722 | assert.Equal(t, "ok", o.Dig("test").AsString(), "wrong result json") 723 | o.Dig("test").Suicide() 724 | 725 | assert.Equal(t, test.result, root.EncodeToString(), "wrong result json") 726 | 727 | Release(root) 728 | } 729 | } 730 | 731 | func TestMutateToArray(t *testing.T) { 732 | tests := []struct { 733 | json string 734 | dig []string 735 | result string 736 | }{ 737 | { 738 | json: `{"a":"b","c":"d"}`, 739 | dig: []string{"a"}, 740 | result: `{"a":[],"c":"d"}`, 741 | }, 742 | { 743 | json: `{"a":"b","c":"d"}`, 744 | dig: []string{"c"}, 745 | result: `{"a":"b","c":[]}`, 746 | }, 747 | { 748 | json: `["a","b","c","d"]`, 749 | dig: []string{"1"}, 750 | result: `["a",[],"c","d"]`, 751 | }, 752 | { 753 | json: `["a","b","c","d"]`, 754 | dig: []string{"0"}, 755 | result: `[[],"b","c","d"]`, 756 | }, 757 | { 758 | json: `["a","b","c","d"]`, 759 | dig: []string{"3"}, 760 | result: `["a","b","c",[]]`, 761 | }, 762 | { 763 | json: `"string"`, 764 | dig: []string{}, 765 | result: `[]`, 766 | }, 767 | { 768 | json: `[]`, 769 | dig: []string{}, 770 | result: `[]`, 771 | }, 772 | { 773 | json: `true`, 774 | dig: []string{}, 775 | result: `[]`, 776 | }, 777 | { 778 | json: `{"a":{"a":{"a":{"a":"b","c":"d"},"c":"d"},"c":"d"},"c":"d"}`, 779 | dig: []string{"a", "a", "a", "a"}, 780 | result: `{"a":{"a":{"a":{"a":[],"c":"d"},"c":"d"},"c":"d"},"c":"d"}`, 781 | }, 782 | } 783 | 784 | for _, test := range tests { 785 | root, err := DecodeString(test.json) 786 | assert.NoError(t, err, "error while decoding") 787 | 788 | mutatingNode := root.Dig(test.dig...).MutateToArray() 789 | assert.True(t, mutatingNode.IsArray(), "wrong node type") 790 | 791 | o := root.Dig(test.dig...) 792 | assert.True(t, o.IsArray(), "wrong node type") 793 | 794 | o.AddElement().MutateToString("ok") 795 | assert.Equal(t, "ok", o.Dig("0").AsString(), "wrong result json") 796 | o.Dig("0").Suicide() 797 | 798 | assert.Equal(t, test.result, root.EncodeToString(), "wrong result json") 799 | 800 | Release(root) 801 | } 802 | } 803 | 804 | func TestMutateCollapse(t *testing.T) { 805 | tests := []struct { 806 | json string 807 | dig []string 808 | mutation int 809 | result string 810 | checkDig [][]string 811 | checkValues []int 812 | }{ 813 | { 814 | json: `{"a":"b","b":["k","k","l","l"],"m":"m"}`, 815 | dig: []string{"b"}, 816 | mutation: 15, 817 | result: `{"a":"b","b":15,"m":"m"}`, 818 | checkDig: [][]string{{"b"}}, 819 | checkValues: []int{15}, 820 | }, 821 | { 822 | json: `{"a":"b","b":{"k":"k","l":"l"},"m":"m"}`, 823 | dig: []string{"b"}, 824 | mutation: 15, 825 | result: `{"a":"b","b":15,"m":"m"}`, 826 | checkDig: [][]string{{"b"}}, 827 | checkValues: []int{15}, 828 | }, 829 | } 830 | 831 | for _, test := range tests { 832 | root, err := DecodeString(test.json) 833 | assert.NoError(t, err, "error while decoding") 834 | 835 | mutatingNode := root.Dig(test.dig...) 836 | mutatingNode.MutateToInt(test.mutation) 837 | for i := range test.checkDig { 838 | assert.Equal(t, test.checkValues[i], root.Dig(test.checkDig[i]...).AsInt(), "wrong node value") 839 | assert.Equal(t, test.checkValues[i], mutatingNode.Dig(test.checkDig[i][1:]...).AsInt(), "wrong node value") 840 | } 841 | 842 | assert.Equal(t, test.result, root.EncodeToString(), "wrong result json") 843 | Release(root) 844 | } 845 | } 846 | 847 | func TestMutateToInt(t *testing.T) { 848 | root, err := DecodeString(`{"a":"b"}`) 849 | defer Release(root) 850 | assert.NoError(t, err, "error while decoding") 851 | 852 | root.Dig("a").MutateToInt(5) 853 | assert.Equal(t, 5, root.Dig("a").AsInt(), "wrong node value") 854 | 855 | assert.Equal(t, `{"a":5}`, root.EncodeToString(), "wrong result json") 856 | } 857 | 858 | func TestMutateToFloat(t *testing.T) { 859 | root, err := DecodeString(`{"a":"b"}`) 860 | defer Release(root) 861 | assert.NoError(t, err, "error while decoding") 862 | 863 | root.Dig("a").MutateToFloat(5.6) 864 | assert.Equal(t, 5.6, root.Dig("a").AsFloat(), "wrong node value") 865 | assert.Equal(t, 6, root.Dig("a").AsInt(), "wrong node value") 866 | 867 | assert.Equal(t, `{"a":5.6}`, root.EncodeToString(), "wrong result json") 868 | } 869 | 870 | func TestMutateToString(t *testing.T) { 871 | root, err := DecodeString(`{"a":"b"}`) 872 | defer Release(root) 873 | assert.NoError(t, err, "error while decoding") 874 | 875 | root.Dig("a").MutateToString("insane") 876 | assert.Equal(t, "insane", root.Dig("a").AsString(), "wrong node value") 877 | 878 | assert.Equal(t, `{"a":"insane"}`, root.EncodeToString(), "wrong result json") 879 | } 880 | 881 | func TestMutateToField(t *testing.T) { 882 | jsons := []string{ 883 | `{"unique":"some_val"}`, 884 | `{"a":"a","b":"b","c":"c","x1":"x1","a1":"a1","b1":"b1","c1":"c1","x2":"x2","a2":"a2","b2":"b2","c2":"c2","x12":"x12","a12":"a12","b12":"b12","c12":"c12","a121":"a121","b121":"b121","c121":"c121","unique":"some_val"}`, 885 | } 886 | 887 | for _, json := range jsons { 888 | root, err := DecodeString(json) 889 | assert.NoError(t, err, "error while decoding") 890 | 891 | root.DigField("unique").MutateToField("mutated") 892 | 893 | assert.Equal(t, "", root.Dig("unique").AsString(), "wrong node value for %s", json) 894 | assert.Equal(t, "some_val", root.Dig("mutated").AsString(), "wrong node value for %s", json) 895 | assert.Equal(t, strings.ReplaceAll(json, "unique", "mutated"), root.EncodeToString(), "wrong result json for %s", json) 896 | 897 | Release(root) 898 | } 899 | } 900 | 901 | func TestDigField(t *testing.T) { 902 | root, err := DecodeString(`{"a":"b"}`) 903 | defer Release(root) 904 | assert.NoError(t, err, "error while decoding") 905 | 906 | root.DigField("a").MutateToField("insane") 907 | assert.Equal(t, "b", root.Dig("insane").AsString(), "wrong node value") 908 | 909 | assert.Equal(t, `{"insane":"b"}`, root.EncodeToString(), "wrong result json") 910 | } 911 | 912 | func TestWhitespace(t *testing.T) { 913 | json := `{ 914 | "one" : "one", 915 | "two" : "two" 916 | }` 917 | 918 | root, err := DecodeString(json) 919 | defer Release(root) 920 | 921 | assert.NoError(t, err, "error while decoding") 922 | assert.NotNil(t, root, "node shouldn't be nil") 923 | 924 | assert.Equal(t, "one", root.Dig("one").AsString(), "wrong field name") 925 | assert.Equal(t, "two", root.Dig("two").AsString(), "wrong field name") 926 | } 927 | 928 | func TestObjectManyFieldsSuicide(t *testing.T) { 929 | root, err := DecodeString(bigJSON) 930 | defer Release(root) 931 | 932 | assert.NoError(t, err, "error while decoding") 933 | assert.NotNil(t, root, "node shouldn't be nil") 934 | 935 | fields := make([]string, 0, 0) 936 | for _, field := range root.AsFields() { 937 | fields = append(fields, string(field.AsBytes())) 938 | } 939 | 940 | for _, field := range root.AsFields() { 941 | root.Dig(field.AsString()).Suicide() 942 | } 943 | 944 | for _, field := range fields { 945 | assert.Nil(t, root.Dig(field), "node should'n be findable") 946 | } 947 | 948 | assert.Equal(t, `{}`, root.EncodeToString(), "wrong result json") 949 | } 950 | 951 | func TestObjectManyFieldsAddSuicide(t *testing.T) { 952 | root, err := DecodeString("{}") 953 | defer Release(root) 954 | 955 | assert.NoError(t, err, "error while decoding") 956 | assert.NotNil(t, root, "node shouldn't be nil") 957 | 958 | fields := 30 959 | for i := 0; i < fields; i++ { 960 | root.AddField(strconv.Itoa(i)).MutateToString(strconv.Itoa(i)) 961 | } 962 | 963 | for i := 0; i < fields; i++ { 964 | assert.NotNil(t, root.Dig(strconv.Itoa(i)), "node should be findable") 965 | assert.Equal(t, strconv.Itoa(i), root.Dig(strconv.Itoa(i)).AsString(), "wrong node value") 966 | } 967 | 968 | for i := 0; i < fields; i++ { 969 | root.Dig(strconv.Itoa(i)).Suicide() 970 | } 971 | 972 | for i := 0; i < fields; i++ { 973 | assert.Nil(t, root.Dig(strconv.Itoa(i), "node should be findable")) 974 | } 975 | 976 | assert.Equal(t, `{}`, root.EncodeToString(), "wrong result json") 977 | } 978 | 979 | func TestObjectFields(t *testing.T) { 980 | json := `{"first": "1","second":"2","third":"3"}` 981 | root, err := DecodeString(json) 982 | defer Release(root) 983 | 984 | assert.NoError(t, err, "error while decoding") 985 | assert.NotNil(t, root, "node shouldn't be nil") 986 | 987 | assert.Equal(t, `first`, root.AsFields()[0].AsString(), "wrong field name") 988 | assert.Equal(t, `second`, root.AsFields()[1].AsString(), "wrong field name") 989 | assert.Equal(t, `third`, root.AsFields()[2].AsString(), "wrong field name") 990 | } 991 | 992 | func TestParseInt64(t *testing.T) { 993 | tests := []struct { 994 | s string 995 | n int64 996 | }{ 997 | {s: "", n: 0}, 998 | {s: " ", n: 0}, 999 | {s: "xxx", n: 0}, 1000 | {s: "-xxx", n: 0}, 1001 | {s: "1xxx", n: 0}, 1002 | {s: "-", n: 0}, 1003 | {s: "111 ", n: 0}, 1004 | {s: "1-1", n: 0}, 1005 | {s: "s1", n: 0}, 1006 | {s: "0", n: 0}, 1007 | {s: "-0", n: 0}, 1008 | {s: "5", n: 5}, 1009 | {s: "-5", n: -5}, 1010 | {s: " 0", n: 0}, 1011 | {s: " 5", n: 0}, 1012 | {s: "333", n: 333}, 1013 | {s: "-333", n: -333}, 1014 | {s: "1111111111", n: 1111111111}, 1015 | {s: "987654321", n: 987654321}, 1016 | {s: "123456789", n: 123456789}, 1017 | {s: "9223372036854775807", n: 9223372036854775807}, 1018 | {s: "-9223372036854775807", n: -9223372036854775807}, 1019 | {s: "9999999999999999999", n: 0}, 1020 | {s: "99999999999999999999", n: 0}, 1021 | {s: "-9999999999999999999", n: 0}, 1022 | {s: "-99999999999999999999", n: 0}, 1023 | } 1024 | 1025 | for _, test := range tests { 1026 | x := decodeInt64(test.s) 1027 | 1028 | assert.Equal(t, test.n, x, "wrong number") 1029 | } 1030 | } 1031 | 1032 | func TestEscapeString(t *testing.T) { 1033 | tests := []struct { 1034 | s string 1035 | }{ 1036 | {s: `"`}, 1037 | {s: ` `}, 1038 | {s: `"""\\\\\"""\'\" \\\""|"|"|"|\\'\dasd' |"|\\\\'\\\|||\\'"`}, 1039 | {s: `OZON\vshpilevoy`}, 1040 | } 1041 | 1042 | out := make([]byte, 0, 0) 1043 | for _, test := range tests { 1044 | out = escapeString(out[:0], test.s) 1045 | assert.Equal(t, string(strconv.AppendQuote(nil, test.s)), string(out), "wrong escaping") 1046 | } 1047 | } 1048 | 1049 | func TestIndex(t *testing.T) { 1050 | node := Node{} 1051 | 1052 | index := 5 1053 | node.setIndex(5) 1054 | 1055 | assert.Equal(t, index, node.getIndex(), "wrong index") 1056 | } 1057 | --------------------------------------------------------------------------------