├── .buildkite ├── hooks │ └── post-checkout ├── pipeline.yml ├── pull-requests.json └── scripts │ └── test.sh ├── .github ├── CODEOWNERS └── workflows │ └── catalog-info.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── array.go ├── basetype_string.go ├── bench ├── bench_json_decode_test.go ├── encoder.go └── files │ ├── filebeat_events.json │ ├── metricbeat_events.json │ └── packetbeat_events.json ├── catalog-info.yaml ├── cborl ├── cborl_test.go ├── decode.go ├── defs.go ├── error.go ├── parse.go ├── stack.go └── visitor.go ├── go.mod ├── go.sum ├── gotype ├── 0gen.go ├── defs.go ├── error.go ├── fold.go ├── fold_arr.go ├── fold_inline.go ├── fold_map.go ├── fold_map_inline.generated.go ├── fold_map_inline.yml ├── fold_opts.go ├── fold_primitives.go ├── fold_refl_sel.generated.go ├── fold_refl_sel.yml ├── fold_reflect.go ├── fold_test.go ├── fold_user.go ├── gotypes_test.go ├── stacks.generated.go ├── stacks.yml ├── symbols.go ├── tags.go ├── types.yml ├── unfold.go ├── unfold_arr.generated.go ├── unfold_arr.yml ├── unfold_err.generated.go ├── unfold_err.yml ├── unfold_ignore.generated.go ├── unfold_ignore.yml ├── unfold_lookup.go ├── unfold_lookup_go.generated.go ├── unfold_lookup_go.yml ├── unfold_map.generated.go ├── unfold_map.yml ├── unfold_opts.go ├── unfold_primitive.generated.go ├── unfold_primitive.yml ├── unfold_refl.generated.go ├── unfold_refl.go ├── unfold_refl.yml ├── unfold_struct.go ├── unfold_templates.yml ├── unfold_test.go ├── unfold_user.go ├── unfold_user_primitive.generated.go ├── unfold_user_primitive.yml ├── unfold_user_processing.generated.go └── unfold_user_processing.yml ├── internal └── unsafe │ └── unsafe.go ├── json ├── decode.go ├── defs.go ├── json.go ├── json_test.go ├── parse.go ├── state_string.go └── visitor.go ├── map.go ├── sftest ├── cases.go ├── sftest.go ├── sftest_test.go └── util.go ├── string.go ├── ubjson ├── decode.go ├── defs.go ├── parse.go ├── stack.go ├── statestep_string.go ├── statetype_string.go ├── ubjson_test.go └── visitor.go ├── visitor.go └── visitors ├── expect_obj.go ├── nilVisitor.go └── stringer.go /.buildkite/hooks/post-checkout: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | checkout_merge() { 6 | local target_branch=$1 7 | local pr_commit=$2 8 | local merge_branch=$3 9 | 10 | if [[ -z "${target_branch}" ]]; then 11 | echo "No pull request target branch" 12 | exit 1 13 | fi 14 | 15 | git fetch -v origin "${target_branch}" 16 | git checkout FETCH_HEAD 17 | echo "Current branch: $(git rev-parse --abbrev-ref HEAD)" 18 | 19 | # create temporal branch to merge the PR with the target branch 20 | git checkout -b ${merge_branch} 21 | echo "New branch created: $(git rev-parse --abbrev-ref HEAD)" 22 | 23 | # set author identity so it can be run git merge 24 | git config user.name "github-merged-pr-post-checkout" 25 | git config user.email "auto-merge@buildkite" 26 | 27 | git merge --no-edit "${BUILDKITE_COMMIT}" || { 28 | local merge_result=$? 29 | echo "Merge failed: ${merge_result}" 30 | git merge --abort 31 | exit ${merge_result} 32 | } 33 | } 34 | 35 | pull_request="${BUILDKITE_PULL_REQUEST:-false}" 36 | 37 | if [[ "${pull_request}" == "false" ]]; then 38 | echo "Not a pull request, skipping" 39 | exit 0 40 | fi 41 | 42 | TARGET_BRANCH="${BUILDKITE_PULL_REQUEST_BASE_BRANCH:-master}" 43 | PR_COMMIT="${BUILDKITE_COMMIT}" 44 | PR_ID=${BUILDKITE_PULL_REQUEST} 45 | MERGE_BRANCH="pr_merge_${PR_ID}" 46 | 47 | checkout_merge "${TARGET_BRANCH}" "${PR_COMMIT}" "${MERGE_BRANCH}" 48 | 49 | echo "Commit information" 50 | git --no-pager log --format=%B -n 1 51 | 52 | # Ensure buildkite groups are rendered 53 | echo "" 54 | -------------------------------------------------------------------------------- /.buildkite/pipeline.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://raw.githubusercontent.com/buildkite/pipeline-schema/main/schema.json 2 | 3 | steps: 4 | - group: "Test matrix. Linux" 5 | steps: 6 | - label: ":linux: Test matrix. Go {{matrix.go_version}}" 7 | key: test-matrix-lin 8 | matrix: 9 | setup: 10 | go_version: 11 | - "1.15.6" 12 | - "1.16.2" 13 | command: 14 | - ".buildkite/scripts/test.sh" 15 | env: 16 | GO_VERSION: "{{matrix.go_version}}" 17 | agents: 18 | image: golang:{{matrix.go_version}} 19 | cpu: "8" 20 | memory: "4G" 21 | artifact_paths: 22 | - "build/junit-*.xml" 23 | 24 | - label: ":junit: Junit annotate" 25 | plugins: 26 | - junit-annotate#v2.4.1: 27 | artifacts: "*.xml" 28 | fail-build-on-error: true 29 | agents: 30 | provider: "gcp" 31 | depends_on: 32 | - step: "test-matrix-lin" 33 | allow_failure: true 34 | -------------------------------------------------------------------------------- /.buildkite/pull-requests.json: -------------------------------------------------------------------------------- 1 | { 2 | "jobs": [ 3 | { 4 | "enabled": true, 5 | "pipelineSlug": "go-structform", 6 | "allow_org_users": true, 7 | "allowed_repo_permissions": ["admin", "write"], 8 | "allowed_list": [ ], 9 | "set_commit_status": true, 10 | "build_on_commit": true, 11 | "build_on_comment": true, 12 | "trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))|^/test$", 13 | "always_trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))|^/test$", 14 | "skip_ci_labels": [ ], 15 | "skip_target_branches": [ ], 16 | "skip_ci_on_only_changed": [ ], 17 | "always_require_ci_on_changed": [ ] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.buildkite/scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | echo "--- go get for ${GO_VERSION}" 5 | go get -v -t ./... 6 | 7 | echo "--- go test for ${GO_VERSION}" 8 | set +e 9 | export OUT_FILE="build/test-report.out" 10 | mkdir -p build 11 | go test -v -race ./... 2>&1 | tee ${OUT_FILE} 12 | status=$? 13 | 14 | go get -v -u github.com/jstemmer/go-junit-report 15 | go-junit-report > "build/junit-${GO_VERSION}.xml" < ${OUT_FILE} 16 | 17 | exit ${status} 18 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # GitHub CODEOWNERS definition 2 | # See: https://help.github.com/articles/about-codeowners/ 3 | * @elastic/elastic-agent-data-plane -------------------------------------------------------------------------------- /.github/workflows/catalog-info.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: catalog-info 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - main 8 | paths: 9 | - 'catalog-info.yaml' 10 | 11 | jobs: 12 | validate: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: read 16 | packages: read 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | - uses: elastic/oblt-actions/elastic/validate-catalog@v1 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | This project adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | ## [Unreleased] 6 | 7 | ### Added 8 | 9 | ### Changed 10 | 11 | ### Deprecated 12 | 13 | ### Removed 14 | 15 | ### Fixed 16 | 17 | ## [0.0.10] 18 | 19 | ### Changed 20 | - Remove duplicate fields to avoid unnecessary struct grows. (PR #38) 21 | - Reorder elements in structs to reduce memory. (PR #37) 22 | 23 | ### Fixed 24 | - Fix to break out of endless loop. (PR #36) 25 | 26 | ## [0.0.9] 27 | 28 | ### Added 29 | - Added `IsZeroer` interface to allow custom types to report that they are not initialized. A structs field is not serialized if the `omitempty` struct tag is set and `IsZero()` returns true. #32 30 | 31 | ### Fixed 32 | - Ensure `Fold` can be called when a value is given by value, but the `Folder` interface is implemented on the pointer type. #32 33 | 34 | ## [0.0.8] 35 | 36 | ### Added 37 | 38 | - Add optional support to JSON encoder to encode a NaN or Inf floating point value to null. (PR #28) 39 | 40 | ## [0.0.7] 41 | 42 | ### Fixed 43 | 44 | - Fix potential use after free in string and []byte conversions. (PR #21) 45 | 46 | ## [0.0.6] 47 | 48 | ### Added 49 | - Regenerate code with new stringer. (PR #9) 50 | - Add support for custom unfolders when generating gotype.Unfolder. (PR #14, PR #15, PR #17) 51 | - Add go.mod file 52 | 53 | ## [0.0.5] 54 | 55 | ### Added 56 | - Add Reset to gotype.Unfolder. (PR #7) 57 | 58 | ## [0.0.4] 59 | 60 | ### Added 61 | - Add SetEscapeHTML to json visitor. (PR #4) 62 | 63 | ## [0.0.3] 64 | 65 | ### Added 66 | - Add `visitors.NilVisitor`. (Commit ab1cb2d) 67 | 68 | ### Changed 69 | - Replace code generator with mktmlp (github.com/urso/mktmpl). (Commit 0356386) 70 | - Introduce custom number parser. (Commit 41308dd) 71 | 72 | ### Fixed 73 | - Fix gc failures by removing region allocator for temporary objects in decoder. Decoding into `map[string]X` with `X` being a custom go struct will require an extra alloc by now. (Commit 9b12176) 74 | - Fix invalid cast on pointer math. (Commit ea18344) 75 | 76 | ## [0.0.2] 77 | 78 | ### Added 79 | - Add struct tag option ",omitempty". 80 | - Add StringConvVisitor converting all primitive values to strings. 81 | - Move and export object visitor into visitors package 82 | 83 | ### Fixed 84 | - Fix invalid pointer indirections in struct to array/map. 85 | 86 | [Unreleased]: https://github.com/elastic/go-structform/compare/v0.0.10...HEAD 87 | [0.0.10]: https://github.com/elastic/go-structform/compare/v0.0.9...v0.0.10 88 | [0.0.9]: https://github.com/elastic/go-structform/compare/v0.0.8...v0.0.9 89 | [0.0.8]: https://github.com/elastic/go-structform/compare/v0.0.7...v0.0.8 90 | [0.0.7]: https://github.com/elastic/go-structform/compare/v0.0.6...v0.0.7 91 | [0.0.6]: https://github.com/elastic/go-structform/compare/v0.0.5...v0.0.6 92 | [0.0.5]: https://github.com/elastic/go-structform/compare/v0.0.4...v0.0.5 93 | [0.0.4]: https://github.com/elastic/go-structform/compare/v0.0.3...v0.0.4 94 | [0.0.3]: https://github.com/elastic/go-structform/compare/v0.0.2...v0.0.3 95 | [0.0.2]: https://github.com/elastic/go-structform/compare/v0.0.1...v0.0.2 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://beats-ci.elastic.co/job/Library/job/go-structform-mbp/job/master/badge/icon)](https://beats-ci.elastic.co/job/Library/job/go-structform-mbp/job/master/) 2 | # Go-Structform - Structured Formatters 3 | 4 | go-structform provides capabilities for serializing, desirializing, and 5 | transcoding structured data efficiently and generically. 6 | 7 | The top-level package provides the common layer by which serializers and 8 | deserializers interact with each other. Serializers convert the input into a 9 | stream of events by calling methods on the 10 | [Visitor](https://pkg.go.dev/github.com/elastic/go-structform?tab=doc#Visitor) 11 | interfaces. The Deserializers implement the Visitor interface to convert the 12 | stream of events into the target structure. 13 | 14 | A type implementing the `Visitor` interface, but forwards calls to another 15 | visitor is called a Transformer or Filter. Transformers/Filters can manipulate 16 | the data stream or create/collect errors if some wanted validation fails. 17 | 18 | ## Examples 19 | 20 | Transcode a stream of JSON objects into a stream of CBOR objects: 21 | 22 | ``` 23 | func TranscodeJSON2CBOR(out io.Writer, in io.Reader) (int64, error) { 24 | bytesCount, err := json.ParseReader(in, cborl.NewVisitor(out)) 25 | return bytesCount, err 26 | } 27 | ``` 28 | 29 | Parse a stream of JSON objects: 30 | 31 | ``` 32 | var in io.Reader = ... 33 | visitor, _ := gotype.NewUnfolder(nil) 34 | dec := json.NewDecoder(in, 2048, visitor) 35 | for { 36 | var to interface{} 37 | visitor.SetTarget(&to) 38 | if err := dec.Next(); err != nil { 39 | return err 40 | } 41 | 42 | // process `to` 43 | } 44 | ``` 45 | 46 | Encode a channel of go `map[string]interface{}` to a stream of cbor objects: 47 | 48 | ``` 49 | var out io.Writer = ... 50 | var objects chan map[string]interface{} = ... 51 | 52 | visitor := cborl.NewVisitor(out) 53 | it, _ := gotype.NewIterator(visitor) 54 | for _, obj := range objects { 55 | if err := it.Fold(obj); err != nil { 56 | return err 57 | } 58 | } 59 | 60 | return nil 61 | ``` 62 | 63 | Convert between go types: 64 | 65 | ``` 66 | var to map[string]int 67 | visitor, _ := gotype.NewUnfolder(&to) 68 | it, _ := gotype.NewIterator(visitor) 69 | 70 | st := struct { 71 | A int `struct:"a"` 72 | B string `struct:",omit"` 73 | }{A: 1, B: "hello"} 74 | if err := it.Fold(st); err != nil { 75 | return err 76 | } 77 | 78 | // to == map[string]int{"a": 1} 79 | ``` 80 | 81 | Use custom folder and unfolder for existing go types: 82 | 83 | ``` 84 | type durationUnfolder struct { 85 | gotype.BaseUnfoldState // Use BaseUnfoldState to create errors for methods not implemented 86 | to *time.Duration 87 | } 88 | 89 | func foldDuration(in *time.Duration, v structform.ExtVisitor) error { 90 | return v.OnString(in.String()) 91 | } 92 | 93 | func unfoldDuration(to *time.Duration) gotype.UnfoldState { 94 | return &durationUnfolder{to: to} 95 | } 96 | 97 | type durationUnfolder struct { 98 | gotype.BaseUnfoldState // Use BaseUnfoldState to create errors for methods not implemented 99 | to *time.Duration 100 | } 101 | 102 | func (u *durationUnfolder) OnString(_ gotype.UnfoldCtx, str string) error { 103 | d, err := time.ParseDuration(str) 104 | if err == nil { 105 | *u.to = d 106 | } 107 | return err 108 | } 109 | 110 | 111 | ... 112 | 113 | 114 | visitor, _ := gotype.NewUnfolder(nil, gotype.Unfolders( 115 | unfoldDuration, 116 | )) 117 | it, _ := gotype.NewIterator(visitor, gotype.Folders( 118 | foldDuration, 119 | )) 120 | 121 | // store duration in temporary value 122 | var tmp interface{} 123 | visitor.SetTarget(&tmp) 124 | it.Fold(5 * time.Minute) 125 | 126 | // restore duration from temporary value 127 | var dur time.Duration 128 | visitor.SetTarget(&dur) 129 | it.Fold(tmp) 130 | 131 | // dur == 5 * time.Minute 132 | ``` 133 | 134 | ## Data Model 135 | 136 | The data model describes by which data types Serializers and Deserializers 137 | interact. In this sense the data model provides a simplified type system, that supports a subset of 138 | serializable go types (for example channels or function pointers can not be serialized). 139 | 140 | Go-structform provides a simplified, common data model via the [Visitor](https://pkg.go.dev/github.com/elastic/go-structform?tab=doc#Visitor) interface. 141 | 142 | ### Types 143 | 144 | - **primitives** (See [ValueVisitor](https://pkg.go.dev/github.com/elastic/go-structform?tab=doc#ValueVisitor) interface): `Bool`, `Byte`, `String`, `Int`, `Int8/16/32/64`, `Uint`, `Uint8/16/32/64`, `Float32`, `Float64`, untyped `Nil` 145 | - **compound**: objects ([ObjectVisitor](https://pkg.go.dev/github.com/elastic/go-structform?tab=doc#ObjectVisitor)), arrays ([ArrayVisitor](https://pkg.go.dev/github.com/elastic/go-structform?tab=doc#ArrayVisitor)) 146 | 147 | ## Extended Data Model 148 | 149 | The extended data model provides support for similar types to the `Visitor` 150 | interface, but provides a number of optimizations, allowing users to pass a set 151 | of common go values directly without having to serialize and deserialize those 152 | values. 153 | 154 | The extended data model is provided by the 155 | [`ExtVisitor`](https://pkg.go.dev/github.com/elastic/go-structform?tab=doc#ExtVisitor) 156 | interface. 157 | 158 | All features in the Extended Data Model can be seamlessly converted to the Data 159 | Model provided by the `Visitor interface. 160 | 161 | Deserializers supporting the Extended Data Model must always implement the 162 | common Data Model as is required by the `Visitor` interface. 163 | 164 | Serializers wanting to interact with the Extended Data Model should still 165 | accept the `Visitor` interface only and use `EnsureExtVisitor` in order to create an `ExtVisitor`. 166 | `EnsureExtVisitor` wraps the `Visitor` if necessarry, allowing the `Visitor` to 167 | implement only a subset of features in the Extended Data Model. 168 | 169 | - **extended primitives**: The Visitor adds support for `[]byte` values as 170 | strings. The value must be consumed immediately or copied, as the buffer is 171 | not guaranteed to be stable. 172 | - **slices**: The visitor adds support for slice types, for each supported 173 | primitive type. For example `[]int8/16/32/64` can be passed as is. 174 | - **map**: The visitor adds support for `map[string]T` types, for each 175 | supported primitive type. For example `map[string]int8/16/32/64`. 176 | 177 | 178 | ## Data Formats 179 | 180 | - JSON: the `json` package provides a JSON parser and JSON serializer. The serializer implements a subset of `ExtVisitor`. 181 | - UBJSON: the `ubjson` packages provides a parser and serializer for Universal Binary JSON. 182 | - CBOR: the `cborl` package supports a compatible subset of CBOR (for example object keys must be strings). 183 | - Go Types: the `gotype` package provides a `Folder` to convert go values into 184 | a stream of events and an `Unfolder` to apply a stream of events to go 185 | values. 186 | -------------------------------------------------------------------------------- /array.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package structform 19 | 20 | type extArrVisitor struct { 21 | Visitor 22 | } 23 | 24 | func (ev extArrVisitor) OnStringArray(a []string) error { 25 | if err := ev.OnArrayStart(len(a), StringType); err != nil { 26 | return err 27 | } 28 | for _, v := range a { 29 | if err := ev.OnString(v); err != nil { 30 | return err 31 | } 32 | } 33 | return ev.OnArrayFinished() 34 | } 35 | 36 | func (ev extArrVisitor) OnBoolArray(a []bool) error { 37 | if err := ev.OnArrayStart(len(a), BoolType); err != nil { 38 | return err 39 | } 40 | for _, v := range a { 41 | if err := ev.OnBool(v); err != nil { 42 | return err 43 | } 44 | } 45 | return ev.OnArrayFinished() 46 | } 47 | 48 | func (ev extArrVisitor) OnInt8Array(a []int8) error { 49 | if err := ev.OnArrayStart(len(a), Int8Type); err != nil { 50 | return err 51 | } 52 | for _, v := range a { 53 | if err := ev.OnInt8(v); err != nil { 54 | return err 55 | } 56 | } 57 | return ev.OnArrayFinished() 58 | } 59 | 60 | func (ev extArrVisitor) OnInt16Array(a []int16) error { 61 | if err := ev.OnArrayStart(len(a), Int16Type); err != nil { 62 | return err 63 | } 64 | for _, v := range a { 65 | if err := ev.OnInt16(v); err != nil { 66 | return err 67 | } 68 | } 69 | return ev.OnArrayFinished() 70 | } 71 | 72 | func (ev extArrVisitor) OnInt32Array(a []int32) error { 73 | if err := ev.OnArrayStart(len(a), Int32Type); err != nil { 74 | return err 75 | } 76 | for _, v := range a { 77 | if err := ev.OnInt32(v); err != nil { 78 | return err 79 | } 80 | } 81 | return ev.OnArrayFinished() 82 | } 83 | 84 | func (ev extArrVisitor) OnInt64Array(a []int64) error { 85 | if err := ev.OnArrayStart(len(a), Int64Type); err != nil { 86 | return err 87 | } 88 | for _, v := range a { 89 | if err := ev.OnInt64(v); err != nil { 90 | return err 91 | } 92 | } 93 | return ev.OnArrayFinished() 94 | } 95 | 96 | func (ev extArrVisitor) OnIntArray(a []int) error { 97 | if err := ev.OnArrayStart(len(a), IntType); err != nil { 98 | return err 99 | } 100 | for _, v := range a { 101 | if err := ev.OnInt(v); err != nil { 102 | return err 103 | } 104 | } 105 | return ev.OnArrayFinished() 106 | } 107 | 108 | func (ev extArrVisitor) OnBytes(b []byte) error { 109 | if err := ev.OnArrayStart(len(b), ByteType); err != nil { 110 | return err 111 | } 112 | for _, v := range b { 113 | if err := ev.OnByte(v); err != nil { 114 | return err 115 | } 116 | } 117 | return ev.OnArrayFinished() 118 | } 119 | 120 | func (ev extArrVisitor) OnUint8Array(a []uint8) error { 121 | if err := ev.OnArrayStart(len(a), Uint8Type); err != nil { 122 | return err 123 | } 124 | for _, v := range a { 125 | if err := ev.OnUint8(v); err != nil { 126 | return err 127 | } 128 | } 129 | return ev.OnArrayFinished() 130 | } 131 | 132 | func (ev extArrVisitor) OnUint16Array(a []uint16) error { 133 | if err := ev.OnArrayStart(len(a), Uint16Type); err != nil { 134 | return err 135 | } 136 | for _, v := range a { 137 | if err := ev.OnUint16(v); err != nil { 138 | return err 139 | } 140 | } 141 | return ev.OnArrayFinished() 142 | } 143 | 144 | func (ev extArrVisitor) OnUint32Array(a []uint32) error { 145 | if err := ev.OnArrayStart(len(a), Uint32Type); err != nil { 146 | return err 147 | } 148 | for _, v := range a { 149 | if err := ev.OnUint32(v); err != nil { 150 | return err 151 | } 152 | } 153 | return ev.OnArrayFinished() 154 | } 155 | 156 | func (ev extArrVisitor) OnUint64Array(a []uint64) error { 157 | if err := ev.OnArrayStart(len(a), Uint64Type); err != nil { 158 | return err 159 | } 160 | for _, v := range a { 161 | if err := ev.OnUint64(v); err != nil { 162 | return err 163 | } 164 | } 165 | return ev.OnArrayFinished() 166 | } 167 | 168 | func (ev extArrVisitor) OnUintArray(a []uint) error { 169 | if err := ev.OnArrayStart(len(a), UintType); err != nil { 170 | return err 171 | } 172 | for _, v := range a { 173 | if err := ev.OnUint(v); err != nil { 174 | return err 175 | } 176 | } 177 | return ev.OnArrayFinished() 178 | } 179 | 180 | func (ev extArrVisitor) OnFloat32Array(a []float32) error { 181 | if err := ev.OnArrayStart(len(a), Float32Type); err != nil { 182 | return err 183 | } 184 | for _, v := range a { 185 | if err := ev.OnFloat32(v); err != nil { 186 | return err 187 | } 188 | } 189 | return ev.OnArrayFinished() 190 | } 191 | 192 | func (ev extArrVisitor) OnFloat64Array(a []float64) error { 193 | if err := ev.OnArrayStart(len(a), Float64Type); err != nil { 194 | return err 195 | } 196 | for _, v := range a { 197 | if err := ev.OnFloat64(v); err != nil { 198 | return err 199 | } 200 | } 201 | return ev.OnArrayFinished() 202 | } 203 | -------------------------------------------------------------------------------- /basetype_string.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | // Code generated by "stringer -type=BaseType"; DO NOT EDIT. 19 | 20 | package structform 21 | 22 | import "strconv" 23 | 24 | const _BaseType_name = "AnyTypeByteTypeStringTypeBoolTypeZeroTypeIntTypeInt8TypeInt16TypeInt32TypeInt64TypeUintTypeUint8TypeUint16TypeUint32TypeUint64TypeFloat32TypeFloat64Type" 25 | 26 | var _BaseType_index = [...]uint8{0, 7, 15, 25, 33, 41, 48, 56, 65, 74, 83, 91, 100, 110, 120, 130, 141, 152} 27 | 28 | func (i BaseType) String() string { 29 | if i >= BaseType(len(_BaseType_index)-1) { 30 | return "BaseType(" + strconv.FormatInt(int64(i), 10) + ")" 31 | } 32 | return _BaseType_name[_BaseType_index[i]:_BaseType_index[i+1]] 33 | } 34 | -------------------------------------------------------------------------------- /bench/encoder.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //+build compare 19 | 20 | package bench 21 | 22 | import ( 23 | "bytes" 24 | stdjson "encoding/json" 25 | "io" 26 | 27 | jsoniter "github.com/json-iterator/go" 28 | "github.com/ugorji/go/codec" 29 | 30 | "github.com/elastic/go-structform/cborl" 31 | "github.com/elastic/go-structform/gotype" 32 | "github.com/elastic/go-structform/json" 33 | "github.com/elastic/go-structform/ubjson" 34 | ) 35 | 36 | type encoderFactory func(io.Writer) func(interface{}) error 37 | type decoderFactory func([]byte) func(interface{}) error 38 | type transcodeFactory func(io.Writer) func([]byte) error 39 | 40 | func stdJSONEncoder(w io.Writer) func(interface{}) error { 41 | enc := stdjson.NewEncoder(w) 42 | return enc.Encode 43 | } 44 | 45 | func stdJSONDecoder(r io.Reader) func(interface{}) error { 46 | dec := stdjson.NewDecoder(r) 47 | return dec.Decode 48 | } 49 | 50 | func stdJSONBufDecoder(b []byte) func(interface{}) error { 51 | return stdJSONDecoder(bytes.NewReader(b)) 52 | } 53 | 54 | func gocodecJSONDecoder(r io.Reader) func(interface{}) error { 55 | h := &codec.JsonHandle{} 56 | dec := codec.NewDecoder(r, h) 57 | return dec.Decode 58 | } 59 | 60 | func jsoniterDecoder(r io.Reader) func(interface{}) error { 61 | dec := jsoniter.ConfigCompatibleWithStandardLibrary.NewDecoder(r) 62 | return func(v interface{}) error { 63 | if !dec.More() { 64 | return io.EOF 65 | } 66 | return dec.Decode(v) 67 | } 68 | } 69 | 70 | func jsoniterBufDecoder(b []byte) func(interface{}) error { 71 | return jsoniterDecoder(bytes.NewReader(b)) 72 | } 73 | 74 | func structformJSONEncoder(w io.Writer) func(interface{}) error { 75 | vs := json.NewVisitor(w) 76 | folder, _ := gotype.NewIterator(vs) 77 | return folder.Fold 78 | } 79 | 80 | func structformUBJSONEncoder(w io.Writer) func(interface{}) error { 81 | vs := ubjson.NewVisitor(w) 82 | folder, _ := gotype.NewIterator(vs) 83 | return folder.Fold 84 | } 85 | 86 | func structformCBORLEncoder(w io.Writer) func(interface{}) error { 87 | vs := cborl.NewVisitor(w) 88 | folder, _ := gotype.NewIterator(vs) 89 | return folder.Fold 90 | } 91 | 92 | func structformJSONBufDecoder(keyCache int) func([]byte) func(interface{}) error { 93 | return func(b []byte) func(interface{}) error { 94 | u, _ := gotype.NewUnfolder(nil) 95 | dec := json.NewBytesDecoder(b, u) 96 | return makeStructformDecoder(u, dec.Next, keyCache) 97 | } 98 | } 99 | 100 | func structformUBJSONBufDecoder(keyCache int) func([]byte) func(interface{}) error { 101 | return func(b []byte) func(interface{}) error { 102 | u, _ := gotype.NewUnfolder(nil) 103 | dec := ubjson.NewBytesDecoder(b, u) 104 | return makeStructformDecoder(u, dec.Next, keyCache) 105 | } 106 | } 107 | 108 | func structformCBORLBufDecoder(keyCache int) func([]byte) func(interface{}) error { 109 | return func(b []byte) func(interface{}) error { 110 | u, _ := gotype.NewUnfolder(nil) 111 | dec := cborl.NewBytesDecoder(b, u) 112 | return makeStructformDecoder(u, dec.Next, keyCache) 113 | } 114 | } 115 | 116 | func makeStructformDecoder( 117 | u *gotype.Unfolder, 118 | next func() error, 119 | keyCache int, 120 | ) func(interface{}) error { 121 | if keyCache > 0 { 122 | u.EnableKeyCache(keyCache) 123 | } 124 | return func(v interface{}) error { 125 | if err := u.SetTarget(v); err != nil { 126 | return err 127 | } 128 | return next() 129 | } 130 | } 131 | 132 | func makeCBORL2JSONTranscoder(w io.Writer) func([]byte) error { 133 | j := json.NewVisitor(w) 134 | p := cborl.NewParser(j) 135 | return p.Parse 136 | } 137 | 138 | func makeUBJSON2JSONTranscoder(w io.Writer) func([]byte) error { 139 | j := json.NewVisitor(w) 140 | p := ubjson.NewParser(j) 141 | return p.Parse 142 | } 143 | -------------------------------------------------------------------------------- /catalog-info.yaml: -------------------------------------------------------------------------------- 1 | # Declare a Backstage Component that represents your application. 2 | --- 3 | # yaml-language-server: $schema=https://json.schemastore.org/catalog-info.json 4 | apiVersion: backstage.io/v1alpha1 5 | kind: Component 6 | metadata: 7 | name: go-structform 8 | 9 | spec: 10 | type: library 11 | owner: group:ingest-fp 12 | system: platform-ingest 13 | lifecycle: production 14 | 15 | --- 16 | # yaml-language-server: $schema=https://gist.githubusercontent.com/elasticmachine/988b80dae436cafea07d9a4a460a011d/raw/rre.schema.json 17 | apiVersion: backstage.io/v1alpha1 18 | kind: Resource 19 | metadata: 20 | name: buildkite-pipeline-go-structform 21 | description: Buildkite pipeline for the go-structform project 22 | links: 23 | - title: Pipeline 24 | url: https://buildkite.com/elastic/go-structform 25 | 26 | spec: 27 | type: buildkite-pipeline 28 | owner: group:ingest-fp 29 | system: platform-ingest 30 | implementation: 31 | apiVersion: buildkite.elastic.dev/v1 32 | kind: Pipeline 33 | metadata: 34 | name: go-structform 35 | description: Buildkite pipeline for the go-structform project 36 | spec: 37 | repository: elastic/go-structform 38 | pipeline_file: ".buildkite/pipeline.yml" 39 | maximum_timeout_in_minutes: 60 40 | provider_settings: 41 | build_pull_request_forks: false 42 | build_pull_requests: true # requires filter_enabled and filter_condition settings as below when used with buildkite-pr-bot 43 | build_tags: true 44 | filter_enabled: true 45 | filter_condition: >- 46 | build.pull_request.id == null || (build.creator.name == 'elasticmachine' && build.pull_request.id != null) 47 | cancel_intermediate_builds: true 48 | cancel_intermediate_builds_branch_filter: '!main' 49 | skip_intermediate_builds: true 50 | skip_intermediate_builds_branch_filter: '!main' 51 | env: 52 | ELASTIC_PR_COMMENTS_ENABLED: 'true' 53 | teams: 54 | ingest-fp: 55 | access_level: MANAGE_BUILD_AND_READ 56 | ingest-eng-prod: 57 | access_level: MANAGE_BUILD_AND_READ 58 | everyone: 59 | access_level: READ_ONLY 60 | -------------------------------------------------------------------------------- /cborl/cborl_test.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package cborl 19 | 20 | import ( 21 | "bytes" 22 | "testing" 23 | 24 | structform "github.com/elastic/go-structform" 25 | "github.com/elastic/go-structform/sftest" 26 | ) 27 | 28 | func TestEncParseConsistent(t *testing.T) { 29 | testEncParseConsistent(t, Parse) 30 | } 31 | 32 | func TestEncDecoderConsistent(t *testing.T) { 33 | testEncParseConsistent(t, func(content []byte, to structform.Visitor) error { 34 | dec := NewBytesDecoder(content, to) 35 | return dec.Next() 36 | }) 37 | } 38 | 39 | func TestEncParseBytesConsistent(t *testing.T) { 40 | testEncParseConsistent(t, func(content []byte, to structform.Visitor) error { 41 | p := NewParser(to) 42 | for _, b := range content { 43 | err := p.feed([]byte{b}) 44 | if err != nil { 45 | return err 46 | } 47 | } 48 | return nil 49 | }) 50 | } 51 | 52 | func testEncParseConsistent( 53 | t *testing.T, 54 | parse func([]byte, structform.Visitor) error, 55 | ) { 56 | sftest.TestEncodeParseConsistent(t, sftest.Samples, 57 | func() (structform.Visitor, func(structform.Visitor) error) { 58 | buf := bytes.NewBuffer(nil) 59 | vs := NewVisitor(buf) 60 | 61 | return vs, func(to structform.Visitor) error { 62 | return parse(buf.Bytes(), to) 63 | } 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /cborl/decode.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package cborl 19 | 20 | import ( 21 | "io" 22 | 23 | structform "github.com/elastic/go-structform" 24 | ) 25 | 26 | type Decoder struct { 27 | in io.Reader 28 | buffer []byte 29 | buffer0 []byte 30 | 31 | p Parser 32 | } 33 | 34 | func NewDecoder(in io.Reader, buffer int, vs structform.Visitor) *Decoder { 35 | dec := &Decoder{ 36 | buffer0: make([]byte, buffer), 37 | in: in, 38 | } 39 | dec.p.init(vs) 40 | return dec 41 | } 42 | 43 | func NewBytesDecoder(b []byte, vs structform.Visitor) *Decoder { 44 | dec := &Decoder{ 45 | buffer: b, 46 | buffer0: b[:0], 47 | in: nil, 48 | } 49 | dec.p.init(vs) 50 | return dec 51 | } 52 | 53 | func (dec *Decoder) Next() error { 54 | var ( 55 | n int 56 | err error 57 | reported bool 58 | ) 59 | 60 | for !reported { 61 | if len(dec.buffer) == 0 { 62 | if dec.in == nil { 63 | return io.EOF 64 | } 65 | 66 | n, err := dec.in.Read(dec.buffer0) 67 | dec.buffer = dec.buffer0[:n] 68 | if err != nil { 69 | return err 70 | } 71 | } 72 | 73 | n, reported, err = dec.p.feedUntil(dec.buffer) 74 | if err != nil { 75 | return err 76 | } 77 | 78 | dec.buffer = dec.buffer[n:] 79 | if reported { 80 | return nil 81 | } 82 | } 83 | 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /cborl/defs.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package cborl 19 | 20 | import "github.com/elastic/go-structform/internal/unsafe" 21 | 22 | const ( 23 | majorUint uint8 = 0x00 24 | majorNeg uint8 = 1 << 5 25 | majorBytes uint8 = 2 << 5 26 | majorText uint8 = 3 << 5 27 | majorArr uint8 = 4 << 5 28 | majorMap uint8 = 5 << 5 29 | majorTag uint8 = 6 << 5 30 | majorOther uint8 = 7 << 5 31 | 32 | majorMask uint8 = 7 << 5 33 | minorMask uint8 = ^majorMask 34 | ) 35 | 36 | const ( 37 | lenSmall uint8 = 0 38 | len8b uint8 = 24 39 | len16b uint8 = 25 40 | len32b uint8 = 26 41 | len64b uint8 = 27 42 | lenIndef uint8 = 31 43 | ) 44 | 45 | const ( 46 | codeFalse uint8 = 20 | majorOther 47 | codeTrue uint8 = 21 | majorOther 48 | codeNull uint8 = 22 | majorOther 49 | codeUndef uint8 = 23 | majorOther 50 | 51 | codeHalfFloat uint8 = 25 | majorOther 52 | codeSingleFloat uint8 = 26 | majorOther 53 | codeDoubleFloat uint8 = 27 | majorOther 54 | codeBreak uint8 = lenIndef | majorOther 55 | ) 56 | 57 | func str2Bytes(s string) []byte { 58 | return unsafe.Str2Bytes(s) 59 | } 60 | 61 | func bytes2Str(b []byte) string { 62 | return unsafe.Bytes2Str(b) 63 | } 64 | -------------------------------------------------------------------------------- /cborl/error.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package cborl 19 | 20 | import "errors" 21 | 22 | func errTODO() error { 23 | err := errors.New("TODO") 24 | panic(err) 25 | } 26 | 27 | var errInvalidCode = errors.New("invalid type code") 28 | var errTextKeyRequired = errors.New("only text keys supported") 29 | var errIndefByteSeq = errors.New("text/bytes of indefinite length not supported") 30 | var errEmptyKey = errors.New("object keys must not be empty") 31 | -------------------------------------------------------------------------------- /cborl/stack.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package cborl 19 | 20 | type stateStack struct { 21 | stack []state // state stack for nested arrays/objects 22 | stack0 [64]state 23 | current state 24 | } 25 | 26 | type lengthStack struct { 27 | stack []int64 28 | stack0 [32]int64 29 | current int64 30 | } 31 | 32 | func (s *stateStack) init(s0 state) { 33 | s.current = s0 34 | s.stack = s.stack0[:0] 35 | } 36 | 37 | func (s *stateStack) push(next state) { 38 | if s.current.major != stFail { 39 | s.stack = append(s.stack, s.current) 40 | } 41 | s.current = next 42 | } 43 | 44 | func (s *stateStack) pop() { 45 | if len(s.stack) == 0 { 46 | s.current = state{stFail, stStart} 47 | } else { 48 | last := len(s.stack) - 1 49 | s.current = s.stack[last] 50 | s.stack = s.stack[:last] 51 | } 52 | } 53 | 54 | func (s *lengthStack) init() { 55 | s.stack = s.stack0[:0] 56 | } 57 | 58 | func (s *lengthStack) push(l int64) { 59 | s.stack = append(s.stack, s.current) 60 | s.current = l 61 | } 62 | 63 | func (s *lengthStack) pop() int64 { 64 | if len(s.stack) == 0 { 65 | s.current = -1 66 | return -1 67 | } else { 68 | last := len(s.stack) - 1 69 | old := s.current 70 | s.current = s.stack[last] 71 | s.stack = s.stack[:last] 72 | return old 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/elastic/go-structform 2 | 3 | go 1.12 4 | 5 | require github.com/stretchr/testify v1.7.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 6 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 7 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= 10 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 11 | -------------------------------------------------------------------------------- /gotype/0gen.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | //go:generate mktmpl -f -o fold_map_inline.generated.go fold_map_inline.yml 21 | //go:generate mktmpl -f -o fold_refl_sel.generated.go fold_refl_sel.yml 22 | //go:generate mktmpl -f -o stacks.generated.go stacks.yml 23 | //go:generate mktmpl -f -o unfold_primitive.generated.go unfold_primitive.yml 24 | //go:generate mktmpl -f -o unfold_lookup_go.generated.go unfold_lookup_go.yml 25 | //go:generate mktmpl -f -o unfold_err.generated.go unfold_err.yml 26 | //go:generate mktmpl -f -o unfold_arr.generated.go unfold_arr.yml 27 | //go:generate mktmpl -f -o unfold_map.generated.go unfold_map.yml 28 | //go:generate mktmpl -f -o unfold_refl.generated.go unfold_refl.yml 29 | //go:generate mktmpl -f -o unfold_ignore.generated.go unfold_ignore.yml 30 | //go:generate mktmpl -f -o unfold_user_primitive.generated.go unfold_user_primitive.yml 31 | //go:generate mktmpl -f -o unfold_user_processing.generated.go unfold_user_processing.yml 32 | 33 | // go:generate mktmpl -f -o unfold_sel_generated.go unfold_sel.yml 34 | -------------------------------------------------------------------------------- /gotype/defs.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import ( 21 | "reflect" 22 | 23 | structform "github.com/elastic/go-structform" 24 | "github.com/elastic/go-structform/internal/unsafe" 25 | ) 26 | 27 | type options struct { 28 | tag string 29 | } 30 | 31 | var ( 32 | tInterface = reflect.TypeOf((*interface{})(nil)).Elem() 33 | tString = reflect.TypeOf("") 34 | tBool = reflect.TypeOf(true) 35 | tInt = reflect.TypeOf(int(0)) 36 | tInt8 = reflect.TypeOf(int8(0)) 37 | tInt16 = reflect.TypeOf(int16(0)) 38 | tInt32 = reflect.TypeOf(int32(0)) 39 | tInt64 = reflect.TypeOf(int64(0)) 40 | tUint = reflect.TypeOf(uint(0)) 41 | tByte = reflect.TypeOf(byte(0)) 42 | tUint8 = reflect.TypeOf(uint8(0)) 43 | tUint16 = reflect.TypeOf(uint16(0)) 44 | tUint32 = reflect.TypeOf(uint32(0)) 45 | tUint64 = reflect.TypeOf(uint64(0)) 46 | tFloat32 = reflect.TypeOf(float32(0)) 47 | tFloat64 = reflect.TypeOf(float64(0)) 48 | 49 | tError = reflect.TypeOf((*error)(nil)).Elem() 50 | 51 | tExtVisitor = reflect.TypeOf((*structform.ExtVisitor)(nil)).Elem() 52 | tFolder = reflect.TypeOf((*Folder)(nil)).Elem() 53 | tExpander = reflect.TypeOf((*Expander)(nil)).Elem() 54 | tUnfoldState = reflect.TypeOf((*UnfoldState)(nil)).Elem() 55 | tIsZeroer = reflect.TypeOf((*IsZeroer)(nil)).Elem() 56 | ) 57 | 58 | func bytes2Str(b []byte) string { 59 | return unsafe.Bytes2Str(b) 60 | } 61 | 62 | func implementsFolder(t reflect.Type) bool { return t.Implements(tFolder) } 63 | func implementsPtrFolder(t reflect.Type) bool { return implementsFolder(reflect.PtrTo(t)) } 64 | 65 | func implementsIsZeroer(t reflect.Type) bool { return t.Implements(tIsZeroer) } 66 | func implementsPtrIsZeroer(t reflect.Type) bool { return implementsIsZeroer(reflect.PtrTo(t)) } 67 | -------------------------------------------------------------------------------- /gotype/error.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import "errors" 21 | 22 | var ( 23 | errNotInitialized = errors.New("Unfolder is not initialized") 24 | errInvalidState = errors.New("invalid state") 25 | errUnsupported = errors.New("unsupported") 26 | errMapRequiresStringKey = errors.New("map requires string key") 27 | errSquashNeedObject = errors.New("require map or struct when using squash/inline") 28 | errNilInput = errors.New("nil input") 29 | errRequiresPointer = errors.New("requires pointer") 30 | errKeyIntoNonStruct = errors.New("key for non-structure target") 31 | errUnexpectedObjectKey = errors.New("unexpected object key") 32 | errRequiresPrimitive = errors.New("requires primitive target to set a boolean value") 33 | errRequiresBoolReceiver = errors.New("requires bool receiver") 34 | errIncompatibleTypes = errors.New("can not assign to incompatible go type") 35 | errStartArrayWaitingForKey = errors.New("start array while waiting for object field name") 36 | errStartObjectWaitingForKey = errors.New("start object while waiting for object field name") 37 | errExpectedArrayNotObject = errors.New("expected array but received object") 38 | errExpectedObjectNotArray = errors.New("expected object but received array") 39 | errUnexpectedArrayStart = errors.New("unexpected array start") 40 | errUnexpectedObjectStart = errors.New("unexpected object start") 41 | errExpectedObjectKey = errors.New("waiting for object key or object end marker") 42 | errExpectedArray = errors.New("expected array") 43 | errExpectedObject = errors.New("expected object") 44 | errExpectedObjectValue = errors.New("expected object value") 45 | errExpectedObjectClose = errors.New("missing object close") 46 | errInlineAndOmitEmpty = errors.New("inline and omitempty must not be set at the same time") 47 | 48 | errUnexpectedNil = errors.New("unexpected nil value received") 49 | errUnexpectedBool = errors.New("unexpected bool value received") 50 | errUnexpectedNum = errors.New("unexpected numeric value received") 51 | errUnexpectedString = errors.New("unexpected string value received") 52 | errUnexpectedArrayEnd = errors.New("array closed early") 53 | errUnexpectedObjectEnd = errors.New("unexpected object close") 54 | ) 55 | 56 | func errTODO() error { 57 | panic(errors.New("TODO")) 58 | } 59 | 60 | func visitErrTODO(V visitor, v interface{}) error { 61 | return errTODO() 62 | } 63 | -------------------------------------------------------------------------------- /gotype/fold.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import ( 21 | "reflect" 22 | 23 | structform "github.com/elastic/go-structform" 24 | ) 25 | 26 | type foldFn func(c *foldContext, v interface{}) error 27 | 28 | type reFoldFn func(c *foldContext, v reflect.Value) error 29 | 30 | type visitor interface { 31 | structform.ExtVisitor 32 | } 33 | 34 | type Iterator struct { 35 | ctx foldContext 36 | } 37 | 38 | type Folder interface { 39 | Fold(structform.ExtVisitor) error 40 | } 41 | 42 | // IsZeroer interface allows custom types to be reported as empty. 43 | // If the `omitempty` struct tag option is set and the custom type implements 44 | // IsZero(), which returns true, then the field will not be reported. 45 | type IsZeroer interface { 46 | IsZero() bool 47 | } 48 | 49 | type foldContext struct { 50 | visitor 51 | userReg map[reflect.Type]reFoldFn 52 | reg *typeFoldRegistry 53 | opts options 54 | } 55 | 56 | func Fold(v interface{}, vs structform.Visitor, opts ...FoldOption) error { 57 | if it, err := NewIterator(vs, opts...); err == nil { 58 | return it.Fold(v) 59 | } 60 | return nil 61 | } 62 | 63 | func NewIterator(vs structform.Visitor, opts ...FoldOption) (*Iterator, error) { 64 | reg := newTypeFoldRegistry() 65 | O, err := applyFoldOpts(opts) 66 | if err != nil { 67 | return nil, err 68 | } 69 | 70 | var userReg map[reflect.Type]reFoldFn 71 | if O.foldFns != nil { 72 | userReg = map[reflect.Type]reFoldFn{} 73 | for typ, folder := range O.foldFns { 74 | reg.set(typ, folder) 75 | userReg[typ] = folder 76 | } 77 | } 78 | 79 | it := &Iterator{ 80 | ctx: foldContext{ 81 | visitor: structform.EnsureExtVisitor(vs).(visitor), 82 | userReg: userReg, 83 | reg: reg, 84 | opts: options{ 85 | tag: "struct", 86 | }, 87 | }, 88 | } 89 | 90 | return it, nil 91 | } 92 | 93 | func (i *Iterator) Fold(v interface{}) error { 94 | return foldInterfaceValue(&i.ctx, v) 95 | } 96 | 97 | func foldInterfaceValue(C *foldContext, v interface{}) error { 98 | if C.userReg != nil { 99 | t := reflect.TypeOf(v) 100 | if f := C.userReg[t]; f != nil { 101 | return f(C, reflect.ValueOf(v)) 102 | } 103 | } 104 | 105 | if f := getFoldGoTypes(v); f != nil { 106 | return f(C, v) 107 | } 108 | 109 | if f, ok := v.(Folder); ok { 110 | return f.Fold(C.visitor) 111 | } 112 | 113 | if tmp, f := getFoldConvert(v); f != nil { 114 | return f(C, tmp) 115 | } 116 | 117 | return foldAnyReflect(C, reflect.ValueOf(v)) 118 | } 119 | 120 | func getFoldConvert(v interface{}) (interface{}, foldFn) { 121 | t := reflect.TypeOf(v) 122 | cast := false 123 | 124 | switch t.Kind() { 125 | case reflect.Map: 126 | if cast = t.Name() != ""; cast { 127 | mt := reflect.MapOf(t.Key(), t.Elem()) 128 | v = reflect.ValueOf(v).Convert(mt).Interface() 129 | } 130 | case reflect.Slice: 131 | if cast = t.Name() != ""; cast { 132 | mt := reflect.SliceOf(t.Elem()) 133 | v = reflect.ValueOf(v).Convert(mt).Interface() 134 | } 135 | case reflect.Array: 136 | if cast = t.Name() != ""; cast { 137 | mt := reflect.ArrayOf(t.Len(), t.Elem()) 138 | v = reflect.ValueOf(v).Convert(mt).Interface() 139 | } 140 | } 141 | 142 | return v, getFoldGoTypes(v) 143 | } 144 | 145 | func getFoldGoTypes(v interface{}) foldFn { 146 | switch v.(type) { 147 | case nil: 148 | return foldNil 149 | 150 | case bool: 151 | return foldBool 152 | case []bool: 153 | return foldArrBool 154 | case map[string]bool: 155 | return foldMapBool 156 | 157 | case int8: 158 | return foldInt8 159 | case int16: 160 | return foldInt16 161 | case int32: 162 | return foldInt32 163 | case int64: 164 | return foldInt64 165 | case int: 166 | return foldInt 167 | 168 | case []int8: 169 | return foldArrInt8 170 | case []int16: 171 | return foldArrInt16 172 | case []int32: 173 | return foldArrInt32 174 | case []int64: 175 | return foldArrInt64 176 | case []int: 177 | return foldArrInt 178 | 179 | case map[string]int8: 180 | return foldMapInt8 181 | case map[string]int16: 182 | return foldMapInt16 183 | case map[string]int32: 184 | return foldMapInt32 185 | case map[string]int64: 186 | return foldMapInt64 187 | case map[string]int: 188 | return foldMapInt 189 | 190 | /* 191 | case byte: 192 | return visitByte 193 | */ 194 | case uint8: 195 | return foldUint8 196 | case uint16: 197 | return foldUint16 198 | case uint32: 199 | return foldUint32 200 | case uint64: 201 | return foldUint64 202 | case uint: 203 | return foldUint 204 | 205 | case []byte: 206 | return foldBytes 207 | /* 208 | case []uint8: 209 | return visitArrUint8 210 | */ 211 | case []uint16: 212 | return foldArrUint16 213 | case []uint32: 214 | return foldArrUint32 215 | case []uint64: 216 | return foldArrUint64 217 | case []uint: 218 | return foldArrUint 219 | 220 | case map[string]uint8: 221 | return foldMapUint8 222 | case map[string]uint16: 223 | return foldMapUint16 224 | case map[string]uint32: 225 | return foldMapUint32 226 | case map[string]uint64: 227 | return foldMapUint64 228 | case map[string]uint: 229 | return foldMapUint 230 | 231 | case float32: 232 | return foldFloat32 233 | case float64: 234 | return foldFloat64 235 | 236 | case []float32: 237 | return foldArrFloat32 238 | case []float64: 239 | return foldArrFloat64 240 | 241 | case map[string]float32: 242 | return foldMapFloat32 243 | case map[string]float64: 244 | return foldMapFloat64 245 | 246 | case string: 247 | return foldString 248 | case []string: 249 | return foldArrString 250 | case map[string]string: 251 | return foldMapString 252 | 253 | case []interface{}: 254 | return foldArrInterface 255 | case map[string]interface{}: 256 | return foldMapInterface 257 | } 258 | 259 | return nil 260 | } 261 | -------------------------------------------------------------------------------- /gotype/fold_arr.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import ( 21 | "reflect" 22 | 23 | structform "github.com/elastic/go-structform" 24 | ) 25 | 26 | var ( 27 | reFoldArrBool = liftFold([]bool(nil), foldArrBool) 28 | reFoldArrInt = liftFold([]int(nil), foldArrInt) 29 | reFoldArrInt8 = liftFold([]int8(nil), foldArrInt8) 30 | reFoldArrInt16 = liftFold([]int16(nil), foldArrInt16) 31 | reFoldArrInt32 = liftFold([]int32(nil), foldArrInt32) 32 | reFoldArrInt64 = liftFold([]int64(nil), foldArrInt64) 33 | reFoldArrUint = liftFold([]uint(nil), foldArrUint) 34 | reFoldArrUint8 = liftFold([]uint8(nil), foldArrUint8) 35 | reFoldArrUint16 = liftFold([]uint16(nil), foldArrUint16) 36 | reFoldArrUint32 = liftFold([]uint32(nil), foldArrUint32) 37 | reFoldArrUint64 = liftFold([]uint64(nil), foldArrUint64) 38 | reFoldArrFloat32 = liftFold([]float32(nil), foldArrFloat32) 39 | reFoldArrFloat64 = liftFold([]float64(nil), foldArrFloat64) 40 | reFoldArrString = liftFold([]string(nil), foldArrString) 41 | ) 42 | 43 | var tArrayAny = reflect.TypeOf([]interface{}(nil)) 44 | 45 | func foldArrInterface(C *foldContext, v interface{}) error { 46 | a := v.([]interface{}) 47 | if err := C.OnArrayStart(len(a), structform.AnyType); err != nil { 48 | return err 49 | } 50 | 51 | for _, v := range a { 52 | if err := foldInterfaceValue(C, v); err != nil { 53 | return err 54 | } 55 | } 56 | return C.OnArrayFinished() 57 | } 58 | 59 | func foldArrBool(C *foldContext, v interface{}) error { return C.visitor.OnBoolArray(v.([]bool)) } 60 | func foldArrString(C *foldContext, v interface{}) error { return C.visitor.OnStringArray(v.([]string)) } 61 | func foldArrInt8(C *foldContext, v interface{}) error { return C.visitor.OnInt8Array(v.([]int8)) } 62 | func foldArrInt16(C *foldContext, v interface{}) error { return C.visitor.OnInt16Array(v.([]int16)) } 63 | func foldArrInt32(C *foldContext, v interface{}) error { return C.visitor.OnInt32Array(v.([]int32)) } 64 | func foldArrInt64(C *foldContext, v interface{}) error { return C.visitor.OnInt64Array(v.([]int64)) } 65 | func foldArrInt(C *foldContext, v interface{}) error { return C.visitor.OnIntArray(v.([]int)) } 66 | func foldBytes(C *foldContext, v interface{}) error { return C.visitor.OnBytes(v.([]byte)) } 67 | func foldArrUint8(C *foldContext, v interface{}) error { return C.visitor.OnUint8Array(v.([]uint8)) } 68 | func foldArrUint16(C *foldContext, v interface{}) error { return C.visitor.OnUint16Array(v.([]uint16)) } 69 | func foldArrUint32(C *foldContext, v interface{}) error { return C.visitor.OnUint32Array(v.([]uint32)) } 70 | func foldArrUint64(C *foldContext, v interface{}) error { return C.visitor.OnUint64Array(v.([]uint64)) } 71 | func foldArrUint(C *foldContext, v interface{}) error { return C.visitor.OnUintArray(v.([]uint)) } 72 | func foldArrFloat32(C *foldContext, v interface{}) error { 73 | return C.visitor.OnFloat32Array(v.([]float32)) 74 | } 75 | func foldArrFloat64(C *foldContext, v interface{}) error { 76 | return C.visitor.OnFloat64Array(v.([]float64)) 77 | } 78 | -------------------------------------------------------------------------------- /gotype/fold_inline.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import ( 21 | "reflect" 22 | 23 | structform "github.com/elastic/go-structform" 24 | "github.com/elastic/go-structform/visitors" 25 | ) 26 | 27 | // getReflectFoldMapKeys implements inline fold of a map[string]X type, 28 | // not reporting object start/end events 29 | func getReflectFoldMapKeys(c *foldContext, t reflect.Type) (reFoldFn, error) { 30 | if t.Key().Kind() != reflect.String { 31 | return nil, errMapRequiresStringKey 32 | } 33 | 34 | f := getMapInlineByPrimitiveElem(t.Elem()) 35 | if f != nil { 36 | return f, nil 37 | } 38 | 39 | elemVisitor, err := getReflectFold(c, t.Elem()) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | return makeMapKeysFold(elemVisitor), nil 45 | } 46 | 47 | func makeMapKeysFold(elemVisitor reFoldFn) reFoldFn { 48 | return func(C *foldContext, rv reflect.Value) error { 49 | if rv.IsNil() || !rv.IsValid() { 50 | return nil 51 | } 52 | 53 | for _, k := range rv.MapKeys() { 54 | if err := C.OnKey(k.String()); err != nil { 55 | return err 56 | } 57 | if err := elemVisitor(C, rv.MapIndex(k)); err != nil { 58 | return err 59 | } 60 | } 61 | return nil 62 | } 63 | } 64 | 65 | // getReflectFoldInlineInterface create an inline folder for an yet unknown type. 66 | // The actual types folder must open/close an object 67 | func getReflectFoldInlineInterface(C *foldContext, t reflect.Type) (reFoldFn, error) { 68 | var ( 69 | // cache last used folder 70 | lastType reflect.Type 71 | lastVisitor reFoldFn 72 | ) 73 | 74 | return embeddObjReFold(C, func(C *foldContext, rv reflect.Value) error { 75 | if rv.Type() != lastType { 76 | elemVisitor, err := getReflectFold(C, rv.Type()) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | lastVisitor = elemVisitor 82 | lastType = rv.Type() 83 | } 84 | return lastVisitor(C, rv) 85 | }), nil 86 | } 87 | 88 | func embeddObjReFold(C *foldContext, objFold reFoldFn) reFoldFn { 89 | var ( 90 | ctx = *C 91 | vs = visitors.NewExpectObjVisitor(nil) 92 | ) 93 | 94 | ctx.visitor = structform.EnsureExtVisitor(vs).(visitor) 95 | return func(C *foldContext, rv reflect.Value) error { 96 | // don't inline missing/empty object 97 | if rv.IsNil() || !rv.IsValid() { 98 | return nil 99 | } 100 | 101 | vs.SetActive(C.visitor) 102 | err := objFold(&ctx, rv) 103 | if err == nil && !vs.Done() { 104 | err = errExpectedObjectClose 105 | } 106 | 107 | vs.SetActive(nil) 108 | return err 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /gotype/fold_map.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import ( 21 | "reflect" 22 | 23 | structform "github.com/elastic/go-structform" 24 | ) 25 | 26 | var ( 27 | reFoldMapBool = liftFold(map[string]bool(nil), foldMapBool) 28 | reFoldMapInt = liftFold(map[string]int(nil), foldMapInt) 29 | reFoldMapInt8 = liftFold(map[string]int8(nil), foldMapInt8) 30 | reFoldMapInt16 = liftFold(map[string]int16(nil), foldMapInt16) 31 | reFoldMapInt32 = liftFold(map[string]int32(nil), foldMapInt32) 32 | reFoldMapInt64 = liftFold(map[string]int64(nil), foldMapInt64) 33 | reFoldMapUint = liftFold(map[string]uint(nil), foldMapUint) 34 | reFoldMapUint8 = liftFold(map[string]uint8(nil), foldMapUint8) 35 | reFoldMapUint16 = liftFold(map[string]uint16(nil), foldMapUint16) 36 | reFoldMapUint32 = liftFold(map[string]uint32(nil), foldMapUint32) 37 | reFoldMapUint64 = liftFold(map[string]uint64(nil), foldMapUint64) 38 | reFoldMapFloat32 = liftFold(map[string]float32(nil), foldMapFloat32) 39 | reFoldMapFloat64 = liftFold(map[string]float64(nil), foldMapFloat64) 40 | reFoldMapString = liftFold(map[string]string(nil), foldMapString) 41 | ) 42 | 43 | var tMapAny = reflect.TypeOf(map[string]interface{}(nil)) 44 | 45 | func foldMapInterface(C *foldContext, v interface{}) error { 46 | m := v.(map[string]interface{}) 47 | if err := C.OnObjectStart(len(m), structform.AnyType); err != nil { 48 | return err 49 | } 50 | 51 | for k, v := range m { 52 | if err := C.OnKey(k); err != nil { 53 | return err 54 | } 55 | if err := foldInterfaceValue(C, v); err != nil { 56 | return err 57 | } 58 | } 59 | return C.OnObjectFinished() 60 | } 61 | 62 | func foldMapBool(C *foldContext, v interface{}) error { 63 | return C.visitor.OnBoolObject(v.(map[string]bool)) 64 | } 65 | 66 | func foldMapString(C *foldContext, v interface{}) error { 67 | return C.visitor.OnStringObject(v.(map[string]string)) 68 | } 69 | 70 | func foldMapInt8(C *foldContext, v interface{}) error { 71 | return C.visitor.OnInt8Object(v.(map[string]int8)) 72 | } 73 | 74 | func foldMapInt16(C *foldContext, v interface{}) error { 75 | return C.visitor.OnInt16Object(v.(map[string]int16)) 76 | } 77 | 78 | func foldMapInt32(C *foldContext, v interface{}) error { 79 | return C.visitor.OnInt32Object(v.(map[string]int32)) 80 | } 81 | 82 | func foldMapInt64(C *foldContext, v interface{}) error { 83 | return C.visitor.OnInt64Object(v.(map[string]int64)) 84 | } 85 | 86 | func foldMapInt(C *foldContext, v interface{}) error { 87 | return C.visitor.OnIntObject(v.(map[string]int)) 88 | } 89 | 90 | func foldMapUint8(C *foldContext, v interface{}) error { 91 | return C.visitor.OnUint8Object(v.(map[string]uint8)) 92 | } 93 | 94 | func foldMapUint16(C *foldContext, v interface{}) error { 95 | return C.visitor.OnUint16Object(v.(map[string]uint16)) 96 | } 97 | 98 | func foldMapUint32(C *foldContext, v interface{}) error { 99 | return C.visitor.OnUint32Object(v.(map[string]uint32)) 100 | } 101 | 102 | func foldMapUint64(C *foldContext, v interface{}) error { 103 | return C.visitor.OnUint64Object(v.(map[string]uint64)) 104 | } 105 | 106 | func foldMapUint(C *foldContext, v interface{}) error { 107 | return C.visitor.OnUintObject(v.(map[string]uint)) 108 | } 109 | 110 | func foldMapFloat32(C *foldContext, v interface{}) error { 111 | return C.visitor.OnFloat32Object(v.(map[string]float32)) 112 | } 113 | 114 | func foldMapFloat64(C *foldContext, v interface{}) error { 115 | return C.visitor.OnFloat64Object(v.(map[string]float64)) 116 | } 117 | -------------------------------------------------------------------------------- /gotype/fold_map_inline.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | import: 19 | - types.yml 20 | 21 | main: | 22 | package gotype 23 | 24 | import ( 25 | "reflect" 26 | 27 | stunsafe "github.com/urso/go-structform/internal/unsafe" 28 | ) 29 | 30 | var _mapInlineMapping = map[reflect.Type]reFoldFn{ 31 | {{ range data.primitiveTypes }} 32 | {{- $t := capitalize . -}} 33 | t{{ $t }}: foldMapInline{{ $t }}, 34 | {{ end }} 35 | } 36 | 37 | func getMapInlineByPrimitiveElem(t reflect.Type) reFoldFn { 38 | if t == tInterface { 39 | return foldMapInlineInterface 40 | } 41 | return _mapInlineMapping[t] 42 | } 43 | 44 | func foldMapInlineInterface(C *foldContext, v reflect.Value) (err error) { 45 | ptr := unsafe.Pointer(v.Pointer()) 46 | if ptr == nil { 47 | return nil 48 | } 49 | 50 | m := *((*map[string]interface{})(unsafe.Pointer(&ptr))) 51 | for k, v := range m { 52 | if err = C.OnKey(k); err != nil { 53 | return err 54 | } 55 | if err = foldInterfaceValue(C, v); err != nil { 56 | return err 57 | } 58 | } 59 | return 60 | } 61 | 62 | {{ range data.primitiveTypes }} 63 | {{ $t := capitalize . }} 64 | func foldMapInline{{ $t }}(C *foldContext, v reflect.Value) (err error) { 65 | ptr := unsafe.Pointer(v.Pointer()) 66 | if ptr == nil { 67 | return nil 68 | } 69 | 70 | m := *((*map[string]{{ . }})(unsafe.Pointer(&ptr))) 71 | for k, v := range m { 72 | if err = C.OnKey(k); err != nil { 73 | return err 74 | } 75 | if err = C.On{{ $t }}(v); err != nil { 76 | return err 77 | } 78 | } 79 | return 80 | } 81 | {{ end }} 82 | -------------------------------------------------------------------------------- /gotype/fold_opts.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import "reflect" 21 | 22 | type initFoldOptions struct { 23 | foldFns map[reflect.Type]reFoldFn 24 | } 25 | 26 | type FoldOption func(*initFoldOptions) error 27 | 28 | func applyFoldOpts(opts []FoldOption) (i initFoldOptions, err error) { 29 | for _, o := range opts { 30 | if err = o(&i); err != nil { 31 | break 32 | } 33 | } 34 | return i, err 35 | } 36 | 37 | func Folders(in ...interface{}) FoldOption { 38 | folders, err := makeUserFoldFns(in) 39 | if err != nil { 40 | return func(_ *initFoldOptions) error { return err } 41 | } 42 | 43 | if len(folders) == 0 { 44 | return func(*initFoldOptions) error { return nil } 45 | } 46 | 47 | return func(o *initFoldOptions) error { 48 | if o.foldFns == nil { 49 | o.foldFns = map[reflect.Type]reFoldFn{} 50 | } 51 | 52 | for k, v := range folders { 53 | o.foldFns[k] = v 54 | } 55 | return nil 56 | } 57 | } 58 | 59 | func makeUserFoldFns(in []interface{}) (map[reflect.Type]reFoldFn, error) { 60 | M := map[reflect.Type]reFoldFn{} 61 | 62 | for _, v := range in { 63 | fn := reflect.ValueOf(v) 64 | fptr, err := makeUserFoldFn(fn) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | ta0 := fn.Type().In(0) 70 | M[ta0] = liftUserPtrFn(fptr) 71 | M[ta0.Elem()] = liftUserValueFn(fptr) 72 | } 73 | 74 | return M, nil 75 | } 76 | -------------------------------------------------------------------------------- /gotype/fold_primitives.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import "reflect" 21 | 22 | func foldNil(C *foldContext, v interface{}) error { return C.OnNil() } 23 | func foldBool(C *foldContext, v interface{}) error { return C.OnBool(v.(bool)) } 24 | func foldInt8(C *foldContext, v interface{}) error { return C.OnInt8(v.(int8)) } 25 | func foldInt16(C *foldContext, v interface{}) error { return C.OnInt16(v.(int16)) } 26 | func foldInt32(C *foldContext, v interface{}) error { return C.OnInt32(v.(int32)) } 27 | func foldInt64(C *foldContext, v interface{}) error { return C.OnInt64(v.(int64)) } 28 | func foldInt(C *foldContext, v interface{}) error { return C.OnInt64(int64(v.(int))) } 29 | func foldByte(C *foldContext, v interface{}) error { return C.OnByte(v.(byte)) } 30 | func foldUint8(C *foldContext, v interface{}) error { return C.OnUint8(v.(uint8)) } 31 | func foldUint16(C *foldContext, v interface{}) error { return C.OnUint16(v.(uint16)) } 32 | func foldUint32(C *foldContext, v interface{}) error { return C.OnUint32(v.(uint32)) } 33 | func foldUint64(C *foldContext, v interface{}) error { return C.OnUint64(v.(uint64)) } 34 | func foldUint(C *foldContext, v interface{}) error { return C.OnUint(v.(uint)) } 35 | func foldFloat32(C *foldContext, v interface{}) error { return C.OnFloat32(v.(float32)) } 36 | func foldFloat64(C *foldContext, v interface{}) error { return C.OnFloat64(v.(float64)) } 37 | func foldString(C *foldContext, v interface{}) error { return C.OnString(v.(string)) } 38 | 39 | func reFoldNil(C *foldContext, v reflect.Value) error { return C.OnNil() } 40 | func reFoldBool(C *foldContext, v reflect.Value) error { return C.OnBool(v.Bool()) } 41 | func reFoldInt8(C *foldContext, v reflect.Value) error { return C.OnInt8(int8(v.Int())) } 42 | func reFoldInt16(C *foldContext, v reflect.Value) error { return C.OnInt16(int16(v.Int())) } 43 | func reFoldInt32(C *foldContext, v reflect.Value) error { return C.OnInt32(int32(v.Int())) } 44 | func reFoldInt64(C *foldContext, v reflect.Value) error { return C.OnInt64(v.Int()) } 45 | func reFoldInt(C *foldContext, v reflect.Value) error { return C.OnInt64(int64(int(v.Int()))) } 46 | func reFoldUint8(C *foldContext, v reflect.Value) error { return C.OnUint8(uint8(v.Uint())) } 47 | func reFoldUint16(C *foldContext, v reflect.Value) error { return C.OnUint16(uint16(v.Uint())) } 48 | func reFoldUint32(C *foldContext, v reflect.Value) error { return C.OnUint32(uint32(v.Uint())) } 49 | func reFoldUint64(C *foldContext, v reflect.Value) error { return C.OnUint64(v.Uint()) } 50 | func reFoldUint(C *foldContext, v reflect.Value) error { return C.OnUint(uint(v.Uint())) } 51 | func reFoldFloat32(C *foldContext, v reflect.Value) error { 52 | return C.OnFloat32(float32(v.Float())) 53 | } 54 | func reFoldFloat64(C *foldContext, v reflect.Value) error { return C.OnFloat64(v.Float()) } 55 | func reFoldString(C *foldContext, v reflect.Value) error { return C.OnString(v.String()) } 56 | 57 | func reFoldFolderIfc(C *foldContext, v reflect.Value) error { 58 | if implementsFolder(v.Type()) { 59 | return v.Interface().(Folder).Fold(C.visitor) 60 | } 61 | 62 | if v.CanAddr() { 63 | return reFoldFolderIfc(C, v.Addr()) 64 | } 65 | 66 | tmp := reflect.New(v.Type()) 67 | tmp.Elem().Set(v) 68 | return reFoldFolderIfc(C, tmp) 69 | } 70 | -------------------------------------------------------------------------------- /gotype/fold_refl_sel.generated.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | // This file has been generated from 'fold_refl_sel.yml', do not edit 19 | package gotype 20 | 21 | import "reflect" 22 | 23 | var _reflPrimitivesMapping = map[reflect.Type]reFoldFn{ 24 | 25 | tBool: reFoldBool, 26 | reflect.SliceOf(tBool): reFoldArrBool, 27 | reflect.MapOf(tString, tBool): reFoldMapBool, 28 | 29 | tString: reFoldString, 30 | reflect.SliceOf(tString): reFoldArrString, 31 | reflect.MapOf(tString, tString): reFoldMapString, 32 | 33 | tUint: reFoldUint, 34 | reflect.SliceOf(tUint): reFoldArrUint, 35 | reflect.MapOf(tString, tUint): reFoldMapUint, 36 | 37 | tUint8: reFoldUint8, 38 | reflect.SliceOf(tUint8): reFoldArrUint8, 39 | reflect.MapOf(tString, tUint8): reFoldMapUint8, 40 | 41 | tUint16: reFoldUint16, 42 | reflect.SliceOf(tUint16): reFoldArrUint16, 43 | reflect.MapOf(tString, tUint16): reFoldMapUint16, 44 | 45 | tUint32: reFoldUint32, 46 | reflect.SliceOf(tUint32): reFoldArrUint32, 47 | reflect.MapOf(tString, tUint32): reFoldMapUint32, 48 | 49 | tUint64: reFoldUint64, 50 | reflect.SliceOf(tUint64): reFoldArrUint64, 51 | reflect.MapOf(tString, tUint64): reFoldMapUint64, 52 | 53 | tInt: reFoldInt, 54 | reflect.SliceOf(tInt): reFoldArrInt, 55 | reflect.MapOf(tString, tInt): reFoldMapInt, 56 | 57 | tInt8: reFoldInt8, 58 | reflect.SliceOf(tInt8): reFoldArrInt8, 59 | reflect.MapOf(tString, tInt8): reFoldMapInt8, 60 | 61 | tInt16: reFoldInt16, 62 | reflect.SliceOf(tInt16): reFoldArrInt16, 63 | reflect.MapOf(tString, tInt16): reFoldMapInt16, 64 | 65 | tInt32: reFoldInt32, 66 | reflect.SliceOf(tInt32): reFoldArrInt32, 67 | reflect.MapOf(tString, tInt32): reFoldMapInt32, 68 | 69 | tInt64: reFoldInt64, 70 | reflect.SliceOf(tInt64): reFoldArrInt64, 71 | reflect.MapOf(tString, tInt64): reFoldMapInt64, 72 | 73 | tFloat32: reFoldFloat32, 74 | reflect.SliceOf(tFloat32): reFoldArrFloat32, 75 | reflect.MapOf(tString, tFloat32): reFoldMapFloat32, 76 | 77 | tFloat64: reFoldFloat64, 78 | reflect.SliceOf(tFloat64): reFoldArrFloat64, 79 | reflect.MapOf(tString, tFloat64): reFoldMapFloat64, 80 | } 81 | 82 | func getReflectFoldPrimitive(t reflect.Type) reFoldFn { 83 | return _reflPrimitivesMapping[t] 84 | } 85 | 86 | func getReflectFoldPrimitiveKind(t reflect.Type) (reFoldFn, error) { 87 | switch t.Kind() { 88 | 89 | case reflect.Bool: 90 | return reFoldBool, nil 91 | 92 | case reflect.String: 93 | return reFoldString, nil 94 | 95 | case reflect.Uint: 96 | return reFoldUint, nil 97 | 98 | case reflect.Uint8: 99 | return reFoldUint8, nil 100 | 101 | case reflect.Uint16: 102 | return reFoldUint16, nil 103 | 104 | case reflect.Uint32: 105 | return reFoldUint32, nil 106 | 107 | case reflect.Uint64: 108 | return reFoldUint64, nil 109 | 110 | case reflect.Int: 111 | return reFoldInt, nil 112 | 113 | case reflect.Int8: 114 | return reFoldInt8, nil 115 | 116 | case reflect.Int16: 117 | return reFoldInt16, nil 118 | 119 | case reflect.Int32: 120 | return reFoldInt32, nil 121 | 122 | case reflect.Int64: 123 | return reFoldInt64, nil 124 | 125 | case reflect.Float32: 126 | return reFoldFloat32, nil 127 | 128 | case reflect.Float64: 129 | return reFoldFloat64, nil 130 | 131 | default: 132 | return nil, errUnsupported 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /gotype/fold_refl_sel.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | import: 19 | - types.yml 20 | 21 | main: | 22 | package gotype 23 | 24 | var _reflPrimitivesMapping = map[reflect.Type]reFoldFn{ 25 | {{ range data.primitiveTypes }} 26 | {{ $t := capitalize . }} 27 | t{{ $t }}: reFold{{ $t }}, 28 | reflect.SliceOf(t{{ $t }}): reFoldArr{{ $t }}, 29 | reflect.MapOf(tString, t{{ $t }}): reFoldMap{{ $t }}, 30 | {{ end }} 31 | } 32 | 33 | func getReflectFoldPrimitive(t reflect.Type) reFoldFn { 34 | return _reflPrimitivesMapping[t] 35 | } 36 | 37 | func getReflectFoldPrimitiveKind(t reflect.Type) (reFoldFn, error) { 38 | switch t.Kind() { 39 | {{ range data.primitiveTypes }} 40 | {{ $t := capitalize . }} 41 | case reflect.{{ $t }}: 42 | return reFold{{ $t }}, nil 43 | {{ end }} 44 | default: 45 | return nil, errUnsupported 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /gotype/fold_user.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import ( 21 | "errors" 22 | "fmt" 23 | "reflect" 24 | "unsafe" 25 | 26 | structform "github.com/elastic/go-structform" 27 | stunsafe "github.com/elastic/go-structform/internal/unsafe" 28 | ) 29 | 30 | type userFoldFn func(unsafe.Pointer, structform.ExtVisitor) error 31 | 32 | func makeUserFoldFn(fn reflect.Value) (userFoldFn, error) { 33 | t := fn.Type() 34 | 35 | if fn.Kind() != reflect.Func { 36 | return nil, errors.New("function type required") 37 | } 38 | 39 | if t.NumIn() != 2 { 40 | return nil, fmt.Errorf("function '%v' must accept 2 arguments", t.Name()) 41 | } 42 | if t.NumOut() != 1 || t.Out(0) != tError { 43 | return nil, fmt.Errorf("function '%v' does not return errors", t.Name()) 44 | } 45 | 46 | ta0 := t.In(0) 47 | if ta0.Kind() != reflect.Ptr { 48 | return nil, fmt.Errorf("first argument in function '%v' must be a pointer", t.Name()) 49 | } 50 | 51 | ta1 := t.In(1) 52 | if ta1 != tExtVisitor { 53 | return nil, fmt.Errorf("second arument in function '%v' must be structform.ExtVisitor", t.Name()) 54 | } 55 | 56 | fptr := *((*userFoldFn)(stunsafe.UnsafeFnPtr(fn))) 57 | return fptr, nil 58 | } 59 | 60 | func liftUserPtrFn(f userFoldFn) reFoldFn { 61 | return func(c *foldContext, v reflect.Value) error { 62 | if v.IsNil() { 63 | return f(nil, c.visitor) 64 | } 65 | return f(stunsafe.ReflValuePtr(v.Elem()), c.visitor) 66 | } 67 | } 68 | 69 | func liftUserValueFn(f userFoldFn) reFoldFn { 70 | return func(c *foldContext, v reflect.Value) error { 71 | return f(stunsafe.ReflValuePtr(v), c.visitor) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /gotype/stacks.generated.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | // This file has been generated from 'stacks.yml', do not edit 19 | package gotype 20 | 21 | import ( 22 | "reflect" 23 | "unsafe" 24 | 25 | structform "github.com/elastic/go-structform" 26 | ) 27 | 28 | type unfolderStack struct { 29 | current unfolder 30 | stack []unfolder 31 | stack0 [32]unfolder 32 | } 33 | 34 | type reflectValueStack struct { 35 | current reflect.Value 36 | stack []reflect.Value 37 | stack0 [32]reflect.Value 38 | } 39 | 40 | type ptrStack struct { 41 | current unsafe.Pointer 42 | stack []unsafe.Pointer 43 | stack0 [32]unsafe.Pointer 44 | } 45 | 46 | type keyStack struct { 47 | current string 48 | stack []string 49 | stack0 [32]string 50 | } 51 | 52 | type idxStack struct { 53 | current int 54 | stack []int 55 | stack0 [32]int 56 | } 57 | 58 | type structformTypeStack struct { 59 | current structform.BaseType 60 | stack []structform.BaseType 61 | stack0 [32]structform.BaseType 62 | } 63 | 64 | func (s *unfolderStack) init(v unfolder) { 65 | s.current = v 66 | s.stack = s.stack0[:0] 67 | } 68 | 69 | func (s *unfolderStack) push(v unfolder) { 70 | s.stack = append(s.stack, s.current) 71 | s.current = v 72 | } 73 | 74 | func (s *unfolderStack) pop() unfolder { 75 | old := s.current 76 | last := len(s.stack) - 1 77 | s.current = s.stack[last] 78 | s.stack = s.stack[:last] 79 | return old 80 | } 81 | 82 | func (s *reflectValueStack) init(v reflect.Value) { 83 | s.current = v 84 | s.stack = s.stack0[:0] 85 | } 86 | 87 | func (s *reflectValueStack) push(v reflect.Value) { 88 | s.stack = append(s.stack, s.current) 89 | s.current = v 90 | } 91 | 92 | func (s *reflectValueStack) pop() reflect.Value { 93 | old := s.current 94 | last := len(s.stack) - 1 95 | s.current = s.stack[last] 96 | s.stack = s.stack[:last] 97 | return old 98 | } 99 | 100 | func (s *ptrStack) init() { 101 | s.current = nil 102 | s.stack = s.stack0[:0] 103 | } 104 | 105 | func (s *ptrStack) push(v unsafe.Pointer) { 106 | s.stack = append(s.stack, s.current) 107 | s.current = v 108 | } 109 | 110 | func (s *ptrStack) pop() unsafe.Pointer { 111 | old := s.current 112 | last := len(s.stack) - 1 113 | s.current = s.stack[last] 114 | s.stack = s.stack[:last] 115 | return old 116 | } 117 | 118 | func (s *keyStack) init() { 119 | s.current = "" 120 | s.stack = s.stack0[:0] 121 | } 122 | 123 | func (s *keyStack) push(v string) { 124 | s.stack = append(s.stack, s.current) 125 | s.current = v 126 | } 127 | 128 | func (s *keyStack) pop() string { 129 | old := s.current 130 | last := len(s.stack) - 1 131 | s.current = s.stack[last] 132 | s.stack = s.stack[:last] 133 | return old 134 | } 135 | 136 | func (s *idxStack) init() { 137 | s.current = -1 138 | s.stack = s.stack0[:0] 139 | } 140 | 141 | func (s *idxStack) push(v int) { 142 | s.stack = append(s.stack, s.current) 143 | s.current = v 144 | } 145 | 146 | func (s *idxStack) pop() int { 147 | old := s.current 148 | last := len(s.stack) - 1 149 | s.current = s.stack[last] 150 | s.stack = s.stack[:last] 151 | return old 152 | } 153 | 154 | func (s *structformTypeStack) init() { 155 | s.current = structform.AnyType 156 | s.stack = s.stack0[:0] 157 | } 158 | 159 | func (s *structformTypeStack) push(v structform.BaseType) { 160 | s.stack = append(s.stack, s.current) 161 | s.current = v 162 | } 163 | 164 | func (s *structformTypeStack) pop() structform.BaseType { 165 | old := s.current 166 | last := len(s.stack) - 1 167 | s.current = s.stack[last] 168 | s.stack = s.stack[:last] 169 | return old 170 | } 171 | -------------------------------------------------------------------------------- /gotype/stacks.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | data.stacks: 19 | - name: unfolderStack 20 | type: unfolder 21 | - name: reflectValueStack 22 | type: reflect.Value 23 | - name: ptrStack 24 | init: 'nil' 25 | type: unsafe.Pointer 26 | - name: keyStack 27 | type: string 28 | init: '""' 29 | - name: idxStack 30 | type: int 31 | init: -1 32 | - name: structformTypeStack 33 | type: structform.BaseType 34 | init: structform.AnyType 35 | 36 | main: | 37 | package gotype 38 | 39 | {{ range .stacks }} 40 | type {{ .name }} struct { 41 | current {{ .type }} 42 | stack []{{ .type }} 43 | stack0 [{{ if .size0 }}{{ .size0 }}{{ else }}32{{ end }}]{{ .type }} 44 | } 45 | {{ end }} 46 | 47 | {{ range .stacks }} 48 | func (s *{{ .name }}) init({{ if isnil .init }}v {{ .type }}{{end}}) { 49 | s.current = {{ if isnil .init }}v{{ else }}{{ .init }}{{end}} 50 | s.stack = s.stack0[:0] 51 | } 52 | 53 | func (s *{{ .name }}) push(v {{ .type }}) { 54 | s.stack = append(s.stack, s.current) 55 | s.current = v 56 | } 57 | 58 | func (s *{{ .name }}) pop() {{ .type }} { 59 | old := s.current 60 | last := len(s.stack) - 1 61 | s.current = s.stack[last] 62 | s.stack = s.stack[:last] 63 | return old 64 | } 65 | {{ end }} 66 | -------------------------------------------------------------------------------- /gotype/symbols.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | type symbolCache struct { 21 | m map[string]*symbol 22 | lst symbolList 23 | max int 24 | } 25 | 26 | type symbol struct { 27 | value string 28 | prev *symbol 29 | next *symbol 30 | } 31 | 32 | type symbolList symbol 33 | 34 | func (c *symbolCache) init(max int) { 35 | c.max = max 36 | c.m = make(map[string]*symbol, max) 37 | c.lst = symbolList{} 38 | c.lst.prev = (*symbol)(&c.lst) 39 | c.lst.next = (*symbol)(&c.lst) 40 | } 41 | 42 | func (c *symbolCache) enabled() bool { 43 | return c.m != nil 44 | } 45 | 46 | func (c *symbolCache) get(in []byte) string { 47 | if !c.enabled() { 48 | return string(in) 49 | } 50 | 51 | if sym := c.lookup(bytes2Str(in)); sym != nil { 52 | return sym.value 53 | } 54 | 55 | str := string(in) 56 | c.add(str) 57 | return str 58 | } 59 | 60 | func (c *symbolCache) lookup(value string) *symbol { 61 | sym := c.m[value] 62 | if sym != nil { 63 | removeLst(sym) 64 | c.lst.append(sym) 65 | } 66 | return sym 67 | } 68 | 69 | func (c *symbolCache) add(value string) { 70 | if len(c.m) == c.max { 71 | old := c.lst.pop() 72 | delete(c.m, old.value) 73 | } 74 | 75 | sym := &symbol{value: value} 76 | c.m[value] = sym 77 | c.lst.append(sym) 78 | } 79 | 80 | func (l *symbolList) empty() bool { 81 | s := (*symbol)(l) 82 | return s.next == s && s.prev == s 83 | } 84 | 85 | func (l *symbolList) append(sym *symbol) { 86 | head := (*symbol)(l) 87 | 88 | sym.prev = head.prev 89 | sym.next = head 90 | head.prev.next = sym 91 | head.prev = sym 92 | } 93 | 94 | func (l *symbolList) pop() (sym *symbol) { 95 | if !l.empty() { 96 | sym = l.next 97 | removeLst(sym) 98 | } 99 | return 100 | } 101 | 102 | func removeLst(s *symbol) { 103 | s.prev.next = s.next 104 | s.next.prev = s.prev 105 | } 106 | -------------------------------------------------------------------------------- /gotype/tags.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import "strings" 21 | 22 | // tagOptions represents additional per field options that have 23 | // been parsed from the struct tags. 24 | // 25 | // Available options are: 26 | // - squash, inline: The field must be a map[string]... or another struct. 27 | // All fields from the struct will be treated like they belong to the current 28 | // struct. 29 | // - omit: The field is never reported 30 | // - omitempty: The field is not reported if the value is assumed to be empty. 31 | // Strings, arrays, maps of 0 length and nil pointers are always assumed to be empty. 32 | // Custom types can implement the `IsZero() bool` method (IsZeroer interface). If the 33 | // `IsZero` method is true and `omitempty` has been set, the field will be ignored. 34 | type tagOptions struct { 35 | squash bool 36 | omitEmpty bool 37 | omit bool 38 | } 39 | 40 | var defaultTagOptions = tagOptions{ 41 | squash: false, 42 | omitEmpty: false, 43 | omit: false, 44 | } 45 | 46 | func parseTags(tag string) (string, tagOptions) { 47 | s := strings.Split(tag, ",") 48 | if len(s) == 0 { 49 | return "", defaultTagOptions 50 | } 51 | 52 | if s[0] == "-" { 53 | opts := defaultTagOptions 54 | opts.omit = true 55 | return "", opts 56 | } 57 | 58 | opts := defaultTagOptions 59 | for _, opt := range s[1:] { 60 | switch strings.TrimSpace(opt) { 61 | case "squash", "inline": 62 | opts.squash = true 63 | case "omitempty": 64 | opts.omitEmpty = true 65 | case "omit": 66 | opts.omit = true 67 | } 68 | } 69 | return strings.TrimSpace(s[0]), opts 70 | } 71 | -------------------------------------------------------------------------------- /gotype/types.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | data.numTypes: [ 19 | uint, uint8, uint16, uint32, uint64, 20 | int, int8, int16, int32, int64, 21 | float32, float64 22 | ] 23 | 24 | data.primitiveTypes: [ 25 | bool, 26 | string, 27 | uint, uint8, uint16, uint32, uint64, 28 | int, int8, int16, int32, int64, 29 | float32, float64 30 | ] 31 | 32 | data.mapTypes: 33 | AnyType: 'interface{}' 34 | ByteType: uint8 35 | StringType: string 36 | BoolType: bool 37 | ZeroType: 'interface{}' 38 | IntType: int 39 | Int8Type: int8 40 | Int16Type: int16 41 | Int32Type: int32 42 | Int64Type: int64 43 | UintType: uint 44 | Uint8Type: uint8 45 | Uint16Type: uint16 46 | Uint32Type: uint32 47 | Uint64Type: uint64 48 | Float32Type: float32 49 | Float64Type: float64 50 | 51 | data.mapReflTypes: 52 | AnyType: tInterface 53 | ByteType: tByte 54 | StringType: tString 55 | BoolType: tBool 56 | ZeroType: tInterface 57 | IntType: tInt 58 | Int8Type: tInt8 59 | Int16Type: tInt16 60 | Int32Type: tInt32 61 | Int64Type: tInt64 62 | UintType: tUint 63 | Uint8Type: tUint8 64 | Uint16Type: tUint16 65 | Uint32Type: tUint32 66 | Uint64Type: tUint64 67 | Float32Type: tFloat32 68 | Float64Type: tFloat64 69 | -------------------------------------------------------------------------------- /gotype/unfold_arr.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | import: 19 | - unfold_templates.yml 20 | 21 | main: | 22 | package gotype 23 | 24 | import "github.com/urso/go-structform" 25 | 26 | {{/* defined 'lifted' pointer slice unfolders into reflection based unfolders */}} 27 | var ( 28 | unfolderReflArrIfc = liftGoUnfolder(newUnfolderArrIfc()) 29 | {{ range data.primitiveTypes }} 30 | {{ $t := capitalize . }} 31 | unfolderReflArr{{ $t }} = liftGoUnfolder(newUnfolderArr{{ $t }}()) 32 | {{ end }} 33 | ) 34 | 35 | {{/* define pointer based unfolder types */}} 36 | {{ invoke "makeTypeWithName" "name" "Ifc" "type" "interface{}" }} 37 | {{ template "makeType" "bool" }} 38 | {{ template "makeType" "string" }} 39 | {{ range .numTypes }} 40 | {{ template "makeType" . }} 41 | {{ end }} 42 | 43 | {{/* create visitor callbacks */}} 44 | {{ invoke "onIfcFns" "name" "unfolderArrIfc" "fn" "append" }} 45 | {{ invoke "onBoolFns" "name" "unfolderArrBool" "fn" "append" }} 46 | {{ invoke "onStringFns" "name" "unfolderArrString" "fn" "append" }} 47 | {{ range .numTypes }} 48 | {{ $type := . }} 49 | {{ $name := capitalize $type | printf "unfolderArr%v" }} 50 | {{ invoke "onNumberFns" "name" $name "type" $type "fn" "append" }} 51 | {{ end }} 52 | 53 | {{ template "arrIfc" }} 54 | 55 | 56 | # makeTypeWithName(name, type) 57 | templates.makeTypeWithName: | 58 | {{ $type := .type }} 59 | {{ $name := capitalize .name | printf "unfolderArr%v" }} 60 | {{ $startName := capitalize .name | printf "unfoldArrStart%v" }} 61 | 62 | {{ invoke "makeUnfoldType" "name" $name }} 63 | {{ invoke "makeUnfoldType" "name" $startName "base" "unfolderErrArrayStart" }} 64 | 65 | func (u *{{ $name }} ) initState(ctx *unfoldCtx, ptr unsafe.Pointer) { 66 | ctx.unfolder.push(u) 67 | ctx.unfolder.push(new{{ $startName | capitalize}}()) 68 | ctx.idx.push(0) 69 | ctx.ptr.push(ptr) 70 | } 71 | 72 | func (u * {{ $name }} ) cleanup(ctx *unfoldCtx) { 73 | ctx.unfolder.pop() 74 | ctx.idx.pop() 75 | ctx.ptr.pop() 76 | } 77 | 78 | func (u * {{ $startName }}) cleanup(ctx *unfoldCtx) { 79 | ctx.unfolder.pop() 80 | } 81 | 82 | func (u *{{ $startName }} ) ptr(ctx *unfoldCtx) *[]{{ $type }} { 83 | return (*[]{{ $type }})(ctx.ptr.current) 84 | } 85 | 86 | func (u *{{ $name }} ) ptr(ctx *unfoldCtx) *[]{{ $type }} { 87 | return (*[]{{ $type }})(ctx.ptr.current) 88 | } 89 | 90 | func (u *{{ $startName }}) OnArrayStart(ctx *unfoldCtx, l int, baseType structform.BaseType) error { 91 | to := u.ptr(ctx) 92 | if l < 0 { 93 | l = 0 94 | } 95 | 96 | if *to == nil && l > 0 { 97 | *to = make([]{{ $type }}, l) 98 | } else if l < len(*to) { 99 | *to = (*to)[:l] 100 | } 101 | 102 | u.cleanup(ctx) 103 | return nil 104 | } 105 | 106 | func (u *{{ $name }} ) OnArrayFinished(ctx *unfoldCtx) error { 107 | u.cleanup(ctx) 108 | return nil 109 | } 110 | 111 | func (u *{{ $name }} ) append(ctx *unfoldCtx, v {{ $type }}) error { 112 | idx := &ctx.idx 113 | to := u.ptr(ctx) 114 | if len(*to) <= idx.current { 115 | *to = append(*to, v) 116 | } else { 117 | (*to)[idx.current] = v 118 | } 119 | 120 | idx.current++ 121 | return nil 122 | } 123 | 124 | templates.arrIfc: | 125 | 126 | func unfoldIfcStartSubArray(ctx *unfoldCtx, l int, baseType structform.BaseType) error { 127 | _, ptr, unfolder := makeArrayPtr(ctx, l, baseType) 128 | ctx.ptr.push(ptr) // store pointer for use in 'Finish' 129 | ctx.baseType.push(baseType) 130 | unfolder.initState(ctx, ptr) 131 | return ctx.unfolder.current.OnArrayStart(ctx, l, baseType) 132 | } 133 | 134 | func unfoldIfcFinishSubArray(ctx *unfoldCtx) (interface{}, error) { 135 | child := ctx.ptr.pop() 136 | bt := ctx.baseType.pop() 137 | switch bt { 138 | {{ range $bt, $gt := data.mapTypes }} 139 | case structform.{{ $bt }}: 140 | value := *(*[]{{ $gt }})(child) 141 | last := len(ctx.valueBuffer.arrays) - 1 142 | ctx.valueBuffer.arrays = ctx.valueBuffer.arrays[:last] 143 | return value, nil 144 | {{ end }} 145 | default: 146 | return nil, errTODO() 147 | } 148 | } 149 | 150 | func makeArrayPtr(ctx *unfoldCtx, l int, bt structform.BaseType) (interface{}, unsafe.Pointer, ptrUnfolder) { 151 | switch bt { 152 | {{ range $bt, $gt := data.mapTypes }} 153 | case structform.{{ $bt }}: 154 | idx := len(ctx.valueBuffer.arrays) 155 | ctx.valueBuffer.arrays = append(ctx.valueBuffer.arrays, nil) 156 | arrPtr := &ctx.valueBuffer.arrays[idx] 157 | ptr := unsafe.Pointer(arrPtr) 158 | to := (*[]{{ $gt }})(ptr) 159 | {{- if or (eq $bt "AnyType") (eq $bt "ZeroType") }} 160 | unfolder := newUnfolderArrIfc() 161 | {{ else }} 162 | unfolder := newUnfolderArr{{ $gt | capitalize }}() 163 | {{ end }} 164 | return to, ptr, unfolder 165 | {{ end }} 166 | default: 167 | panic("invalid type code") 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /gotype/unfold_err.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | import: 19 | - unfold_templates.yml 20 | 21 | data.errorUnfolders: 22 | unfolderNoTarget: errNotInitialized 23 | unfolderErrUnknown: errUnsupported 24 | unfolderErrArrayStart: errExpectedArray 25 | unfolderErrObjectStart: errExpectedObject 26 | unfolderErrExpectKey: errExpectedObjectKey 27 | 28 | main: | 29 | package gotype 30 | 31 | {{ range $name, $err := data.errorUnfolders }} 32 | type {{ $name }} struct {} 33 | {{ end }} 34 | 35 | {{ range $name, $err := data.errorUnfolders }} 36 | 37 | func (*{{ $name }}) OnNil(*unfoldCtx) error { return {{ $err }} } 38 | func (*{{ $name }}) OnBool(*unfoldCtx, bool) error { return {{ $err }} } 39 | func (*{{ $name }}) OnString(*unfoldCtx, string) error { return {{ $err }} } 40 | func (*{{ $name }}) OnStringRef(*unfoldCtx, []byte) error { return {{ $err }} } 41 | func (*{{ $name }}) OnInt8(*unfoldCtx, int8) error { return {{ $err }} } 42 | func (*{{ $name }}) OnInt16(*unfoldCtx, int16) error { return {{ $err }} } 43 | func (*{{ $name }}) OnInt32(*unfoldCtx, int32) error { return {{ $err }} } 44 | func (*{{ $name }}) OnInt64(*unfoldCtx, int64) error { return {{ $err }} } 45 | func (*{{ $name }}) OnInt(*unfoldCtx, int) error { return {{ $err }} } 46 | func (*{{ $name }}) OnByte(*unfoldCtx, byte) error { return {{ $err }} } 47 | func (*{{ $name }}) OnUint8(*unfoldCtx, uint8) error { return {{ $err }} } 48 | func (*{{ $name }}) OnUint16(*unfoldCtx, uint16) error { return {{ $err }} } 49 | func (*{{ $name }}) OnUint32(*unfoldCtx, uint32) error { return {{ $err }} } 50 | func (*{{ $name }}) OnUint64(*unfoldCtx, uint64) error { return {{ $err }} } 51 | func (*{{ $name }}) OnUint(*unfoldCtx, uint) error { return {{ $err }} } 52 | func (*{{ $name }}) OnFloat32(*unfoldCtx, float32) error { return {{ $err }} } 53 | func (*{{ $name }}) OnFloat64(*unfoldCtx, float64) error { return {{ $err }} } 54 | func (*{{ $name }}) OnArrayStart(*unfoldCtx, int, structform.BaseType) error { return {{ $err }} } 55 | func (*{{ $name }}) OnArrayFinished(*unfoldCtx) error { return {{ $err }} } 56 | func (*{{ $name }}) OnChildArrayDone(*unfoldCtx) error { return {{ $err }} } 57 | func (*{{ $name }}) OnObjectStart(*unfoldCtx, int, structform.BaseType) error { return {{ $err }} } 58 | func (*{{ $name }}) OnObjectFinished(*unfoldCtx) error { return {{ $err }} } 59 | func (*{{ $name }}) OnKey(*unfoldCtx, string) error { return {{ $err }} } 60 | func (*{{ $name }}) OnKeyRef(*unfoldCtx, []byte) error { return {{ $err }} } 61 | func (*{{ $name }}) OnChildObjectDone(*unfoldCtx) error { return {{ $err }} } 62 | 63 | {{ end }} 64 | -------------------------------------------------------------------------------- /gotype/unfold_ignore.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | main: | 19 | package gotype 20 | 21 | {{ invoke "makeIgnoreType" "type" "" }} 22 | 23 | func (*unfolderIgnore) onValue(ctx *unfoldCtx) error { 24 | ctx.unfolder.pop() 25 | return nil 26 | } 27 | 28 | {{ invoke "makeIgnoreType" "type" "Arr" }} 29 | 30 | func (*unfolderIgnoreArr) onValue(ctx *unfoldCtx) error { 31 | return nil 32 | } 33 | 34 | func (*unfolderIgnoreArr) OnArrayFinished(ctx *unfoldCtx) error { 35 | ctx.unfolder.pop() 36 | return nil 37 | } 38 | 39 | {{ invoke "makeIgnoreType" "type" "Obj" }} 40 | 41 | func (*unfolderIgnoreObj) onValue(ctx *unfoldCtx) error { 42 | return nil 43 | } 44 | 45 | func (*unfolderIgnoreObj) OnObjectFinished(ctx *unfoldCtx) error { 46 | ctx.unfolder.pop() 47 | return nil 48 | } 49 | 50 | templates.makeIgnoreType: | 51 | {{ $type := .type }} 52 | {{ $tUnfolder := printf "unfolderIgnore%v" $type }} 53 | 54 | type unfoldIgnore{{ $type }}Value struct {} 55 | type unfoldIgnore{{ $type }}Ptr struct {} 56 | type {{ $tUnfolder }} struct { 57 | unfolderErrUnknown 58 | } 59 | 60 | var ( 61 | _singletonUnfoldIgnore{{ $type }}Value = &unfoldIgnore{{ $type }}Value{} 62 | _singletonUnfoldIgnore{{ $type }}Ptr = &unfoldIgnore{{ $type }}Ptr{} 63 | _singleton{{ $tUnfolder }} = &{{ $tUnfolder }}{} 64 | ) 65 | 66 | func (*unfoldIgnore{{ $type }}Value) initState(ctx *unfoldCtx, _ reflect.Value) { 67 | ctx.unfolder.push(_singleton{{ $tUnfolder }}) 68 | } 69 | 70 | func (*unfoldIgnore{{ $type }}Ptr) initState(ctx *unfoldCtx, _ unsafe.Pointer) { 71 | ctx.unfolder.push(_singleton{{ $tUnfolder }}) 72 | } 73 | 74 | func (u *{{ $tUnfolder }}) OnNil(ctx *unfoldCtx) error { return u.onValue(ctx) } 75 | func (u *{{ $tUnfolder }}) OnBool(ctx *unfoldCtx, _ bool) error { return u.onValue(ctx) } 76 | func (u *{{ $tUnfolder }}) OnString(ctx *unfoldCtx, _ string) error { return u.onValue(ctx) } 77 | func (u *{{ $tUnfolder }}) OnInt8(ctx *unfoldCtx, _ int8) error { return u.onValue(ctx) } 78 | func (u *{{ $tUnfolder }}) OnInt16(ctx *unfoldCtx, _ int16) error { return u.onValue(ctx) } 79 | func (u *{{ $tUnfolder }}) OnInt32(ctx *unfoldCtx, _ int32) error { return u.onValue(ctx) } 80 | func (u *{{ $tUnfolder }}) OnInt64(ctx *unfoldCtx, _ int64) error { return u.onValue(ctx) } 81 | func (u *{{ $tUnfolder }}) OnInt(ctx *unfoldCtx, _ int) error { return u.onValue(ctx) } 82 | func (u *{{ $tUnfolder }}) OnByte(ctx *unfoldCtx, _ byte) error { return u.onValue(ctx) } 83 | func (u *{{ $tUnfolder }}) OnUint8(ctx *unfoldCtx, _ uint8) error { return u.onValue(ctx) } 84 | func (u *{{ $tUnfolder }}) OnUint16(ctx *unfoldCtx, _ uint16) error { return u.onValue(ctx) } 85 | func (u *{{ $tUnfolder }}) OnUint32(ctx *unfoldCtx, _ uint32) error { return u.onValue(ctx) } 86 | func (u *{{ $tUnfolder }}) OnUint64(ctx *unfoldCtx, _ uint64) error { return u.onValue(ctx) } 87 | func (u *{{ $tUnfolder }}) OnUint(ctx *unfoldCtx, _ uint) error { return u.onValue(ctx) } 88 | func (u *{{ $tUnfolder }}) OnFloat32(ctx *unfoldCtx, _ float32) error { return u.onValue(ctx) } 89 | func (u *{{ $tUnfolder }}) OnFloat64(ctx *unfoldCtx, _ float64) error { return u.onValue(ctx) } 90 | 91 | func (u *{{ $tUnfolder }}) OnArrayStart(ctx *unfoldCtx, _ int, _ structform.BaseType) error { 92 | _singletonUnfoldIgnoreArrPtr.initState(ctx, nil) 93 | return nil 94 | } 95 | 96 | func (u *{{ $tUnfolder }}) OnChildArrayDone(ctx *unfoldCtx) error { 97 | return u.onValue(ctx) 98 | } 99 | 100 | func (u *{{ $tUnfolder }}) OnObjectStart(ctx *unfoldCtx, _ int, _ structform.BaseType) error { 101 | _singletonUnfoldIgnoreObjPtr.initState(ctx, nil) 102 | return nil 103 | } 104 | 105 | func (u *{{ $tUnfolder }}) OnChildObjectDone(ctx *unfoldCtx) error { 106 | return u.onValue(ctx) 107 | } 108 | -------------------------------------------------------------------------------- /gotype/unfold_lookup.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | /* 21 | func lookupReflUnfolder(t reflect.Type) (reflUnfolder, error) { 22 | // we always expect a pointer to a value 23 | bt := t.Elem() 24 | 25 | switch bt.Kind() { 26 | case reflect.Array: 27 | switch bt.Elem().Kind() { 28 | case reflect.Int: 29 | return liftGoUnfolder(newUnfolderArrInt()), nil 30 | } 31 | 32 | case reflect.Slice: 33 | 34 | case reflect.Map: 35 | if bt.Key().Kind() != reflect.String { 36 | return nil, errMapRequiresStringKey 37 | } 38 | 39 | switch bt.Elem().Kind() { 40 | case reflect.Interface: 41 | return liftGoUnfolder(newUnfolderMapIfc()), nil 42 | } 43 | 44 | case reflect.Struct: 45 | 46 | } 47 | 48 | return nil, errTODO() 49 | } 50 | */ 51 | -------------------------------------------------------------------------------- /gotype/unfold_lookup_go.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | import: 19 | - unfold_templates.yml 20 | 21 | main: | 22 | package gotype 23 | 24 | func lookupUserPrimitiveConstructor(t reflect.Type) func(reflect.Value) ptrUnfolder { 25 | switch t.Kind() { 26 | {{- range data.primitiveTypes }} 27 | case reflect.{{ capitalize . }}: 28 | return {{ capitalize . | printf "newUserUnfolder%v" }} 29 | {{ end }} 30 | default: 31 | return nil 32 | } 33 | } 34 | 35 | func lookupGoTypeUnfolder(to interface{}) (unsafe.Pointer, ptrUnfolder) { 36 | switch ptr := to.(type) { 37 | case *interface{}: 38 | return unsafe.Pointer(ptr), newUnfolderIfc() 39 | case *[]interface{}: 40 | return unsafe.Pointer(ptr), newUnfolderArrIfc() 41 | case *map[string]interface{}: 42 | return unsafe.Pointer(ptr), newUnfolderMapIfc() 43 | 44 | {{ range data.primitiveTypes }} 45 | case *{{ . }}: 46 | return unsafe.Pointer(ptr), newUnfolder{{ . | capitalize }}() 47 | case *[]{{ . }}: 48 | return unsafe.Pointer(ptr), newUnfolderArr{{ . | capitalize }}() 49 | case *map[string]{{ . }}: 50 | return unsafe.Pointer(ptr), newUnfolderMap{{ . | capitalize }}() 51 | {{ end }} 52 | default: 53 | return nil, nil 54 | } 55 | } 56 | 57 | func lookupGoPtrUnfolder(t reflect.Type) (ptrUnfolder) { 58 | switch t.Kind() { 59 | case reflect.Interface: 60 | return newUnfolderIfc() 61 | {{ range data.primitiveTypes }} 62 | case reflect.{{ . | capitalize }}: 63 | return newUnfolder{{ . | capitalize }}() 64 | {{ end }} 65 | 66 | case reflect.Slice: 67 | et := t.Elem() 68 | switch et.Kind() { 69 | case reflect.Interface: 70 | return newUnfolderArrIfc() 71 | {{ range data.primitiveTypes }} 72 | case reflect.{{ . | capitalize }}: 73 | return newUnfolderArr{{ . | capitalize }}() 74 | {{ end }} 75 | } 76 | 77 | case reflect.Map: 78 | if t.Key().Kind() != reflect.String { 79 | return nil 80 | } 81 | 82 | et := t.Elem() 83 | switch et.Kind() { 84 | case reflect.Interface: 85 | return newUnfolderMapIfc() 86 | {{ range data.primitiveTypes }} 87 | case reflect.{{ . | capitalize }}: 88 | return newUnfolderMap{{ . | capitalize }}() 89 | {{ end }} 90 | } 91 | 92 | } 93 | 94 | return nil 95 | } 96 | 97 | func lookupReflUnfolder(ctx *unfoldCtx, t reflect.Type, withUser bool) (reflUnfolder, error) { 98 | if withUser { 99 | if f := lookupReflUser(ctx, t); f != nil { 100 | return f, nil 101 | } 102 | } 103 | 104 | if t.Implements(tExpander) { 105 | return newExpanderInit(), nil 106 | } 107 | 108 | if f := ctx.reg.find(t); f != nil { 109 | return f, nil 110 | } 111 | 112 | f, err := buildReflUnfolder(ctx, t) 113 | if err != nil { 114 | return nil, err 115 | } 116 | 117 | ctx.reg.set(t, f) 118 | return f, nil 119 | } 120 | 121 | func lookupReflUser(ctx *unfoldCtx, t reflect.Type) reflUnfolder { 122 | if ctx.userReg != nil { 123 | return ctx.userReg[t] 124 | } 125 | return nil 126 | } 127 | 128 | func buildReflUnfolder(ctx *unfoldCtx, t reflect.Type) (reflUnfolder, error) { 129 | // we always expect a pointer 130 | bt := t.Elem() 131 | 132 | switch bt.Kind() { 133 | case reflect.Interface: 134 | return unfolderReflIfc, nil 135 | {{ range data.primitiveTypes }} 136 | case reflect.{{ . | capitalize }}: 137 | return unfolderRefl{{ . | capitalize }}, nil 138 | {{ end }} 139 | 140 | case reflect.Array: 141 | return nil, errTODO() 142 | 143 | case reflect.Ptr: 144 | unfolderElem, err := lookupReflUnfolder(ctx, bt, true) 145 | if err != nil { 146 | return nil, err 147 | } 148 | return newUnfolderReflPtr(unfolderElem), nil 149 | 150 | case reflect.Slice: 151 | et := bt.Elem() 152 | 153 | if unfolderElem := lookupReflUser(ctx, et); unfolderElem != nil { 154 | return newUnfolderReflSlice(unfolderElem), nil 155 | } 156 | 157 | if reflect.PtrTo(et).Implements(tExpander) { 158 | return newUnfolderReflSlice(newExpanderInit()), nil 159 | } 160 | 161 | switch et.Kind() { 162 | case reflect.Interface: 163 | return unfolderReflArrIfc, nil 164 | {{ range data.primitiveTypes }} 165 | case reflect.{{ . | capitalize }}: 166 | return unfolderReflArr{{ . | capitalize }}, nil 167 | {{ end }} 168 | } 169 | 170 | unfolderElem, err := lookupReflUnfolder(ctx, reflect.PtrTo(et), false) 171 | if err != nil { 172 | return nil, err 173 | } 174 | return newUnfolderReflSlice(unfolderElem), nil 175 | 176 | case reflect.Map: 177 | et := bt.Elem() 178 | 179 | if unfolderElem := lookupReflUser(ctx, et); unfolderElem != nil { 180 | return newUnfolderReflMap(unfolderElem), nil 181 | } 182 | 183 | if reflect.PtrTo(et).Implements(tExpander) { 184 | return newUnfolderReflMap(newExpanderInit()), nil 185 | } 186 | 187 | switch et.Kind() { 188 | case reflect.Interface: 189 | return unfolderReflMapIfc, nil 190 | {{ range data.primitiveTypes }} 191 | case reflect.{{ . | capitalize }}: 192 | return unfolderReflMap{{ . | capitalize }}, nil 193 | {{ end }} 194 | } 195 | 196 | unfolderElem, err := lookupReflUnfolder(ctx, reflect.PtrTo(et), false) 197 | if err != nil { 198 | return nil, err 199 | } 200 | return newUnfolderReflMap(unfolderElem), nil 201 | 202 | case reflect.Struct: 203 | return createUnfolderReflStruct(ctx, t) 204 | 205 | default: 206 | return nil, errTODO() 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /gotype/unfold_map.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | import: 19 | - unfold_templates.yml 20 | 21 | main: | 22 | package gotype 23 | 24 | import "github.com/urso/go-structform" 25 | 26 | {{/* defined 'lifted' pointer map unfolders into reflection based unfolders */}} 27 | var ( 28 | unfolderReflMapIfc = liftGoUnfolder(newUnfolderMapIfc()) 29 | {{ range data.primitiveTypes }} 30 | {{ $t := capitalize . }} 31 | unfolderReflMap{{ $t }} = liftGoUnfolder(newUnfolderMap{{ $t }}()) 32 | {{ end }} 33 | ) 34 | 35 | {{/* define pointer based unfolder types */}} 36 | {{ invoke "makeTypeWithName" "name" "Ifc" "type" "interface{}" }} 37 | {{ template "makeType" "bool" }} 38 | {{ template "makeType" "string" }} 39 | {{ range .numTypes }} 40 | {{ template "makeType" . }} 41 | {{ end }} 42 | 43 | {{/* create value visitor callbacks */}} 44 | {{ invoke "onIfcFns" "name" "unfolderMapIfc" "fn" "put" }} 45 | {{ invoke "onBoolFns" "name" "unfolderMapBool" "fn" "put" }} 46 | {{ invoke "onStringFns" "name" "unfolderMapString" "fn" "put" }} 47 | {{ range .numTypes }} 48 | {{ $type := . }} 49 | {{ $name := capitalize $type | printf "unfolderMap%v" }} 50 | {{ invoke "onNumberFns" "name" $name "type" $type "fn" "put" }} 51 | {{ end }} 52 | 53 | {{ template "mapIfc" }} 54 | 55 | 56 | # makeTypeWithName(name, type) 57 | templates.makeTypeWithName: | 58 | {{ $type := .type }} 59 | {{ $name := capitalize .name | printf "unfolderMap%v" }} 60 | {{ $startName := capitalize .name | printf "unfoldMapStart%v" }} 61 | {{ $keyName := capitalize .name | printf "unfoldMapKey%v" }} 62 | 63 | {{ invoke "makeUnfoldType" "name" $name }} 64 | {{ invoke "makeUnfoldType" "name" $startName "base" "unfolderErrObjectStart" }} 65 | {{ invoke "makeUnfoldType" "name" $keyName "base" "unfolderErrExpectKey" }} 66 | 67 | func (u *{{ $name }} ) initState(ctx *unfoldCtx, ptr unsafe.Pointer) { 68 | ctx.unfolder.push(new{{ $keyName | capitalize}}()) 69 | ctx.unfolder.push(new{{ $startName | capitalize}}()) 70 | ctx.ptr.push(ptr) 71 | } 72 | 73 | func (u * {{ $keyName }} ) cleanup(ctx *unfoldCtx) { 74 | ctx.unfolder.pop() 75 | ctx.ptr.pop() 76 | } 77 | 78 | func (u * {{ $startName }}) cleanup(ctx *unfoldCtx) { 79 | ctx.unfolder.pop() 80 | } 81 | 82 | 83 | func (u *{{ $name }} ) ptr(ctx *unfoldCtx) *map[string]{{ $type }} { 84 | return (*map[string]{{ $type }})(ctx.ptr.current) 85 | } 86 | 87 | 88 | func (u *{{ $startName }} ) OnObjectStart(ctx *unfoldCtx, l int, baseType structform.BaseType) error { 89 | // TODO: validate baseType 90 | 91 | u.cleanup(ctx) 92 | return nil 93 | } 94 | 95 | func (u *{{ $keyName }} ) OnKeyRef(ctx *unfoldCtx, key []byte) error { 96 | return u.OnKey(ctx, ctx.keyCache.get(key)) 97 | } 98 | 99 | func (u *{{ $keyName }} ) OnKey(ctx *unfoldCtx, key string) error { 100 | ctx.key.push(key) 101 | ctx.unfolder.current = new{{ $name | capitalize }}() 102 | return nil 103 | } 104 | 105 | func (u *{{ $keyName }} ) OnObjectFinished(ctx *unfoldCtx) error { 106 | u.cleanup(ctx) 107 | return nil 108 | } 109 | 110 | func (u *{{ $name }} ) put(ctx *unfoldCtx, v {{ $type }}) error { 111 | to := u.ptr(ctx) 112 | if *to == nil { 113 | *to = map[string]{{ $type }}{} 114 | } 115 | (*to)[ctx.key.pop()] = v 116 | 117 | ctx.unfolder.current = new{{ $keyName | capitalize }}() 118 | return nil 119 | } 120 | 121 | templates.mapIfc: | 122 | func unfoldIfcStartSubMap(ctx *unfoldCtx, l int, baseType structform.BaseType) error { 123 | _, ptr, unfolder := makeMapPtr(ctx, l, baseType) 124 | ctx.ptr.push(ptr) 125 | ctx.baseType.push(baseType) 126 | unfolder.initState(ctx, ptr) 127 | return ctx.unfolder.current.OnObjectStart(ctx, l, baseType) 128 | } 129 | 130 | func unfoldIfcFinishSubMap(ctx *unfoldCtx) (interface{}, error) { 131 | child := ctx.ptr.pop() 132 | bt := ctx.baseType.pop() 133 | switch bt { 134 | {{ range $bt, $gt := data.mapTypes }} 135 | case structform.{{ $bt }}: 136 | value := *(*map[string]{{ $gt }})(child) 137 | {{- if or (eq $bt "AnyType") (eq $bt "ZeroType") }} 138 | last := len(ctx.valueBuffer.mapAny)-1 139 | ctx.valueBuffer.mapAny = ctx.valueBuffer.mapAny[:last] 140 | {{ else }} 141 | last := len(ctx.valueBuffer.mapPrimitive)-1 142 | ctx.valueBuffer.mapPrimitive = ctx.valueBuffer.mapPrimitive[:last] 143 | {{ end -}} 144 | return value, nil 145 | 146 | {{ end }} 147 | default: 148 | return nil, errTODO() 149 | } 150 | } 151 | 152 | func makeMapPtr(ctx *unfoldCtx, l int, bt structform.BaseType) (interface{}, unsafe.Pointer, ptrUnfolder) { 153 | switch bt { 154 | {{ range $bt, $gt := data.mapTypes }} 155 | case structform.{{ $bt }}: 156 | {{- if or (eq $bt "AnyType") (eq $bt "ZeroType") }} 157 | idx := len(ctx.valueBuffer.mapAny) 158 | ctx.valueBuffer.mapAny = append(ctx.valueBuffer.mapAny, nil) 159 | to := &ctx.valueBuffer.mapAny[idx] 160 | ptr := unsafe.Pointer(to) 161 | unfolder := newUnfolderMapIfc() 162 | return to, ptr, unfolder 163 | {{ else }} 164 | idx := len(ctx.valueBuffer.mapPrimitive) 165 | ctx.valueBuffer.mapPrimitive = append(ctx.valueBuffer.mapPrimitive, nil) 166 | mapPtr := &ctx.valueBuffer.mapPrimitive[idx] 167 | ptr := unsafe.Pointer(mapPtr) 168 | to := (*map[string]{{ $gt }})(ptr) 169 | unfolder := newUnfolderMap{{ $gt | capitalize }}() 170 | return to, ptr, unfolder 171 | {{ end }} 172 | 173 | {{ end }} 174 | default: 175 | panic("invalid type code") 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /gotype/unfold_opts.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import ( 21 | "reflect" 22 | ) 23 | 24 | type initUnfoldOptions struct { 25 | unfoldFns map[reflect.Type]reflUnfolder 26 | } 27 | 28 | type UnfoldOption func(*initUnfoldOptions) error 29 | 30 | func applyUnfoldOpts(opts []UnfoldOption) (i initUnfoldOptions, err error) { 31 | for _, o := range opts { 32 | if err = o(&i); err != nil { 33 | break 34 | } 35 | } 36 | return i, err 37 | } 38 | 39 | // Unfolders accepts a list of primitive, processing, or stateful unfolders. 40 | // 41 | // Primitive unfolder must implement a function matching the type: func(to *Target, from P) error 42 | // Where to is an arbitrary go type that the result should be written to and 43 | // P must be one of: bool, string, uint(8|16|32|64), int(8|16|32|64), float(32|64) 44 | // 45 | // Processing unfolders first unfold a structure into a temporary structure, followed 46 | // by a post-processing function used to fill in the original target. Processing unfolders 47 | // for type T have the signature: 48 | // func(to *T) (cell interface{}, process func(to *T, cell interface{}) error) 49 | // 50 | // A processing unfolder returns a temporary value for unfolding. The Unfolder will process 51 | // the temporary value (held in cell), like any regular supported value. 52 | // The process function is executed if the parsing step did succeed. 53 | // The address to the target structure and the original cell are reported to the process function, 54 | // reducing the need for allocation storage on the heap in most simple cases. 55 | // 56 | // Stateful unfolders have the function signature: func(to *T) UnfoldState. 57 | // The state returned by the initialization function is used for parsing. 58 | // Although stateful unfolders allow for the most complex unfolding possible, 59 | // they add the most overhead in managing state and allocations. If possible 60 | // prefer primitive unfolders, followed by processing unfolder. 61 | func Unfolders(in ...interface{}) UnfoldOption { 62 | unfolders, err := makeUserUnfolderFns(in) 63 | if err != nil || len(unfolders) == 0 { 64 | return func(_ *initUnfoldOptions) error { return err } 65 | } 66 | 67 | return func(o *initUnfoldOptions) error { 68 | if o.unfoldFns == nil { 69 | o.unfoldFns = map[reflect.Type]reflUnfolder{} 70 | } 71 | 72 | for k, v := range unfolders { 73 | o.unfoldFns[k] = v 74 | } 75 | return nil 76 | } 77 | } 78 | 79 | func makeUserUnfolderFns(in []interface{}) (map[reflect.Type]reflUnfolder, error) { 80 | M := map[reflect.Type]reflUnfolder{} 81 | 82 | for _, cur := range in { 83 | if cur == nil { 84 | continue 85 | } 86 | 87 | t, unfolder, err := makeUserUnfolder(reflect.ValueOf(cur)) 88 | if err != nil { 89 | return nil, err 90 | } 91 | 92 | M[t] = unfolder 93 | } 94 | 95 | return M, nil 96 | } 97 | -------------------------------------------------------------------------------- /gotype/unfold_primitive.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | import: 19 | - unfold_templates.yml 20 | 21 | main: | 22 | package gotype 23 | 24 | var ( 25 | {{/* defined 'lifted' pointer primitive unfolders into reflection based unfolders */}} 26 | unfolderReflIfc = liftGoUnfolder(newUnfolderIfc()) 27 | {{ range data.primitiveTypes }} 28 | {{ $t := capitalize . }} 29 | unfolderRefl{{ $t }} = liftGoUnfolder(newUnfolder{{ $t }}()) 30 | {{ end }} 31 | ) 32 | 33 | {{/* define pointer based unfolder types */}} 34 | {{ invoke "makeTypeWithName" "name" "ifc" "type" "interface{}" }} 35 | {{ template "makeType" "bool" }} 36 | {{ template "makeType" "string" }} 37 | {{ range .numTypes }} 38 | {{ template "makeType" . }} 39 | {{ end }} 40 | 41 | {{/* create value visitor callbacks */}} 42 | {{ invoke "onIfcFns" "name" "unfolderIfc" "fn" "assign" }} 43 | {{ invoke "onBoolFns" "name" "unfolderBool" "fn" "assign" }} 44 | {{ invoke "onStringFns" "name" "unfolderString" "fn" "assign" }} 45 | {{ range .numTypes }} 46 | {{ $type := . }} 47 | {{ $name := capitalize . | printf "unfolder%v" }} 48 | {{ invoke "onNumberFns" "name" $name "type" $type "fn" "assign" }} 49 | {{ end }} 50 | 51 | /* 52 | func (*unfolderIfc) OnArrayStart(ctx *unfoldCtx, l int, bt structform.BaseType) error { 53 | return unfoldIfcStartSubArray(ctx, l, bt) 54 | } 55 | 56 | func (u *unfolderIfc) OnChildArrayDone(ctx *unfoldCtx) error { 57 | v, err := unfoldIfcFinishSubArray(ctx) 58 | if err == nil { 59 | err = u.assign(ctx, v) 60 | } 61 | return err 62 | } 63 | */ 64 | 65 | # makeTypeWithName(name, type, [base]) 66 | templates.makeTypeWithName: | 67 | {{ $type := .type }} 68 | {{ $name := capitalize .name | printf "unfolder%v" }} 69 | {{ invoke "makeUnfoldType" "type" $type "name" $name "base" .base }} 70 | 71 | func (u *{{ $name }} ) initState(ctx *unfoldCtx, ptr unsafe.Pointer) { 72 | ctx.unfolder.push(u) 73 | ctx.ptr.push(ptr) 74 | } 75 | 76 | func (u *{{ $name }} ) cleanup(ctx *unfoldCtx) { 77 | ctx.unfolder.pop() 78 | ctx.ptr.pop() 79 | } 80 | 81 | func (u *{{ $name }} ) ptr(ctx *unfoldCtx) *{{ $type }} { 82 | return (*{{ $type }})(ctx.ptr.current) 83 | } 84 | 85 | func (u *{{ $name }}) assign(ctx *unfoldCtx, v {{ $type }}) error { 86 | *u.ptr(ctx) = v 87 | u.cleanup(ctx) 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /gotype/unfold_refl.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import ( 21 | "reflect" 22 | "unsafe" 23 | 24 | structform "github.com/elastic/go-structform" 25 | ) 26 | 27 | type liftedReflUnfolder struct{ unfolder ptrUnfolder } 28 | 29 | type unfolderReflSlice struct { 30 | elem reflUnfolder 31 | } 32 | 33 | type unfolderReflSliceStart struct { 34 | unfolderErrArrayStart 35 | } 36 | 37 | type unfolderReflMap struct { 38 | shared unfolderReflMapShared 39 | } 40 | 41 | type unfolderReflMapShared struct { 42 | waitKey *unfolderReflMapOnKey 43 | waitElem *unfolderReflMapOnElem 44 | } 45 | 46 | type unfolderReflMapStart struct { 47 | unfolderErrObjectStart 48 | } 49 | 50 | type unfolderReflMapOnKey struct { 51 | unfolderErrExpectKey 52 | shared *unfolderReflMapShared 53 | } 54 | 55 | type unfolderReflMapOnElem struct { 56 | shared *unfolderReflMapShared 57 | elem reflUnfolder 58 | } 59 | 60 | type unfolderReflPtr struct { 61 | elem reflUnfolder 62 | } 63 | 64 | var ( 65 | _singletonUnfolderReflSliceStart = &unfolderReflSliceStart{} 66 | _singletonUnfolderReflMapStart = &unfolderReflMapStart{} 67 | ) 68 | 69 | func liftGoUnfolder(u ptrUnfolder) *liftedReflUnfolder { return &liftedReflUnfolder{u} } 70 | 71 | func (u *liftedReflUnfolder) initState(ctx *unfoldCtx, v reflect.Value) { 72 | ptr := unsafe.Pointer(v.Pointer()) 73 | u.unfolder.initState(ctx, ptr) 74 | } 75 | 76 | func newUnfolderReflSlice(elem reflUnfolder) *unfolderReflSlice { 77 | return &unfolderReflSlice{elem} 78 | } 79 | 80 | func (u *unfolderReflSlice) initState(ctx *unfoldCtx, v reflect.Value) { 81 | ctx.value.push(v) 82 | ctx.unfolder.push(u) 83 | ctx.idx.push(0) 84 | ctx.unfolder.push(_singletonUnfolderReflSliceStart) 85 | } 86 | 87 | func (u *unfolderReflSlice) cleanup(ctx *unfoldCtx) { 88 | ctx.idx.pop() 89 | ctx.value.pop() 90 | ctx.unfolder.pop() 91 | } 92 | 93 | func (u *unfolderReflSliceStart) cleanup(ctx *unfoldCtx) { 94 | ctx.unfolder.pop() 95 | } 96 | 97 | func (u *unfolderReflSliceStart) OnArrayStart(ctx *unfoldCtx, l int, baseType structform.BaseType) error { 98 | 99 | ptr := ctx.value.current 100 | v := ptr.Elem() 101 | 102 | if l < 0 { 103 | l = 0 104 | } 105 | 106 | if v.IsNil() && l > 0 { 107 | v.Set(reflect.MakeSlice(v.Type(), l, l)) 108 | } else if !v.IsNil() && l < v.Len() { 109 | v.SetLen(l) 110 | } 111 | 112 | u.cleanup(ctx) 113 | return nil 114 | } 115 | 116 | func (u *unfolderReflSlice) OnArrayFinished(ctx *unfoldCtx) error { 117 | u.cleanup(ctx) 118 | return nil 119 | } 120 | 121 | func (u *unfolderReflSlice) prepare(ctx *unfoldCtx) reflect.Value { 122 | // make space for some more element 123 | ptr := ctx.value.current 124 | idx := &ctx.idx 125 | v := ptr.Elem() 126 | 127 | switch { 128 | case v.Len() > idx.current: 129 | 130 | case v.Cap() > idx.current: 131 | v.SetLen(idx.current + 1) 132 | 133 | default: 134 | v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem()))) 135 | } 136 | 137 | elem := v.Index(idx.current).Addr() 138 | idx.current++ 139 | 140 | return elem 141 | } 142 | 143 | func (u *unfolderReflSlice) OnObjectFinished(_ *unfoldCtx) error { 144 | return errUnsupported 145 | } 146 | 147 | func newUnfolderReflMap(elem reflUnfolder) *unfolderReflMap { 148 | u := &unfolderReflMap{} 149 | u.shared.waitKey = &unfolderReflMapOnKey{shared: &u.shared} 150 | u.shared.waitElem = &unfolderReflMapOnElem{shared: &u.shared, elem: elem} 151 | return u 152 | } 153 | 154 | func (u *unfolderReflMap) initState(ctx *unfoldCtx, v reflect.Value) { 155 | ctx.value.push(v) 156 | ctx.unfolder.push(u.shared.waitKey) 157 | ctx.unfolder.push(_singletonUnfolderReflMapStart) 158 | } 159 | 160 | func (u *unfolderReflMapStart) OnObjectStart(ctx *unfoldCtx, l int, bt structform.BaseType) error { 161 | ctx.unfolder.pop() 162 | return nil 163 | } 164 | 165 | func (u *unfolderReflMapOnKey) OnKey(ctx *unfoldCtx, key string) error { 166 | ctx.key.push(key) 167 | ctx.unfolder.current = u.shared.waitElem 168 | return nil 169 | } 170 | 171 | func (u *unfolderReflMapOnKey) OnKeyRef(ctx *unfoldCtx, key []byte) error { 172 | return u.OnKey(ctx, ctx.keyCache.get(key)) 173 | } 174 | 175 | func (u *unfolderReflMapOnKey) OnObjectFinished(ctx *unfoldCtx) error { 176 | ctx.unfolder.pop() 177 | ctx.value.pop() 178 | return nil 179 | } 180 | 181 | func (u *unfolderReflMapOnElem) prepare(ctx *unfoldCtx) reflect.Value { 182 | ptr := ctx.value.current 183 | v := ptr.Elem() 184 | et := v.Type().Elem() 185 | 186 | target := reflect.New(et) 187 | ctx.value.push(target) 188 | return target 189 | } 190 | 191 | func (u *unfolderReflMapOnElem) process(ctx *unfoldCtx) { 192 | ptr := ctx.value.pop() 193 | v := ptr.Elem() 194 | 195 | ptr = ctx.value.current 196 | m := ptr.Elem() 197 | m.SetMapIndex(reflect.ValueOf(ctx.key.pop()), v) 198 | 199 | ctx.unfolder.current = u.shared.waitKey 200 | } 201 | 202 | func (u *unfolderReflMapOnElem) OnObjectFinished(_ *unfoldCtx) error { return errExpectedObjectValue } 203 | func (u *unfolderReflMapOnElem) OnArrayFinished(_ *unfoldCtx) error { return errUnsupported } 204 | 205 | func newUnfolderReflPtr(elem reflUnfolder) *unfolderReflPtr { 206 | return &unfolderReflPtr{elem} 207 | } 208 | 209 | func (u *unfolderReflPtr) initState(ctx *unfoldCtx, v reflect.Value) { 210 | ctx.value.push(v) 211 | ctx.unfolder.push(u) 212 | } 213 | 214 | func (u *unfolderReflPtr) cleanup(ctx *unfoldCtx) { 215 | ctx.value.pop() 216 | ctx.unfolder.pop() 217 | } 218 | 219 | func (u *unfolderReflPtr) prepare(ctx *unfoldCtx) reflect.Value { 220 | ptr := ctx.value.current 221 | 222 | v := ptr.Elem() 223 | target := reflect.New(v.Type().Elem()) 224 | ctx.value.push(target) 225 | return target 226 | } 227 | 228 | func (u *unfolderReflPtr) process(ctx *unfoldCtx) { 229 | v := ctx.value.pop() 230 | ptr := ctx.value.current.Elem() 231 | ptr.Set(v) 232 | u.cleanup(ctx) 233 | } 234 | 235 | func (u *unfolderReflPtr) OnObjectFinished(_ *unfoldCtx) error { return errUnsupported } 236 | func (u *unfolderReflPtr) OnArrayFinished(_ *unfoldCtx) error { return errUnsupported } 237 | -------------------------------------------------------------------------------- /gotype/unfold_refl.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | import: 19 | - unfold_templates.yml 20 | 21 | main: | 22 | package gotype 23 | 24 | func (u *unfolderReflSlice) OnNil(ctx *unfoldCtx) error { 25 | u.prepare(ctx) 26 | return nil 27 | } 28 | 29 | {{ invoke "makeReflPrimitives" "type" "unfolderReflSlice" }} 30 | {{ invoke "makeReflChildArrays" "type" "unfolderReflSlice" }} 31 | {{ invoke "makeReflChildObjects" "type" "unfolderReflSlice" }} 32 | 33 | func (u *unfolderReflMapOnElem) OnNil(ctx *unfoldCtx) error { 34 | ptr := ctx.value.current 35 | m := ptr.Elem() 36 | v := reflect.Zero(m.Type().Elem()) 37 | m.SetMapIndex(reflect.ValueOf(ctx.key.pop()), v) 38 | 39 | ctx.unfolder.current = u.shared.waitKey 40 | return nil 41 | } 42 | 43 | {{ invoke "makeReflPrimitives" "type" "unfolderReflMapOnElem" "process" "process" }} 44 | {{ invoke "makeReflChildArrays" "type" "unfolderReflMapOnElem" "process" "process" }} 45 | {{ invoke "makeReflChildObjects" "type" "unfolderReflMapOnElem" "process" "process" "error" "errExpectedObjectValue" }} 46 | 47 | func (u *unfolderReflPtr) OnNil(ctx *unfoldCtx) error { 48 | ptr := ctx.value.current 49 | v := ptr.Elem() 50 | v.Set(reflect.Zero(v.Type())) 51 | u.cleanup(ctx) 52 | return nil 53 | } 54 | 55 | {{ invoke "makeReflPrimitives" "type" "unfolderReflPtr" "process" "process" }} 56 | {{ invoke "makeReflChildArrays" "type" "unfolderReflPtr" "process" "process" }} 57 | {{ invoke "makeReflChildObjects" "type" "unfolderReflPtr" "process" "process" }} 58 | 59 | # makeReflPrimitiveCallbacks(type, [process]) 60 | templates.makeReflPrimitives: | 61 | {{ $type := .type}} 62 | {{ $process := .process }} 63 | 64 | func (u *{{ $type }}) OnByte(ctx *unfoldCtx, v byte) error { 65 | elem := u.prepare(ctx) 66 | u.elem.initState(ctx, elem) 67 | err := ctx.unfolder.current.OnByte(ctx, v) 68 | {{ if $process }} 69 | if err == nil { 70 | u.{{ $process }}(ctx) 71 | } 72 | {{ end }} 73 | return err 74 | } 75 | 76 | func (u *{{ $type }}) OnStringRef(ctx *unfoldCtx, v []byte) error { 77 | return u.OnString(ctx, string(v)) 78 | } 79 | 80 | {{ range data.primitiveTypes }} 81 | func (u *{{ $type }}) On{{ . | capitalize}}(ctx *unfoldCtx, v {{ . }}) error { 82 | elem := u.prepare(ctx) 83 | u.elem.initState(ctx, elem) 84 | err := ctx.unfolder.current.On{{ . | capitalize }}(ctx, v) 85 | {{ if $process }} 86 | if err == nil { 87 | u.{{ $process }}(ctx) 88 | } 89 | {{ end }} 90 | return err 91 | } 92 | {{ end }} 93 | 94 | # makeReflChildArrays(type, [process]) 95 | templates.makeReflChildArrays: | 96 | {{ $type := .type}} 97 | {{ $process := .process }} 98 | 99 | func (u *{{ $type }}) OnArrayStart(ctx *unfoldCtx, l int, bt structform.BaseType) error { 100 | elem := u.prepare(ctx) 101 | u.elem.initState(ctx, elem) 102 | return ctx.unfolder.current.OnArrayStart(ctx, l, bt) 103 | } 104 | 105 | func (u *{{ $type }}) OnChildArrayDone(ctx *unfoldCtx) error { 106 | {{ if $process }} 107 | u.{{ $process }}(ctx) 108 | {{ end }} 109 | return nil 110 | } 111 | 112 | # makeReflChildObjects(type, [process], [error]) 113 | templates.makeReflChildObjects: | 114 | {{ $type := .type}} 115 | {{ $process := .process }} 116 | {{ $error := default "errUnsupported" .error }} 117 | 118 | func (u *{{ $type }}) OnObjectStart(ctx *unfoldCtx, l int, bt structform.BaseType) error { 119 | elem := u.prepare(ctx) 120 | u.elem.initState(ctx, elem) 121 | return ctx.unfolder.current.OnObjectStart(ctx, l, bt) 122 | } 123 | 124 | func (u *{{ $type }}) OnKey(_ *unfoldCtx, _ string) error { 125 | return {{ $error }} 126 | } 127 | 128 | func (u *{{ $type }}) OnKeyRef(_ *unfoldCtx, _ []byte) error { 129 | return {{ $error }} 130 | } 131 | 132 | func (u *{{ $type }}) OnChildObjectDone(ctx *unfoldCtx) error { 133 | {{ if $process }} 134 | u.{{ $process }}(ctx) 135 | {{ end }} 136 | return nil 137 | } 138 | 139 | -------------------------------------------------------------------------------- /gotype/unfold_struct.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package gotype 19 | 20 | import ( 21 | "fmt" 22 | "reflect" 23 | "strings" 24 | "unicode" 25 | "unicode/utf8" 26 | "unsafe" 27 | 28 | structform "github.com/elastic/go-structform" 29 | ) 30 | 31 | type unfolderStruct struct { 32 | unfolderErrExpectKey 33 | fields map[string]fieldUnfolder 34 | } 35 | 36 | type unfolderStructStart struct { 37 | unfolderErrObjectStart 38 | } 39 | 40 | type fieldUnfolder struct { 41 | offset uintptr 42 | initState func(ctx *unfoldCtx, sp unsafe.Pointer) 43 | } 44 | 45 | var ( 46 | _singletonUnfolderStructStart = &unfolderStructStart{} 47 | 48 | _ignoredField = &fieldUnfolder{ 49 | initState: _singletonUnfoldIgnorePtr.initState, 50 | } 51 | ) 52 | 53 | func createUnfolderReflStruct(ctx *unfoldCtx, t reflect.Type) (*unfolderStruct, error) { 54 | // assume t is pointer to struct 55 | t = t.Elem() 56 | 57 | fields, err := fieldUnfolders(ctx, t) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | u := &unfolderStruct{fields: fields} 63 | return u, nil 64 | } 65 | 66 | func fieldUnfolders(ctx *unfoldCtx, t reflect.Type) (map[string]fieldUnfolder, error) { 67 | count := t.NumField() 68 | fields := map[string]fieldUnfolder{} 69 | 70 | for i := 0; i < count; i++ { 71 | st := t.Field(i) 72 | 73 | name := st.Name 74 | rune, _ := utf8.DecodeRuneInString(name) 75 | if !unicode.IsUpper(rune) { 76 | continue 77 | } 78 | 79 | tagName, tagOpts := parseTags(st.Tag.Get(ctx.opts.tag)) 80 | if tagOpts.omit { 81 | continue 82 | } 83 | 84 | if tagOpts.squash { 85 | if st.Type.Kind() != reflect.Struct { 86 | return nil, errSquashNeedObject 87 | } 88 | 89 | sub, err := fieldUnfolders(ctx, st.Type) 90 | if err != nil { 91 | return nil, err 92 | } 93 | 94 | for name, fu := range sub { 95 | fu.offset += st.Offset 96 | if _, exists := fields[name]; exists { 97 | return nil, fmt.Errorf("duplicate field name %v", name) 98 | } 99 | 100 | fields[name] = fu 101 | } 102 | } else { 103 | if tagName != "" { 104 | name = tagName 105 | } else { 106 | name = strings.ToLower(name) 107 | } 108 | 109 | if _, exists := fields[name]; exists { 110 | return nil, fmt.Errorf("duplicate field name %v", name) 111 | } 112 | 113 | fu, err := makeFieldUnfolder(ctx, st) 114 | if err != nil { 115 | return nil, err 116 | } 117 | 118 | fields[name] = fu 119 | } 120 | } 121 | 122 | return fields, nil 123 | } 124 | 125 | func makeFieldUnfolder(ctx *unfoldCtx, st reflect.StructField) (fieldUnfolder, error) { 126 | fu := fieldUnfolder{offset: st.Offset} 127 | targetType := reflect.PtrTo(st.Type) 128 | 129 | if uu := lookupReflUser(ctx, targetType); uu != nil { 130 | fu.initState = wrapReflUnfolder(st.Type, uu) 131 | } else if targetType.Implements(tExpander) { 132 | fu.initState = wrapReflUnfolder(st.Type, newExpanderInit()) 133 | } else if pu := lookupGoPtrUnfolder(st.Type); pu != nil { 134 | fu.initState = pu.initState 135 | } else { 136 | ru, err := lookupReflUnfolder(ctx, targetType, false) 137 | if err != nil { 138 | return fu, err 139 | } 140 | 141 | if su, ok := ru.(*unfolderStruct); ok { 142 | fu.initState = su.initStatePtr 143 | } else { 144 | fu.initState = wrapReflUnfolder(st.Type, ru) 145 | } 146 | } 147 | 148 | return fu, nil 149 | } 150 | 151 | func wrapReflUnfolder(t reflect.Type, ru reflUnfolder) func(*unfoldCtx, unsafe.Pointer) { 152 | return func(ctx *unfoldCtx, ptr unsafe.Pointer) { 153 | v := reflect.NewAt(t, ptr) 154 | ru.initState(ctx, v) 155 | } 156 | } 157 | 158 | func (u *unfolderStruct) initState(ctx *unfoldCtx, v reflect.Value) { 159 | u.initStatePtr(ctx, unsafe.Pointer(v.Pointer())) 160 | } 161 | 162 | func (u *unfolderStruct) initStatePtr(ctx *unfoldCtx, ptr unsafe.Pointer) { 163 | ctx.ptr.push(ptr) 164 | ctx.unfolder.push(u) 165 | ctx.unfolder.push(_singletonUnfolderStructStart) 166 | } 167 | 168 | func (u *unfolderStructStart) OnObjectStart(ctx *unfoldCtx, l int, bt structform.BaseType) error { 169 | ctx.unfolder.pop() 170 | return nil 171 | } 172 | 173 | func (u *unfolderStruct) OnObjectFinished(ctx *unfoldCtx) error { 174 | ctx.unfolder.pop() 175 | ctx.ptr.pop() 176 | return nil 177 | } 178 | 179 | func (u *unfolderStruct) OnChildObjectDone(ctx *unfoldCtx) error { return nil } 180 | func (u *unfolderStruct) OnChildArrayDone(ctx *unfoldCtx) error { return nil } 181 | 182 | func (u *unfolderStruct) OnKeyRef(ctx *unfoldCtx, key []byte) error { 183 | return u.OnKey(ctx, bytes2Str(key)) 184 | } 185 | 186 | func (u *unfolderStruct) OnKey(ctx *unfoldCtx, key string) error { 187 | field, exists := u.fields[key] 188 | if !exists { 189 | _ignoredField.initState(ctx, nil) 190 | return nil 191 | } 192 | 193 | structPtr := ctx.ptr.current 194 | fieldPtr := unsafe.Pointer(uintptr(structPtr) + field.offset) 195 | field.initState(ctx, fieldPtr) 196 | return nil 197 | } 198 | -------------------------------------------------------------------------------- /gotype/unfold_templates.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | import: 19 | - "types.yml" 20 | 21 | # makeUnfoldType(name, [.base]) 22 | templates.makeUnfoldType: | 23 | {{ $name := .name }} 24 | {{ $base := default "unfolderErrUnknown" .base }} 25 | 26 | type {{ $name }} struct { 27 | {{ $base }} 28 | } 29 | 30 | var _singleton{{ $name | capitalize }} = &{{ $name }}{} 31 | 32 | func new{{ $name | capitalize }}() *{{ $name }} { 33 | return _singleton{{ $name | capitalize }} 34 | } 35 | 36 | # makeType(.) -> invokes makeTypeWithName(name, type) 37 | templates.makeType: | 38 | {{ invoke "makeTypeWithName" "name" (capitalize .) "type" . }} 39 | 40 | # makeBoolFns(name, fn) 41 | templates.onBoolFns: | 42 | {{ invoke "onNil" "name" .name "fn" .fn "default" "false" }} 43 | {{ invoke "onBool" "name" .name "fn" .fn }} 44 | 45 | # makeStringFns(name, fn) 46 | templates.onStringFns: | 47 | {{ invoke "onNil" "name" .name "fn" .fn "default" "\"\"" }} 48 | {{ invoke "onString" "name" .name "fn" .fn }} 49 | 50 | # makeNumberFns(name, fn) 51 | templates.onNumberFns: | 52 | {{ invoke "onNil" "name" .name "fn" .fn "default" "0" }} 53 | {{ invoke "onNumber" "name" .name "type" .type "fn" .fn }} 54 | 55 | # onIfcFns(name, fn) 56 | templates.onIfcFns: | 57 | {{ invoke "onNil" "name" .name "fn" .fn "default" "nil" }} 58 | {{ invoke "onBool" "name" .name "fn" .fn }} 59 | {{ invoke "onString" "name" .name "fn" .fn }} 60 | {{ invoke "onNumber" "name" .name "type" "(interface{})" "fn" .fn }} 61 | 62 | func (*{{ .name }} ) OnArrayStart(ctx *unfoldCtx, l int, bt structform.BaseType) error { 63 | return unfoldIfcStartSubArray(ctx, l, bt) 64 | } 65 | 66 | func (u *{{ .name }}) OnChildArrayDone(ctx *unfoldCtx) error { 67 | v, err := unfoldIfcFinishSubArray(ctx) 68 | if err == nil { 69 | err = u.{{ .fn }}(ctx, v) 70 | } 71 | return err 72 | } 73 | 74 | func (*{{ .name }}) OnObjectStart(ctx *unfoldCtx, l int, bt structform.BaseType) error { 75 | return unfoldIfcStartSubMap(ctx, l, bt) 76 | } 77 | 78 | func (u *{{ .name }}) OnChildObjectDone(ctx *unfoldCtx) error { 79 | v, err := unfoldIfcFinishSubMap(ctx) 80 | if err == nil { 81 | err = u.{{ .fn }}(ctx, v) 82 | } 83 | return err 84 | } 85 | 86 | 87 | # onBool(name, fn) 88 | templates.onBool: | 89 | func (u *{{ .name }}) OnBool(ctx *unfoldCtx, v bool) error { return u.{{ .fn }} (ctx, v) } 90 | 91 | # onString(name, fn) 92 | templates.onString: | 93 | func (u *{{ .name }}) OnString(ctx *unfoldCtx, v string) error { return u.{{ .fn }} (ctx, v) } 94 | func (u *{{ .name }}) OnStringRef(ctx *unfoldCtx, v []byte) error { 95 | return u.OnString(ctx, string(v)) 96 | } 97 | 98 | 99 | # onNil(name, fn, default) 100 | templates.onNil: | 101 | func (u *{{ .name }}) OnNil(ctx *unfoldCtx) error { 102 | return u.{{ .fn }}(ctx, {{ .default }}) 103 | } 104 | 105 | 106 | # onNumber(name, fn, type) 107 | templates.onNumber: | 108 | {{ $name := .name }} 109 | {{ $fn := .fn }} 110 | {{ $type := .type }} 111 | 112 | func (u *{{ $name }}) OnByte(ctx *unfoldCtx, v byte) error { 113 | return u.{{ $fn }}(ctx, {{ $type }}(v)) 114 | } 115 | {{ range $t := data.numTypes }} 116 | func (u *{{ $name }}) On{{ $t | capitalize}}(ctx *unfoldCtx, v {{ $t }}) error { 117 | return u.{{ $fn }}(ctx, {{ $type }}(v)) 118 | } 119 | {{ end }} 120 | -------------------------------------------------------------------------------- /gotype/unfold_user_primitive.yml: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | import: 19 | - unfold_templates.yml 20 | 21 | main: | 22 | package gotype 23 | 24 | {{/* define pointer based user unfolder types */}} 25 | {{ template "makeType" "bool" }} 26 | {{ template "makeType" "string" }} 27 | {{ range .numTypes }} 28 | {{ template "makeType" . }} 29 | {{ end }} 30 | 31 | {{/* create value visitor callbacks */}} 32 | {{ invoke "onBoolFns" "name" "userUnfolderBool" "fn" "process" }} 33 | {{ invoke "onStringFns" "name" "userUnfolderString" "fn" "process" }} 34 | {{ range .numTypes }} 35 | {{ $type := . }} 36 | {{ $name := capitalize . | printf "userUnfolder%v" }} 37 | {{ invoke "onNumberFns" "name" $name "type" $type "fn" "process" }} 38 | {{ end }} 39 | 40 | # makeTypeWithName(name, type, [base]) 41 | templates.makeTypeWithName: | 42 | {{ $type := .type }} 43 | {{ $name := capitalize .name | printf "userUnfolder%v" }} 44 | {{ invoke "makeUserUnfoldType" "type" $type "name" $name "base" .base }} 45 | 46 | func (u *{{ $name }} ) initState(ctx *unfoldCtx, ptr unsafe.Pointer) { 47 | ctx.unfolder.push(u) 48 | ctx.ptr.push(ptr) 49 | } 50 | 51 | func (u *{{ $name }} ) cleanup(ctx *unfoldCtx) { 52 | ctx.unfolder.pop() 53 | ctx.ptr.pop() 54 | } 55 | 56 | func (u *{{ $name }}) process(ctx *unfoldCtx, v {{ $type }}) error { 57 | err := u.fn(ctx.ptr.current, v) 58 | u.cleanup(ctx) 59 | return err 60 | } 61 | 62 | templates.makeUserUnfoldType: | 63 | {{ $type := .type }} 64 | {{ $name := .name }} 65 | {{ $cbname := .name | printf "%vCB" }} 66 | {{ $base := default "unfolderErrUnknown" .base }} 67 | 68 | type ( 69 | {{ $name }} struct { 70 | {{ $base }} 71 | fn {{ $cbname }} 72 | } 73 | 74 | {{ $cbname }} func(unsafe.Pointer, {{ $type }}) error 75 | ) 76 | 77 | func new{{ $name | capitalize }}(fn reflect.Value) ptrUnfolder { 78 | return &{{ $name }}{ 79 | fn: *((*{{ $cbname }})(stunsafe.UnsafeFnPtr(fn))), 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /internal/unsafe/unsafe.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package unsafe 19 | 20 | import ( 21 | "reflect" 22 | "runtime" 23 | "unsafe" 24 | ) 25 | 26 | type emptyInterface struct { 27 | typ unsafe.Pointer 28 | word unsafe.Pointer 29 | } 30 | 31 | func Str2Bytes(s string) (b []byte) { 32 | sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) 33 | bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 34 | bh.Data = sh.Data 35 | bh.Cap = sh.Len 36 | bh.Len = sh.Len 37 | runtime.KeepAlive(s) 38 | return 39 | } 40 | 41 | func Bytes2Str(b []byte) (s string) { 42 | bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 43 | sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) 44 | sh.Data = bh.Data 45 | sh.Len = bh.Len 46 | runtime.KeepAlive(b) 47 | return 48 | } 49 | 50 | // IfcValuePtr extracts the underlying values pointer from an empty interface{} 51 | // value. 52 | // Note: this might beome more unsafe in future go-versions, 53 | // if primitive values < pointer size will be stored by value in the 54 | // `interface{}` type. 55 | func IfcValuePtr(v interface{}) unsafe.Pointer { 56 | ifc := (*emptyInterface)(unsafe.Pointer(&v)) 57 | return ifc.word 58 | } 59 | 60 | // ReflValuePtr extracts the pointer value from a reflect.Value instance. 61 | // With reflect.Value basically being similar to `interface{}` augmented with additional 62 | // flags to execute checks, we map the value into an empty interface value (no methods) 63 | // and extract the actual values pointer. 64 | // Note: this might beome more unsafe in future go-versions, 65 | // if primitive values < pointer size will be stored by value in the 66 | // `interface{}` type. 67 | func ReflValuePtr(v reflect.Value) unsafe.Pointer { 68 | ifc := (*emptyInterface)(unsafe.Pointer(&v)) 69 | return ifc.word 70 | } 71 | 72 | // Returns a newly (allocated on heap) function pointer. The unsafe.Pointer returned 73 | // can be used to cast a function type into a function with other(compatible) 74 | // type (e.g. passing pointers only). 75 | func UnsafeFnPtr(fn interface{}) unsafe.Pointer { 76 | var v reflect.Value 77 | if tmp, ok := fn.(reflect.Value); ok { 78 | v = tmp 79 | } else { 80 | v = reflect.ValueOf(fn) 81 | } 82 | 83 | tmp := reflect.New(v.Type()) 84 | tmp.Elem().Set(v) 85 | return unsafe.Pointer(tmp.Pointer()) 86 | } 87 | -------------------------------------------------------------------------------- /json/decode.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package json 19 | 20 | import ( 21 | "io" 22 | 23 | structform "github.com/elastic/go-structform" 24 | ) 25 | 26 | type Decoder struct { 27 | in io.Reader 28 | buffer []byte 29 | p Parser 30 | } 31 | 32 | func NewDecoder(in io.Reader, buffer int, vs structform.Visitor) *Decoder { 33 | dec := &Decoder{ 34 | buffer: make([]byte, 0, buffer), 35 | in: in, 36 | } 37 | dec.p.init(vs) 38 | return dec 39 | } 40 | 41 | func NewBytesDecoder(b []byte, vs structform.Visitor) *Decoder { 42 | dec := &Decoder{ 43 | buffer: b, 44 | in: nil, 45 | } 46 | dec.p.init(vs) 47 | return dec 48 | } 49 | 50 | func (dec *Decoder) Next() error { 51 | var ( 52 | n int 53 | err error 54 | reported bool 55 | ) 56 | 57 | for !reported { 58 | if len(dec.buffer) == 0 { 59 | if dec.in == nil { 60 | if err := dec.p.finalize(); err != nil { 61 | return err 62 | } 63 | return io.EOF 64 | } 65 | 66 | n, err := dec.in.Read(dec.buffer) 67 | dec.buffer = dec.buffer[:n] 68 | if n == 0 && err != nil { 69 | return err 70 | } 71 | } 72 | 73 | n, reported, err = dec.p.feedUntil(dec.buffer) 74 | if err != nil { 75 | return err 76 | } 77 | 78 | dec.buffer = dec.buffer[n:] 79 | if reported { 80 | return nil 81 | } 82 | } 83 | 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /json/defs.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package json 19 | 20 | import "github.com/elastic/go-structform/internal/unsafe" 21 | 22 | var ( 23 | nullSymbol = []byte("null") 24 | trueSymbol = []byte("true") 25 | falseSymbol = []byte("false") 26 | commaSymbol = []byte(",") 27 | 28 | invalidCharSym = []byte(`\ufffd`) 29 | ) 30 | 31 | func str2Bytes(s string) []byte { 32 | return unsafe.Str2Bytes(s) 33 | } 34 | 35 | func bytes2Str(b []byte) string { 36 | return unsafe.Bytes2Str(b) 37 | } 38 | -------------------------------------------------------------------------------- /json/json.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package json 19 | -------------------------------------------------------------------------------- /json/json_test.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package json 19 | 20 | import ( 21 | "bytes" 22 | "io" 23 | "math" 24 | "strings" 25 | "testing" 26 | 27 | "github.com/stretchr/testify/assert" 28 | "github.com/stretchr/testify/require" 29 | 30 | structform "github.com/elastic/go-structform" 31 | "github.com/elastic/go-structform/sftest" 32 | ) 33 | 34 | func TestEncParseConsistent(t *testing.T) { 35 | testEncParseConsistent(t, Parse) 36 | } 37 | 38 | func TestEncDecoderConsistent(t *testing.T) { 39 | testEncParseConsistent(t, func(content []byte, to structform.Visitor) error { 40 | dec := NewBytesDecoder(content, to) 41 | err := dec.Next() 42 | if err == io.EOF { 43 | err = nil 44 | } 45 | return err 46 | }) 47 | } 48 | 49 | func TestEncParseBytesConsistent(t *testing.T) { 50 | testEncParseConsistent(t, func(content []byte, to structform.Visitor) error { 51 | p := NewParser(to) 52 | for _, b := range content { 53 | err := p.feed([]byte{b}) 54 | if err != nil { 55 | return err 56 | } 57 | } 58 | return p.finalize() 59 | }) 60 | } 61 | 62 | func testEncParseConsistent( 63 | t *testing.T, 64 | parse func([]byte, structform.Visitor) error, 65 | ) { 66 | sftest.TestEncodeParseConsistent(t, sftest.Samples, 67 | func() (structform.Visitor, func(structform.Visitor) error) { 68 | buf := bytes.NewBuffer(nil) 69 | vs := NewVisitor(buf) 70 | 71 | return vs, func(to structform.Visitor) error { 72 | return parse(buf.Bytes(), to) 73 | } 74 | }) 75 | } 76 | 77 | func TestStringEncoding(t *testing.T) { 78 | type testCase struct { 79 | in string 80 | htmlEscape bool 81 | expected string 82 | } 83 | 84 | cases := map[string]testCase{ 85 | "no escaping required": testCase{ 86 | in: "hello world", 87 | expected: `"hello world"`, 88 | }, 89 | "json required escaping": testCase{ 90 | in: `"hello \nworld"`, 91 | htmlEscape: false, 92 | expected: `"\"hello \\nworld\""`, 93 | }, 94 | "html escape with json required escaping": testCase{ 95 | in: `"hello \nworld"`, 96 | htmlEscape: true, 97 | expected: `"\"hello \\nworld\""`, 98 | }, 99 | "html with html escaping enabled": testCase{ 100 | in: "world", 101 | htmlEscape: true, 102 | expected: `"\u003chello\u003eworld\u003c/hello\u003e"`, 103 | }, 104 | "html without html escaping": testCase{ 105 | in: "world", 106 | htmlEscape: false, 107 | expected: `"world"`, 108 | }, 109 | "html with double quotes": testCase{ 110 | in: ``, 111 | htmlEscape: true, 112 | expected: `"\u003chello id=\"hola\"\u003e"`, 113 | }, 114 | "html with single quotes": testCase{ 115 | in: ``, 116 | htmlEscape: true, 117 | expected: `"\u003chello id='hola'\u003e"`, 118 | }, 119 | } 120 | 121 | for name, test := range cases { 122 | in, htmlEscape, expected := test.in, test.htmlEscape, test.expected 123 | 124 | t.Run(name, func(t *testing.T) { 125 | buf := &bytes.Buffer{} 126 | visitor := NewVisitor(buf) 127 | visitor.SetEscapeHTML(htmlEscape) 128 | visitor.OnString(in) 129 | 130 | actual := buf.String() 131 | assert.Equal(t, expected, actual) 132 | }) 133 | } 134 | } 135 | 136 | func TestEncodeIgnoreSpecialFloatValues(t *testing.T) { 137 | cases := map[string]struct { 138 | tokens sftest.Recording 139 | want string 140 | }{ 141 | "float64 NaN": { 142 | tokens: sftest.Recording{ 143 | sftest.Float64Rec{math.NaN()}, 144 | }, 145 | want: `null`, 146 | }, 147 | "float64 +Inf": { 148 | tokens: sftest.Recording{ 149 | sftest.Float64Rec{math.Inf(1)}, 150 | }, 151 | want: `null`, 152 | }, 153 | "float64 -Inf": { 154 | tokens: sftest.Recording{ 155 | sftest.Float64Rec{math.Inf(-1)}, 156 | }, 157 | want: `null`, 158 | }, 159 | } 160 | 161 | for name, test := range cases { 162 | t.Run(name, func(t *testing.T) { 163 | var buf strings.Builder 164 | visitor := NewVisitor(&buf) 165 | visitor.SetIgnoreInvalidFloat(true) 166 | 167 | err := test.tokens.Replay(visitor) 168 | require.NoError(t, err) 169 | 170 | require.Equal(t, test.want, buf.String()) 171 | }) 172 | } 173 | } 174 | 175 | func TestEncodeExplicitRadixPoint(t *testing.T) { 176 | cases := map[string]struct { 177 | tokens sftest.Recording 178 | want string 179 | }{ 180 | "1e+10": { 181 | tokens: sftest.Recording{ 182 | sftest.Float64Rec{1e+10}, 183 | }, 184 | want: `1.0e+10`, 185 | }, 186 | "1.1e+10": { 187 | tokens: sftest.Recording{ 188 | sftest.Float64Rec{1.1e+10}, 189 | }, 190 | want: `1.1e+10`, 191 | }, 192 | "1": { 193 | tokens: sftest.Recording{ 194 | sftest.Float64Rec{1}, 195 | }, 196 | want: `1.0`, 197 | }, 198 | "1.1": { 199 | tokens: sftest.Recording{ 200 | sftest.Float64Rec{1.1}, 201 | }, 202 | want: `1.1`, 203 | }, 204 | } 205 | 206 | for name, test := range cases { 207 | t.Run(name, func(t *testing.T) { 208 | var buf strings.Builder 209 | visitor := NewVisitor(&buf) 210 | visitor.SetExplicitRadixPoint(true) 211 | 212 | err := test.tokens.Replay(visitor) 213 | require.NoError(t, err) 214 | 215 | require.Equal(t, test.want, buf.String()) 216 | }) 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /json/state_string.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | // Code generated by "stringer -type=state"; DO NOT EDIT. 19 | 20 | package json 21 | 22 | import "strconv" 23 | 24 | const _state_name = "failedStatestartStatearrStatearrStateValuearrStateNextdictStatedictFieldStatedictNextFieldStatedictFieldValuedictFieldValueSepdictFieldStateEndnullStatetrueStatefalseStatestringStatenumberState" 25 | 26 | var _state_index = [...]uint8{0, 11, 21, 29, 42, 54, 63, 77, 95, 109, 126, 143, 152, 161, 171, 182, 193} 27 | 28 | func (i state) String() string { 29 | if i >= state(len(_state_index)-1) { 30 | return "state(" + strconv.FormatInt(int64(i), 10) + ")" 31 | } 32 | return _state_name[_state_index[i]:_state_index[i+1]] 33 | } 34 | -------------------------------------------------------------------------------- /map.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package structform 19 | 20 | type extObjVisitor struct { 21 | Visitor 22 | } 23 | 24 | func (e extObjVisitor) OnStringObject(m map[string]string) error { 25 | if err := e.OnObjectStart(len(m), StringType); err != nil { 26 | return err 27 | } 28 | 29 | for k, v := range m { 30 | if err := e.OnKey(k); err != nil { 31 | return err 32 | } 33 | if err := e.OnString(v); err != nil { 34 | return err 35 | } 36 | } 37 | 38 | return e.OnObjectFinished() 39 | } 40 | 41 | func (e extObjVisitor) OnBoolObject(m map[string]bool) error { 42 | if err := e.OnObjectStart(len(m), BoolType); err != nil { 43 | return err 44 | } 45 | 46 | for k, v := range m { 47 | if err := e.OnKey(k); err != nil { 48 | return err 49 | } 50 | if err := e.OnBool(v); err != nil { 51 | return err 52 | } 53 | } 54 | 55 | return e.OnObjectFinished() 56 | } 57 | 58 | func (e extObjVisitor) OnInt8Object(m map[string]int8) error { 59 | if err := e.OnObjectStart(len(m), Int8Type); err != nil { 60 | return err 61 | } 62 | 63 | for k, v := range m { 64 | if err := e.OnKey(k); err != nil { 65 | return err 66 | } 67 | if err := e.OnInt8(v); err != nil { 68 | return err 69 | } 70 | } 71 | 72 | return e.OnObjectFinished() 73 | } 74 | 75 | func (e extObjVisitor) OnInt16Object(m map[string]int16) error { 76 | if err := e.OnObjectStart(len(m), Int16Type); err != nil { 77 | return err 78 | } 79 | 80 | for k, v := range m { 81 | if err := e.OnKey(k); err != nil { 82 | return err 83 | } 84 | if err := e.OnInt16(v); err != nil { 85 | return err 86 | } 87 | } 88 | 89 | return e.OnObjectFinished() 90 | } 91 | 92 | func (e extObjVisitor) OnInt32Object(m map[string]int32) error { 93 | if err := e.OnObjectStart(len(m), Int32Type); err != nil { 94 | return err 95 | } 96 | 97 | for k, v := range m { 98 | if err := e.OnKey(k); err != nil { 99 | return err 100 | } 101 | if err := e.OnInt32(v); err != nil { 102 | return err 103 | } 104 | } 105 | 106 | return e.OnObjectFinished() 107 | } 108 | 109 | func (e extObjVisitor) OnInt64Object(m map[string]int64) error { 110 | if err := e.OnObjectStart(len(m), Int64Type); err != nil { 111 | return err 112 | } 113 | 114 | for k, v := range m { 115 | if err := e.OnKey(k); err != nil { 116 | return err 117 | } 118 | if err := e.OnInt64(v); err != nil { 119 | return err 120 | } 121 | } 122 | 123 | return e.OnObjectFinished() 124 | } 125 | 126 | func (e extObjVisitor) OnIntObject(m map[string]int) error { 127 | if err := e.OnObjectStart(len(m), IntType); err != nil { 128 | return err 129 | } 130 | 131 | for k, v := range m { 132 | if err := e.OnKey(k); err != nil { 133 | return err 134 | } 135 | if err := e.OnInt(v); err != nil { 136 | return err 137 | } 138 | } 139 | 140 | return e.OnObjectFinished() 141 | } 142 | 143 | func (e extObjVisitor) OnUint8Object(m map[string]uint8) error { 144 | if err := e.OnObjectStart(len(m), Uint8Type); err != nil { 145 | return err 146 | } 147 | 148 | for k, v := range m { 149 | if err := e.OnKey(k); err != nil { 150 | return err 151 | } 152 | if err := e.OnUint8(v); err != nil { 153 | return err 154 | } 155 | } 156 | 157 | return e.OnObjectFinished() 158 | } 159 | 160 | func (e extObjVisitor) OnUint16Object(m map[string]uint16) error { 161 | if err := e.OnObjectStart(len(m), Uint16Type); err != nil { 162 | return err 163 | } 164 | 165 | for k, v := range m { 166 | if err := e.OnKey(k); err != nil { 167 | return err 168 | } 169 | if err := e.OnUint16(v); err != nil { 170 | return err 171 | } 172 | } 173 | 174 | return e.OnObjectFinished() 175 | } 176 | 177 | func (e extObjVisitor) OnUint32Object(m map[string]uint32) error { 178 | if err := e.OnObjectStart(len(m), Uint32Type); err != nil { 179 | return err 180 | } 181 | 182 | for k, v := range m { 183 | if err := e.OnKey(k); err != nil { 184 | return err 185 | } 186 | if err := e.OnUint32(v); err != nil { 187 | return err 188 | } 189 | } 190 | 191 | return e.OnObjectFinished() 192 | } 193 | 194 | func (e extObjVisitor) OnUint64Object(m map[string]uint64) error { 195 | if err := e.OnObjectStart(len(m), Uint64Type); err != nil { 196 | return err 197 | } 198 | 199 | for k, v := range m { 200 | if err := e.OnKey(k); err != nil { 201 | return err 202 | } 203 | if err := e.OnUint64(v); err != nil { 204 | return err 205 | } 206 | } 207 | 208 | return e.OnObjectFinished() 209 | } 210 | 211 | func (e extObjVisitor) OnUintObject(m map[string]uint) error { 212 | if err := e.OnObjectStart(len(m), UintType); err != nil { 213 | return err 214 | } 215 | 216 | for k, v := range m { 217 | if err := e.OnKey(k); err != nil { 218 | return err 219 | } 220 | if err := e.OnUint(v); err != nil { 221 | return err 222 | } 223 | } 224 | 225 | return e.OnObjectFinished() 226 | } 227 | 228 | func (e extObjVisitor) OnFloat32Object(m map[string]float32) error { 229 | if err := e.OnObjectStart(len(m), Float32Type); err != nil { 230 | return err 231 | } 232 | 233 | for k, v := range m { 234 | if err := e.OnKey(k); err != nil { 235 | return err 236 | } 237 | if err := e.OnFloat32(v); err != nil { 238 | return err 239 | } 240 | } 241 | 242 | return e.OnObjectFinished() 243 | } 244 | 245 | func (e extObjVisitor) OnFloat64Object(m map[string]float64) error { 246 | if err := e.OnObjectStart(len(m), Float64Type); err != nil { 247 | return err 248 | } 249 | 250 | for k, v := range m { 251 | if err := e.OnKey(k); err != nil { 252 | return err 253 | } 254 | if err := e.OnFloat64(v); err != nil { 255 | return err 256 | } 257 | } 258 | 259 | return e.OnObjectFinished() 260 | } 261 | -------------------------------------------------------------------------------- /sftest/cases.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package sftest 19 | 20 | import structform "github.com/elastic/go-structform" 21 | 22 | var Samples = concatSamples( 23 | SamplesPrimitives, 24 | SamplesArr, 25 | SamplesObj, 26 | SamplesCombinations, 27 | ) 28 | 29 | var SamplesPrimitives = []Recording{ 30 | // simple primitives 31 | {NilRec{}}, // "null" 32 | {BoolRec{true}}, // "true" 33 | {BoolRec{false}}, // "false" 34 | {StringRec{"test"}}, 35 | {StringRec{`test with " being special`}}, 36 | {StringRec{""}}, 37 | 38 | // int types 39 | {IntRec{8}}, 40 | {IntRec{42}}, 41 | {IntRec{100}}, 42 | {IntRec{-90}}, 43 | {IntRec{12345678}}, 44 | {IntRec{-12345678}}, 45 | {Int8Rec{8}}, 46 | {Int8Rec{42}}, 47 | {Int8Rec{100}}, 48 | {Int8Rec{-90}}, 49 | {Int16Rec{8}}, 50 | {Int16Rec{42}}, 51 | {Int16Rec{100}}, 52 | {Int16Rec{-90}}, 53 | {Int16Rec{500}}, 54 | {Int16Rec{-500}}, 55 | {Int32Rec{8}}, 56 | {Int32Rec{42}}, 57 | {Int32Rec{100}}, 58 | {Int32Rec{-90}}, 59 | {Int32Rec{500}}, 60 | {Int32Rec{-500}}, 61 | {Int32Rec{12345678}}, 62 | {Int32Rec{-12345678}}, 63 | {Int64Rec{8}}, 64 | {Int64Rec{42}}, 65 | {Int64Rec{100}}, 66 | {Int64Rec{-90}}, 67 | {Int64Rec{500}}, 68 | {Int64Rec{-500}}, 69 | {Int64Rec{123456781234}}, 70 | {Int64Rec{-123456781234}}, 71 | 72 | // uint types 73 | {UintRec{8}}, 74 | {UintRec{42}}, 75 | {UintRec{100}}, 76 | {UintRec{12345678}}, 77 | {Uint8Rec{8}}, 78 | {Uint8Rec{42}}, 79 | {Uint8Rec{100}}, 80 | {ByteRec{8}}, 81 | {ByteRec{42}}, 82 | {ByteRec{100}}, 83 | {Uint16Rec{8}}, 84 | {Uint16Rec{42}}, 85 | {Uint16Rec{100}}, 86 | {Uint16Rec{500}}, 87 | {Uint32Rec{8}}, 88 | {Uint32Rec{42}}, 89 | {Uint32Rec{100}}, 90 | {Uint32Rec{500}}, 91 | {Uint32Rec{12345678}}, 92 | {Uint64Rec{8}}, 93 | {Uint64Rec{42}}, 94 | {Uint64Rec{100}}, 95 | {Uint64Rec{500}}, 96 | {Uint64Rec{123456781234}}, 97 | 98 | // float types 99 | {Float32Rec{3.14}}, 100 | {Float32Rec{-3.14}}, 101 | {Float64Rec{3.14}}, 102 | {Float64Rec{-3.14}}, 103 | } 104 | 105 | var SamplesArr = []Recording{ 106 | // empty arrays `[]` 107 | Arr(0, structform.AnyType), 108 | Arr(-1, structform.AnyType), 109 | 110 | // nested empty array `[[]]` 111 | Arr(1, structform.AnyType, 112 | Arr(0, structform.AnyType), 113 | ), 114 | Arr(-1, structform.AnyType, 115 | Arr(0, structform.AnyType), 116 | ), 117 | Arr(-1, structform.AnyType, 118 | Arr(-1, structform.AnyType), 119 | ), 120 | 121 | // array with arbitrary values 122 | Arr(-1, structform.AnyType, 123 | NilRec{}, 124 | BoolRec{true}, 125 | BoolRec{false}, 126 | IntRec{1}, 127 | Int64Rec{12345678910}, 128 | Float32Rec{3.14}, 129 | Float64Rec{7e+09}, 130 | StringRec{"test"}, 131 | ), 132 | Arr(2, structform.AnyType, 133 | Int8Rec{1}, 134 | BoolRec{true}, 135 | ), 136 | { 137 | Int8ArrRec{[]int8{1, 2, 3}}, 138 | }, 139 | { 140 | StringArrRec{[]string{"a", "b", "c"}}, 141 | }, 142 | } 143 | 144 | var SamplesObj = []Recording{ 145 | // empty object '{}' 146 | Obj(-1, structform.AnyType), 147 | Obj(0, structform.AnyType), 148 | 149 | Obj(-1, structform.AnyType, 150 | "a", NilRec{}, 151 | ), 152 | 153 | // objects 154 | Obj(-1, structform.AnyType, 155 | "a", StringRec{"test"}), 156 | Obj(1, structform.StringType, 157 | "a", StringRec{"test"}), 158 | Obj(-1, structform.AnyType, 159 | "a", BoolRec{true}, 160 | "b", BoolRec{false}, 161 | ), 162 | Obj(2, structform.AnyType, 163 | "a", BoolRec{true}, 164 | "b", BoolRec{false}, 165 | ), 166 | Obj(-1, structform.BoolType, 167 | "a", BoolRec{true}, 168 | "b", BoolRec{false}, 169 | ), 170 | Obj(2, structform.BoolType, 171 | "a", BoolRec{true}, 172 | "b", BoolRec{false}, 173 | ), 174 | Obj(-1, structform.AnyType, 175 | "a", UintRec{1}, 176 | "b", Float64Rec{3.14}, 177 | "c", StringRec{"test"}, 178 | "d", BoolRec{true}, 179 | ), 180 | 181 | // typed objects 182 | { 183 | StringObjRec{map[string]string{ 184 | "a": "test1", 185 | "b": "test2", 186 | "c": "test3", 187 | }}, 188 | }, 189 | { 190 | UintObjRec{map[string]uint{ 191 | "a": 1, 192 | "b": 2, 193 | "c": 3, 194 | }}, 195 | }, 196 | } 197 | 198 | var SamplesCombinations = []Recording{ 199 | // objects in array 200 | Arr(-1, structform.AnyType, 201 | Obj(-1, structform.AnyType)), 202 | Arr(1, structform.AnyType, 203 | Obj(0, structform.AnyType)), 204 | Arr(-1, structform.AnyType, 205 | Obj(-1, structform.AnyType, 206 | "a", IntRec{-1}, 207 | ), 208 | Obj(1, structform.UintType, 209 | "a", UintRec{1}, 210 | ), 211 | ), 212 | Arr(2, structform.AnyType, 213 | Obj(-1, structform.AnyType, 214 | "a", IntRec{-1}, 215 | ), 216 | Obj(1, structform.UintType, 217 | "a", UintRec{1}, 218 | ), 219 | ), 220 | 221 | // array in object 222 | Obj(-1, structform.AnyType, 223 | "a", Arr(3, structform.IntType, 224 | IntRec{1}, IntRec{2}, IntRec{3}), 225 | ), 226 | Obj(1, structform.AnyType, 227 | "a", Arr(3, structform.IntType, 228 | IntRec{1}, IntRec{2}, IntRec{3}), 229 | ), 230 | Obj(1, structform.AnyType, 231 | "a", Int8ArrRec{[]int8{1, 2, 3}}, 232 | ), 233 | } 234 | -------------------------------------------------------------------------------- /sftest/sftest_test.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package sftest 19 | 20 | import ( 21 | "testing" 22 | 23 | structform "github.com/elastic/go-structform" 24 | ) 25 | 26 | func TestRecordingConsistent(t *testing.T) { 27 | TestEncodeParseConsistent(t, Samples, 28 | func() (structform.Visitor, func(structform.Visitor) error) { 29 | buf := &Recording{} 30 | return buf, buf.Replay 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /sftest/util.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package sftest 19 | 20 | import structform "github.com/elastic/go-structform" 21 | 22 | func Arr(l int, t structform.BaseType, elems ...interface{}) []Record { 23 | a := []Record{ArrayStartRec{l, t}} 24 | for _, elem := range elems { 25 | switch v := elem.(type) { 26 | case Record: 27 | a = append(a, v) 28 | case []Record: 29 | a = append(a, v...) 30 | case Recording: 31 | a = append(a, v...) 32 | default: 33 | panic("invalid key type") 34 | } 35 | } 36 | 37 | return append(a, ArrayFinishRec{}) 38 | } 39 | 40 | func Obj(l int, t structform.BaseType, kv ...interface{}) []Record { 41 | if len(kv)%2 != 0 { 42 | panic("invalid object") 43 | } 44 | 45 | a := []Record{ObjectStartRec{l, t}} 46 | for i := 0; i < len(kv); i += 2 { 47 | k := kv[i].(string) 48 | a = append(a, ObjectKeyRec{k}) 49 | 50 | switch v := kv[i+1].(type) { 51 | case Record: 52 | a = append(a, v) 53 | case []Record: 54 | a = append(a, v...) 55 | case Recording: 56 | a = append(a, v...) 57 | default: 58 | panic("invalid key type") 59 | } 60 | } 61 | 62 | return append(a, ObjectFinishRec{}) 63 | } 64 | -------------------------------------------------------------------------------- /string.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package structform 19 | 20 | type extStrVisitor struct { 21 | v Visitor 22 | } 23 | 24 | func MakeStringRefVisitor(v Visitor) StringRefVisitor { 25 | if sv, ok := v.(StringRefVisitor); ok { 26 | return sv 27 | } 28 | return extStrVisitor{v} 29 | } 30 | 31 | func (ev extStrVisitor) OnStringRef(s []byte) error { 32 | return ev.v.OnString(string(s)) 33 | } 34 | 35 | func (ev extStrVisitor) OnKeyRef(s []byte) error { 36 | return ev.v.OnKey(string(s)) 37 | } 38 | -------------------------------------------------------------------------------- /ubjson/decode.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package ubjson 19 | 20 | import ( 21 | "io" 22 | 23 | structform "github.com/elastic/go-structform" 24 | ) 25 | 26 | type Decoder struct { 27 | in io.Reader 28 | buffer []byte 29 | buffer0 []byte 30 | p Parser 31 | } 32 | 33 | func NewDecoder(in io.Reader, buffer int, vs structform.Visitor) *Decoder { 34 | dec := &Decoder{ 35 | buffer0: make([]byte, buffer), 36 | in: in, 37 | } 38 | dec.p.init(vs) 39 | return dec 40 | } 41 | 42 | func NewBytesDecoder(b []byte, vs structform.Visitor) *Decoder { 43 | dec := &Decoder{ 44 | buffer: b, 45 | buffer0: b[:0], 46 | in: nil, 47 | } 48 | dec.p.init(vs) 49 | return dec 50 | } 51 | 52 | func (dec *Decoder) Next() error { 53 | var ( 54 | n int 55 | err error 56 | reported bool 57 | ) 58 | 59 | for !reported { 60 | if len(dec.buffer) == 0 { 61 | if dec.in == nil { 62 | if err := dec.p.finalize(); err != nil { 63 | return err 64 | } 65 | return io.EOF 66 | } 67 | 68 | n, err := dec.in.Read(dec.buffer0) 69 | dec.buffer = dec.buffer0[:n] 70 | if n == 0 && err != nil { 71 | return err 72 | } 73 | } 74 | 75 | n, reported, err = dec.p.feedUntil(dec.buffer) 76 | if err != nil { 77 | return err 78 | } 79 | 80 | dec.buffer = dec.buffer[n:] 81 | if reported { 82 | return nil 83 | } 84 | } 85 | 86 | return nil 87 | } 88 | -------------------------------------------------------------------------------- /ubjson/defs.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package ubjson 19 | 20 | import "github.com/elastic/go-structform/internal/unsafe" 21 | 22 | const ( 23 | noMarker byte = 0 24 | 25 | // value markers 26 | nullMarker byte = 'Z' 27 | noopMarker byte = 'N' 28 | trueMarker byte = 'T' 29 | falseMarker byte = 'F' 30 | int8Marker byte = 'i' 31 | uint8Marker byte = 'U' 32 | int16Marker byte = 'I' 33 | int32Marker byte = 'l' 34 | int64Marker byte = 'L' 35 | float32Marker byte = 'd' 36 | float64Marker byte = 'D' 37 | highPrecMarker byte = 'H' 38 | charMarker byte = 'C' 39 | stringMarker byte = 'S' 40 | 41 | objStartMarker byte = '{' 42 | objEndMarker byte = '}' 43 | arrStartMarker byte = '[' 44 | arrEndMarker byte = ']' 45 | 46 | countMarker byte = '#' 47 | typeMarker byte = '$' 48 | ) 49 | 50 | func str2Bytes(s string) []byte { 51 | return unsafe.Str2Bytes(s) 52 | } 53 | 54 | func bytes2Str(b []byte) string { 55 | return unsafe.Bytes2Str(b) 56 | } 57 | -------------------------------------------------------------------------------- /ubjson/stack.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package ubjson 19 | 20 | type stateStack struct { 21 | stack []state // state stack for nested arrays/objects 22 | stack0 [32]state 23 | current state 24 | } 25 | 26 | type lengthStack struct { 27 | stack []int64 28 | stack0 [32]int64 29 | current int64 30 | } 31 | 32 | func (s *stateStack) push(next state) { 33 | if s.current.stateType != stFail { 34 | s.stack = append(s.stack, s.current) 35 | } 36 | s.current = next 37 | } 38 | 39 | func (s *stateStack) pop() { 40 | if len(s.stack) == 0 { 41 | s.current = state{stFail, stStart} 42 | } else { 43 | last := len(s.stack) - 1 44 | s.current = s.stack[last] 45 | s.stack = s.stack[:last] 46 | } 47 | } 48 | 49 | func (s *lengthStack) push(l int64) { 50 | s.stack = append(s.stack, s.current) 51 | s.current = l 52 | } 53 | 54 | func (s *lengthStack) pop() int64 { 55 | if len(s.stack) == 0 { 56 | s.current = -1 57 | return -1 58 | } else { 59 | last := len(s.stack) - 1 60 | old := s.current 61 | s.current = s.stack[last] 62 | s.stack = s.stack[:last] 63 | return old 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ubjson/statestep_string.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | // Code generated by "stringer -type=stateStep"; DO NOT EDIT. 19 | 20 | package ubjson 21 | 22 | import "strconv" 23 | 24 | const _stateStep_name = "stStartstNilstNoopstTruestFalsestInt8stUInt8stInt16stInt32stInt64stFloat32stFloat64stCharstWithLenstWithType0stWithType1stContstFieldNamestFieldNameLen" 25 | 26 | var _stateStep_index = [...]uint8{0, 7, 12, 18, 24, 31, 37, 44, 51, 58, 65, 74, 83, 89, 98, 109, 120, 126, 137, 151} 27 | 28 | func (i stateStep) String() string { 29 | if i >= stateStep(len(_stateStep_index)-1) { 30 | return "stateStep(" + strconv.FormatInt(int64(i), 10) + ")" 31 | } 32 | return _stateStep_name[_stateStep_index[i]:_stateStep_index[i+1]] 33 | } 34 | -------------------------------------------------------------------------------- /ubjson/statetype_string.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | // Code generated by "stringer -type=stateType"; DO NOT EDIT. 19 | 20 | package ubjson 21 | 22 | import "strconv" 23 | 24 | const _stateType_name = "stFailstNextstFixedstHighPrecstStringstArraystArrayDynstArrayCountstArrayTypedstObjectstObjectDynstObjectCountstObjectTyped" 25 | 26 | var _stateType_index = [...]uint8{0, 6, 12, 19, 29, 37, 44, 54, 66, 78, 86, 97, 110, 123} 27 | 28 | func (i stateType) String() string { 29 | if i >= stateType(len(_stateType_index)-1) { 30 | return "stateType(" + strconv.FormatInt(int64(i), 10) + ")" 31 | } 32 | return _stateType_name[_stateType_index[i]:_stateType_index[i+1]] 33 | } 34 | -------------------------------------------------------------------------------- /ubjson/ubjson_test.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package ubjson 19 | 20 | import ( 21 | "bytes" 22 | "io" 23 | "testing" 24 | 25 | "github.com/elastic/go-structform" 26 | "github.com/elastic/go-structform/sftest" 27 | ) 28 | 29 | func TestEncParseConsistent(t *testing.T) { 30 | testEncParseConsistent(t, Parse) 31 | } 32 | 33 | func TestEncDecoderConsistent(t *testing.T) { 34 | testEncParseConsistent(t, func(content []byte, to structform.Visitor) error { 35 | dec := NewBytesDecoder(content, to) 36 | err := dec.Next() 37 | if err == io.EOF { 38 | err = nil 39 | } 40 | return err 41 | }) 42 | } 43 | 44 | func TestEncParseBytesConsistent(t *testing.T) { 45 | testEncParseConsistent(t, func(content []byte, to structform.Visitor) error { 46 | p := NewParser(to) 47 | for _, b := range content { 48 | err := p.feed([]byte{b}) 49 | if err != nil { 50 | return err 51 | } 52 | } 53 | return p.finalize() 54 | }) 55 | } 56 | 57 | func testEncParseConsistent( 58 | t *testing.T, 59 | parse func([]byte, structform.Visitor) error, 60 | ) { 61 | sftest.TestEncodeParseConsistent(t, sftest.Samples, 62 | func() (structform.Visitor, func(structform.Visitor) error) { 63 | buf := bytes.NewBuffer(nil) 64 | vs := NewVisitor(buf) 65 | 66 | return vs, func(to structform.Visitor) error { 67 | return parse(buf.Bytes(), to) 68 | } 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /visitor.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package structform 19 | 20 | // Visitor interface for accepting events. The Vistor defined the common Data 21 | // Model all serializers should accept and all deserializers must implement. 22 | type Visitor interface { 23 | ObjectVisitor 24 | ArrayVisitor 25 | ValueVisitor 26 | } 27 | 28 | // ExtVisitor interface defines the Extended Data Model. Usage and 29 | // implementation of the Extended Data Model is optional, but can speed up 30 | // operations. 31 | type ExtVisitor interface { 32 | Visitor 33 | ArrayValueVisitor 34 | ObjectValueVisitor 35 | StringRefVisitor 36 | } 37 | 38 | //go:generate stringer -type=BaseType 39 | type BaseType uint8 40 | 41 | const ( 42 | AnyType BaseType = iota 43 | ByteType 44 | StringType 45 | BoolType 46 | ZeroType 47 | IntType 48 | Int8Type 49 | Int16Type 50 | Int32Type 51 | Int64Type 52 | UintType 53 | Uint8Type 54 | Uint16Type 55 | Uint32Type 56 | Uint64Type 57 | Float32Type 58 | Float64Type 59 | ) 60 | 61 | // ObjectVisitor iterates all fields in a dictionary like structure. 62 | type ObjectVisitor interface { 63 | // OnObjectStart is called when a new object (key-value pairs) is going to be reported. 64 | // A call to OnKey or OnObjectFinished must follow directly. 65 | OnObjectStart(len int, baseType BaseType) error 66 | 67 | // OnArrayFinished indicates that there are no more key value pairs to report. 68 | OnObjectFinished() error 69 | 70 | // OnKey adds a new key to the object. A value must directly follow a call to OnKey. 71 | OnKey(s string) error 72 | } 73 | 74 | // ArrayVisitor defines the support for arrays/slices in the Data Model. 75 | type ArrayVisitor interface { 76 | // OnArrayStart is called whan a new array is going to be reported. 77 | // 78 | // The `len` argument should report the length if known. `len` MUST BE -1 if 79 | // the length of the array is not known. The BaseType should indicate the 80 | // element type of the array. If the element type is unknown or can be any 81 | // type (e.g. interface{}), AnyType must be used. 82 | OnArrayStart(len int, baseType BaseType) error 83 | 84 | // OnArrayFinished indicates that there are no more elements in the array. 85 | OnArrayFinished() error 86 | } 87 | 88 | // ValueVisitor defines the set of supported primitive types in the Data Model. 89 | type ValueVisitor interface { 90 | // untyped nil value 91 | OnNil() error 92 | 93 | OnBool(b bool) error 94 | 95 | OnString(s string) error 96 | 97 | // int 98 | OnInt8(i int8) error 99 | OnInt16(i int16) error 100 | OnInt32(i int32) error 101 | OnInt64(i int64) error 102 | OnInt(i int) error 103 | 104 | // uint 105 | OnByte(b byte) error 106 | OnUint8(u uint8) error 107 | OnUint16(u uint16) error 108 | OnUint32(u uint32) error 109 | OnUint64(u uint64) error 110 | OnUint(u uint) error 111 | 112 | // float 113 | OnFloat32(f float32) error 114 | OnFloat64(f float64) error 115 | } 116 | 117 | // ArrayValueVisitor passes arrays with known type. Implementation 118 | // of ArrayValueVisitor is optional. 119 | type ArrayValueVisitor interface { 120 | OnBoolArray([]bool) error 121 | 122 | OnStringArray([]string) error 123 | 124 | // int 125 | OnInt8Array([]int8) error 126 | OnInt16Array([]int16) error 127 | OnInt32Array([]int32) error 128 | OnInt64Array([]int64) error 129 | OnIntArray([]int) error 130 | 131 | // uint 132 | OnBytes([]byte) error 133 | OnUint8Array([]uint8) error 134 | OnUint16Array([]uint16) error 135 | OnUint32Array([]uint32) error 136 | OnUint64Array([]uint64) error 137 | OnUintArray([]uint) error 138 | 139 | // float 140 | OnFloat32Array([]float32) error 141 | OnFloat64Array([]float64) error 142 | } 143 | 144 | // ObjectValueVisitor passes map[string]T. Implementation 145 | // of ObjectValueVisitor is optional. 146 | type ObjectValueVisitor interface { 147 | OnBoolObject(map[string]bool) error 148 | 149 | OnStringObject(map[string]string) error 150 | 151 | // int 152 | OnInt8Object(map[string]int8) error 153 | OnInt16Object(map[string]int16) error 154 | OnInt32Object(map[string]int32) error 155 | OnInt64Object(map[string]int64) error 156 | OnIntObject(map[string]int) error 157 | 158 | // uint 159 | OnUint8Object(map[string]uint8) error 160 | OnUint16Object(map[string]uint16) error 161 | OnUint32Object(map[string]uint32) error 162 | OnUint64Object(map[string]uint64) error 163 | OnUintObject(map[string]uint) error 164 | 165 | // float 166 | OnFloat32Object(map[string]float32) error 167 | OnFloat64Object(map[string]float64) error 168 | } 169 | 170 | // StringRefVisitor handles strings by reference into a byte string. 171 | // The reference must be processed immediately, as the string passed 172 | // might get modified after the callback returns. 173 | type StringRefVisitor interface { 174 | OnStringRef(s []byte) error 175 | OnKeyRef(s []byte) error 176 | } 177 | 178 | type extVisitor struct { 179 | Visitor 180 | ObjectValueVisitor 181 | ArrayValueVisitor 182 | StringRefVisitor 183 | } 184 | 185 | // EnsureExtVisitor converts a Visitor into an ExtVisitor. If v already 186 | // implements ExtVisitor, it is directly implemented. If v only implements a 187 | // subset of ExtVisitor, then conversions for the missing interfaces will be 188 | // created. 189 | func EnsureExtVisitor(v Visitor) ExtVisitor { 190 | if ev, ok := v.(ExtVisitor); ok { 191 | return ev 192 | } 193 | 194 | e := &extVisitor{ 195 | Visitor: v, 196 | } 197 | if ov, ok := v.(ObjectValueVisitor); ok { 198 | e.ObjectValueVisitor = ov 199 | } else { 200 | e.ObjectValueVisitor = extObjVisitor{v} 201 | } 202 | if av, ok := v.(ArrayValueVisitor); ok { 203 | e.ArrayValueVisitor = av 204 | } else { 205 | e.ArrayValueVisitor = extArrVisitor{v} 206 | } 207 | e.StringRefVisitor = MakeStringRefVisitor(v) 208 | 209 | return e 210 | } 211 | -------------------------------------------------------------------------------- /visitors/expect_obj.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package visitors 19 | 20 | import ( 21 | "errors" 22 | 23 | structform "github.com/elastic/go-structform" 24 | ) 25 | 26 | type ExpectObjVisitor struct { 27 | active structform.ExtVisitor 28 | depth int 29 | } 30 | 31 | func NewExpectObjVisitor(target structform.ExtVisitor) *ExpectObjVisitor { 32 | return &ExpectObjVisitor{active: target} 33 | } 34 | 35 | func (e *ExpectObjVisitor) SetActive(a structform.ExtVisitor) { 36 | e.active = a 37 | e.depth = 0 38 | } 39 | 40 | func (e *ExpectObjVisitor) Done() bool { 41 | return e.depth == 0 42 | } 43 | 44 | func (v *ExpectObjVisitor) OnObjectStart(len int, baseType structform.BaseType) error { 45 | v.depth++ 46 | if v.depth == 1 { 47 | return nil 48 | } 49 | return v.active.OnObjectStart(len, baseType) 50 | } 51 | 52 | func (v *ExpectObjVisitor) OnObjectFinished() error { 53 | v.depth-- 54 | if v.depth == 0 { 55 | return nil 56 | } 57 | return v.active.OnObjectFinished() 58 | } 59 | 60 | func (v *ExpectObjVisitor) OnKey(s string) error { 61 | if err := v.check(); err != nil { 62 | return err 63 | } 64 | return v.active.OnKey(s) 65 | } 66 | 67 | func (v *ExpectObjVisitor) OnArrayStart(len int, baseType structform.BaseType) error { 68 | if err := v.check(); err != nil { 69 | return err 70 | } 71 | return v.active.OnArrayStart(len, baseType) 72 | } 73 | 74 | func (v *ExpectObjVisitor) OnArrayFinished() error { 75 | if err := v.check(); err != nil { 76 | return err 77 | } 78 | return v.active.OnArrayFinished() 79 | } 80 | 81 | func (v *ExpectObjVisitor) OnNil() error { 82 | if err := v.check(); err != nil { 83 | return err 84 | } 85 | return v.active.OnNil() 86 | } 87 | 88 | func (v *ExpectObjVisitor) OnBool(b bool) error { 89 | if err := v.check(); err != nil { 90 | return err 91 | } 92 | return v.active.OnBool(b) 93 | } 94 | 95 | func (v *ExpectObjVisitor) OnString(s string) error { 96 | if err := v.check(); err != nil { 97 | return err 98 | } 99 | return v.active.OnString(s) 100 | } 101 | 102 | func (v *ExpectObjVisitor) OnInt8(i int8) error { 103 | if err := v.check(); err != nil { 104 | return err 105 | } 106 | return v.active.OnInt8(i) 107 | } 108 | 109 | func (v *ExpectObjVisitor) OnInt16(i int16) error { 110 | if err := v.check(); err != nil { 111 | return err 112 | } 113 | return v.active.OnInt16(i) 114 | } 115 | 116 | func (v *ExpectObjVisitor) OnInt32(i int32) error { 117 | if err := v.check(); err != nil { 118 | return err 119 | } 120 | return v.active.OnInt32(i) 121 | } 122 | 123 | func (v *ExpectObjVisitor) OnInt64(i int64) error { 124 | if err := v.check(); err != nil { 125 | return err 126 | } 127 | return v.active.OnInt64(i) 128 | } 129 | 130 | func (v *ExpectObjVisitor) OnInt(i int) error { 131 | if err := v.check(); err != nil { 132 | return err 133 | } 134 | return v.active.OnInt(i) 135 | } 136 | 137 | func (v *ExpectObjVisitor) OnByte(b byte) error { 138 | if err := v.check(); err != nil { 139 | return err 140 | } 141 | return v.active.OnByte(b) 142 | } 143 | 144 | func (v *ExpectObjVisitor) OnUint8(u uint8) error { 145 | if err := v.check(); err != nil { 146 | return err 147 | } 148 | return v.active.OnUint8(u) 149 | } 150 | 151 | func (v *ExpectObjVisitor) OnUint16(u uint16) error { 152 | if err := v.check(); err != nil { 153 | return err 154 | } 155 | return v.active.OnUint16(u) 156 | } 157 | 158 | func (v *ExpectObjVisitor) OnUint32(u uint32) error { 159 | if err := v.check(); err != nil { 160 | return err 161 | } 162 | return v.active.OnUint32(u) 163 | } 164 | 165 | func (v *ExpectObjVisitor) OnUint64(u uint64) error { 166 | if err := v.check(); err != nil { 167 | return err 168 | } 169 | return v.active.OnUint64(u) 170 | } 171 | 172 | func (v *ExpectObjVisitor) OnUint(u uint) error { 173 | if err := v.check(); err != nil { 174 | return err 175 | } 176 | return v.active.OnUint(u) 177 | } 178 | 179 | func (v *ExpectObjVisitor) OnFloat32(f float32) error { 180 | if err := v.check(); err != nil { 181 | return err 182 | } 183 | return v.active.OnFloat32(f) 184 | } 185 | 186 | func (v *ExpectObjVisitor) OnFloat64(f float64) error { 187 | if err := v.check(); err != nil { 188 | return err 189 | } 190 | return v.active.OnFloat64(f) 191 | } 192 | 193 | func (v *ExpectObjVisitor) OnStringRef(s []byte) error { 194 | if err := v.check(); err != nil { 195 | return err 196 | } 197 | return v.active.OnStringRef(s) 198 | } 199 | 200 | func (v *ExpectObjVisitor) OnKeyRef(s []byte) error { 201 | if err := v.check(); err != nil { 202 | return err 203 | } 204 | return v.active.OnKeyRef(s) 205 | } 206 | 207 | func (v *ExpectObjVisitor) check() error { 208 | if v.depth == 0 { 209 | return errors.New("inline object is no object") 210 | } 211 | return nil 212 | } 213 | -------------------------------------------------------------------------------- /visitors/nilVisitor.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package visitors 19 | 20 | import structform "github.com/elastic/go-structform" 21 | 22 | type emptyVisitor struct { 23 | } 24 | 25 | func NilVisitor() structform.Visitor { 26 | return (*emptyVisitor)(nil) 27 | } 28 | 29 | func (e *emptyVisitor) OnObjectStart(len int, baseType structform.BaseType) error { 30 | return nil 31 | } 32 | 33 | func (e *emptyVisitor) OnObjectFinished() error { 34 | return nil 35 | } 36 | 37 | func (e *emptyVisitor) OnKey(s string) error { 38 | return nil 39 | } 40 | 41 | func (e *emptyVisitor) OnArrayStart(len int, baseType structform.BaseType) error { 42 | return nil 43 | } 44 | 45 | func (e *emptyVisitor) OnArrayFinished() error { 46 | return nil 47 | } 48 | 49 | func (e *emptyVisitor) OnNil() error { 50 | return nil 51 | } 52 | 53 | func (e *emptyVisitor) OnBool(b bool) error { 54 | return nil 55 | } 56 | 57 | func (e *emptyVisitor) OnString(s string) error { 58 | return nil 59 | } 60 | 61 | func (e *emptyVisitor) OnInt8(i int8) error { 62 | return nil 63 | } 64 | 65 | func (e *emptyVisitor) OnInt16(i int16) error { 66 | return nil 67 | } 68 | 69 | func (e *emptyVisitor) OnInt32(i int32) error { 70 | return nil 71 | } 72 | 73 | func (e *emptyVisitor) OnInt64(i int64) error { 74 | return nil 75 | } 76 | 77 | func (e *emptyVisitor) OnInt(i int) error { 78 | return nil 79 | } 80 | 81 | func (e *emptyVisitor) OnByte(b byte) error { 82 | return nil 83 | } 84 | 85 | func (e *emptyVisitor) OnUint8(u uint8) error { 86 | return nil 87 | } 88 | 89 | func (e *emptyVisitor) OnUint16(u uint16) error { 90 | return nil 91 | } 92 | 93 | func (e *emptyVisitor) OnUint32(u uint32) error { 94 | return nil 95 | } 96 | 97 | func (e *emptyVisitor) OnUint64(u uint64) error { 98 | return nil 99 | } 100 | 101 | func (e *emptyVisitor) OnUint(u uint) error { 102 | return nil 103 | } 104 | 105 | func (e *emptyVisitor) OnFloat32(f float32) error { 106 | return nil 107 | } 108 | 109 | func (e *emptyVisitor) OnFloat64(f float64) error { 110 | return nil 111 | } 112 | 113 | func (e *emptyVisitor) OnBoolArray([]bool) error { 114 | return nil 115 | } 116 | 117 | func (e *emptyVisitor) OnStringArray([]string) error { 118 | return nil 119 | } 120 | 121 | func (e *emptyVisitor) OnInt8Array([]int8) error { 122 | return nil 123 | } 124 | 125 | func (e *emptyVisitor) OnInt16Array([]int16) error { 126 | return nil 127 | } 128 | 129 | func (e *emptyVisitor) OnInt32Array([]int32) error { 130 | return nil 131 | } 132 | 133 | func (e *emptyVisitor) OnInt64Array([]int64) error { 134 | return nil 135 | } 136 | 137 | func (e *emptyVisitor) OnIntArray([]int) error { 138 | return nil 139 | } 140 | 141 | func (e *emptyVisitor) OnBytes([]byte) error { 142 | return nil 143 | } 144 | 145 | func (e *emptyVisitor) OnUint8Array([]uint8) error { 146 | return nil 147 | } 148 | 149 | func (e *emptyVisitor) OnUint16Array([]uint16) error { 150 | return nil 151 | } 152 | 153 | func (e *emptyVisitor) OnUint32Array([]uint32) error { 154 | return nil 155 | } 156 | 157 | func (e *emptyVisitor) OnUint64Array([]uint64) error { 158 | return nil 159 | } 160 | 161 | func (e *emptyVisitor) OnUintArray([]uint) error { 162 | return nil 163 | } 164 | 165 | func (e *emptyVisitor) OnFloat32Array([]float32) error { 166 | return nil 167 | } 168 | 169 | func (e *emptyVisitor) OnFloat64Array([]float64) error { 170 | return nil 171 | } 172 | 173 | func (e *emptyVisitor) OnBoolObject(map[string]bool) error { 174 | return nil 175 | } 176 | 177 | func (e *emptyVisitor) OnStringObject(map[string]string) error { 178 | return nil 179 | } 180 | 181 | func (e *emptyVisitor) OnInt8Object(map[string]int8) error { 182 | return nil 183 | } 184 | 185 | func (e *emptyVisitor) OnInt16Object(map[string]int16) error { 186 | return nil 187 | } 188 | 189 | func (e *emptyVisitor) OnInt32Object(map[string]int32) error { 190 | return nil 191 | } 192 | 193 | func (e *emptyVisitor) OnInt64Object(map[string]int64) error { 194 | return nil 195 | } 196 | 197 | func (e *emptyVisitor) OnIntObject(map[string]int) error { 198 | return nil 199 | } 200 | 201 | func (e *emptyVisitor) OnUint8Object(map[string]uint8) error { 202 | return nil 203 | } 204 | 205 | func (e *emptyVisitor) OnUint16Object(map[string]uint16) error { 206 | return nil 207 | } 208 | 209 | func (e *emptyVisitor) OnUint32Object(map[string]uint32) error { 210 | return nil 211 | } 212 | 213 | func (e *emptyVisitor) OnUint64Object(map[string]uint64) error { 214 | return nil 215 | } 216 | 217 | func (e *emptyVisitor) OnUintObject(map[string]uint) error { 218 | return nil 219 | } 220 | 221 | func (e *emptyVisitor) OnFloat32Object(map[string]float32) error { 222 | return nil 223 | } 224 | 225 | func (e *emptyVisitor) OnFloat64Object(map[string]float64) error { 226 | return nil 227 | } 228 | 229 | func (e *emptyVisitor) OnStringRef(s []byte) error { 230 | return nil 231 | } 232 | 233 | func (e *emptyVisitor) OnKeyRef(s []byte) error { 234 | return nil 235 | } 236 | -------------------------------------------------------------------------------- /visitors/stringer.go: -------------------------------------------------------------------------------- 1 | // Licensed to Elasticsearch B.V. under one or more contributor 2 | // license agreements. See the NOTICE file distributed with 3 | // this work for additional information regarding copyright 4 | // ownership. Elasticsearch B.V. licenses this file to you under 5 | // the Apache License, Version 2.0 (the "License"); you may 6 | // not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | package visitors 19 | 20 | import ( 21 | "fmt" 22 | 23 | structform "github.com/elastic/go-structform" 24 | ) 25 | 26 | type StringConvVisitor struct { 27 | active structform.ExtVisitor 28 | } 29 | 30 | func NewStringConvVisitor(target structform.ExtVisitor) *StringConvVisitor { 31 | return &StringConvVisitor{target} 32 | } 33 | 34 | func (v *StringConvVisitor) SetActive(a structform.ExtVisitor) { 35 | v.active = a 36 | } 37 | 38 | func (v *StringConvVisitor) OnObjectStart(l int, t structform.BaseType) error { 39 | return v.active.OnObjectStart(l, t) 40 | } 41 | 42 | func (v *StringConvVisitor) OnObjectFinished() error { 43 | return v.active.OnObjectFinished() 44 | } 45 | 46 | func (v *StringConvVisitor) OnKey(s string) error { 47 | return v.active.OnKey(s) 48 | } 49 | 50 | func (v *StringConvVisitor) OnKeyRef(s []byte) error { 51 | return v.active.OnKeyRef(s) 52 | } 53 | 54 | func (v *StringConvVisitor) OnArrayStart(l int, t structform.BaseType) error { 55 | return v.active.OnArrayStart(l, t) 56 | } 57 | 58 | func (v *StringConvVisitor) OnArrayFinished() error { 59 | return v.active.OnArrayFinished() 60 | } 61 | 62 | func (v *StringConvVisitor) OnNil() error { 63 | return v.OnString("") 64 | } 65 | 66 | func (v *StringConvVisitor) OnBool(b bool) error { 67 | t := "false" 68 | if b { 69 | t = "true" 70 | } 71 | return v.OnString(t) 72 | } 73 | 74 | func (v *StringConvVisitor) OnString(s string) error { 75 | return v.active.OnString(s) 76 | } 77 | 78 | func (v *StringConvVisitor) OnStringRef(b []byte) error { 79 | return v.active.OnStringRef(b) 80 | } 81 | 82 | func (v *StringConvVisitor) OnInt8(i int8) error { 83 | return v.OnInt64(int64(i)) 84 | } 85 | 86 | func (v *StringConvVisitor) OnInt16(i int16) error { 87 | return v.OnInt64(int64(i)) 88 | } 89 | 90 | func (v *StringConvVisitor) OnInt32(i int32) error { 91 | return v.OnInt64(int64(i)) 92 | } 93 | 94 | func (v *StringConvVisitor) OnInt64(i int64) error { 95 | return v.OnString(fmt.Sprintf("%v", i)) 96 | } 97 | 98 | func (v *StringConvVisitor) OnInt(i int) error { 99 | return v.OnInt64(int64(i)) 100 | } 101 | 102 | func (v *StringConvVisitor) OnUint8(i uint8) error { 103 | return v.OnUint64(uint64(i)) 104 | } 105 | 106 | func (v *StringConvVisitor) OnUint16(i uint16) error { 107 | return v.OnUint64(uint64(i)) 108 | } 109 | 110 | func (v *StringConvVisitor) OnUint32(i uint32) error { 111 | return v.OnUint64(uint64(i)) 112 | } 113 | 114 | func (v *StringConvVisitor) OnUint64(i uint64) error { 115 | return v.OnString(fmt.Sprintf("%v", i)) 116 | } 117 | 118 | func (v *StringConvVisitor) OnUint(i uint) error { 119 | return v.OnUint64(uint64(i)) 120 | } 121 | 122 | func (v *StringConvVisitor) OnFloat32(f float32) error { 123 | return v.OnString(fmt.Sprintf("%v", f)) 124 | } 125 | 126 | func (v *StringConvVisitor) OnFloat64(f float64) error { 127 | return v.OnString(fmt.Sprintf("%v", f)) 128 | } 129 | --------------------------------------------------------------------------------