├── def_b.go ├── resolver_test.go ├── testdata ├── def.go └── test.sh ├── .golangci.yml ├── internal ├── fortest │ ├── cmd │ │ ├── main_test.go │ │ └── main.go │ └── define │ │ ├── define │ │ └── define.go │ │ └── define.go └── generator │ ├── ptn │ ├── pattern.go │ └── const.go │ ├── structure │ ├── code_time.go │ ├── code_pointer.go │ ├── code_named.go │ ├── code_ident.go │ ├── pattern.go │ ├── code_array.go │ ├── code_slice.go │ ├── code_map.go │ ├── node.go │ └── structure.go │ ├── gen_test.go │ ├── analyze.go │ └── gen.go ├── go.mod ├── msgpack ├── dec │ ├── byte.go │ ├── rune.go │ ├── nil.go │ ├── bool.go │ ├── dec.go │ ├── read.go │ ├── map.go │ ├── slice.go │ ├── complex.go │ ├── string.go │ ├── time.go │ ├── float.go │ ├── uint.go │ ├── int.go │ └── struct.go ├── enc │ ├── rune.go │ ├── nil.go │ ├── bool.go │ ├── enc.go │ ├── float.go │ ├── map.go │ ├── complex.go │ ├── slice.go │ ├── byte.go │ ├── time.go │ ├── struct.go │ ├── set.go │ ├── string.go │ ├── uint.go │ └── int.go ├── encode.go ├── decode.go ├── resolver.go └── msgpack.go ├── .gitignore ├── go.sum ├── .github ├── no-response.yml └── workflows │ ├── stale.yml │ ├── release.yml │ └── test.yml ├── .goreleaser.yml ├── LICENSE ├── main.go ├── def_a.go ├── generator_test.go ├── README.md └── msgpack_test.go /def_b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type outside struct { 4 | Int int 5 | } 6 | -------------------------------------------------------------------------------- /resolver_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func RegisterGeneratedResolver() { 4 | panic("this is dummy.") 5 | } 6 | -------------------------------------------------------------------------------- /testdata/def.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | type DuplicateTag struct { 4 | A int `msgpack:"d"` 5 | B int `msgpack:"d"` 6 | } 7 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | linters: 4 | exclusions: 5 | rules: 6 | - linters: [staticcheck] 7 | text: ST1001 8 | -------------------------------------------------------------------------------- /internal/fortest/cmd/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestA(t *testing.T) { 8 | main() 9 | } 10 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/shamaton/msgpackgen 2 | 3 | go 1.23 4 | 5 | require ( 6 | github.com/dave/jennifer v1.7.1 7 | github.com/shamaton/msgpack/v2 v2.3.1 8 | ) 9 | -------------------------------------------------------------------------------- /internal/fortest/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // S is a definition for test 4 | type S struct { 5 | Int int 6 | } 7 | 8 | func main() { 9 | s := S{Int: 1} 10 | s.Int += 1 11 | } 12 | -------------------------------------------------------------------------------- /msgpack/dec/byte.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | // AsByte checks codes and returns the got bytes as byte 4 | func (d *Decoder) AsByte(offset int) (byte, int, error) { 5 | b, offset := d.readSize1(offset) 6 | return b, offset, nil 7 | } 8 | -------------------------------------------------------------------------------- /msgpack/dec/rune.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | // AsRune checks codes and returns the got bytes as rune 4 | func (d *Decoder) AsRune(offset int) (rune, int, error) { 5 | v, offset, err := d.asInt(offset) 6 | return rune(v), offset, err 7 | } 8 | -------------------------------------------------------------------------------- /msgpack/dec/nil.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import "github.com/shamaton/msgpack/v2/def" 4 | 5 | // IsCodeNil returns true if the next data is def.Nil. 6 | func (d *Decoder) IsCodeNil(offset int) bool { 7 | return def.Nil == d.data[offset] 8 | } 9 | -------------------------------------------------------------------------------- /internal/generator/ptn/pattern.go: -------------------------------------------------------------------------------- 1 | package ptn 2 | 3 | import "fmt" 4 | 5 | // PrivateFuncName gets a function name that adapts funcName to the pattern 6 | func PrivateFuncName(funcName string) string { 7 | return fmt.Sprintf("___%s", funcName) 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | *.coverprofile 14 | 15 | .DS_Store 16 | .idea 17 | -------------------------------------------------------------------------------- /internal/fortest/define/define/define.go: -------------------------------------------------------------------------------- 1 | package define 2 | 3 | // B is a definition for test 4 | type B struct { 5 | Int int 6 | } 7 | 8 | // DotImport is a definition for test 9 | type DotImport struct { 10 | Int int 11 | } 12 | 13 | // Time is a definition for test 14 | type Time struct { 15 | Int int 16 | } 17 | -------------------------------------------------------------------------------- /msgpack/enc/rune.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | // CalcRune check value and returns data size that need. 4 | func (e *Encoder) CalcRune(v rune) int { 5 | return e.calcInt(int64(v)) 6 | } 7 | 8 | // WriteRune sets the contents of v to the buffer. 9 | func (e *Encoder) WriteRune(v rune, offset int) int { 10 | return e.writeInt(int64(v), offset) 11 | } 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo= 2 | github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= 3 | github.com/shamaton/msgpack/v2 v2.3.1 h1:R3QNLIGA/tbdczNMZ5PCRxrXvy+fnzsIaHG4kKMgWYo= 4 | github.com/shamaton/msgpack/v2 v2.3.1/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI= 5 | -------------------------------------------------------------------------------- /internal/generator/ptn/const.go: -------------------------------------------------------------------------------- 1 | package ptn 2 | 3 | const ( 4 | // PkTop is import path for jen.Qual(path) 5 | PkTop = "github.com/shamaton/msgpackgen/msgpack" 6 | // PkEnc is import path for jen.Qual(path) 7 | PkEnc = PkTop + "/enc" 8 | // PkDec is import path for jen.Qual(path) 9 | PkDec = PkTop + "/dec" 10 | 11 | IdEncoder = "encoder" 12 | IdDecoder = "decoder" 13 | ) 14 | -------------------------------------------------------------------------------- /msgpack/enc/nil.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | import "github.com/shamaton/msgpack/v2/def" 4 | 5 | // CalcNil returns data size that need. 6 | func (e *Encoder) CalcNil() int { 7 | return def.Byte1 8 | } 9 | 10 | // WriteNil sets the contents of v to the buffer. 11 | func (e *Encoder) WriteNil(offset int) int { 12 | offset = e.setByte1Int(def.Nil, offset) 13 | return offset 14 | } 15 | -------------------------------------------------------------------------------- /testdata/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | DIR=$(dirname "$0") 5 | CURRENT=$(cd "$DIR" && pwd) 6 | PJ=$(cd "$CURRENT" && cd ../ && pwd) 7 | cd "${PJ}" || exit 1 8 | 9 | go generate 10 | go test -v -count=1 \ 11 | --coverpkg=github.com/shamaton/msgpackgen/... \ 12 | --coverprofile=coverage.coverprofile \ 13 | --covermode=atomic ./... 14 | git checkout resolver_test.go 15 | rm -f resolver.msgpackgen.go -------------------------------------------------------------------------------- /internal/fortest/define/define.go: -------------------------------------------------------------------------------- 1 | package define 2 | 3 | import ( 4 | define2 "github.com/shamaton/msgpackgen/internal/fortest/define/define" 5 | . "time" 6 | ) 7 | 8 | // A is a definition for test 9 | type A struct { 10 | Int int 11 | B define2.B 12 | } 13 | 14 | // AA is a definition for test 15 | type AA struct { 16 | Time 17 | } 18 | 19 | // NotGeneratedChild is a definition for test 20 | type NotGeneratedChild struct { 21 | Interface interface{} 22 | } 23 | -------------------------------------------------------------------------------- /msgpack/dec/bool.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import ( 4 | "github.com/shamaton/msgpack/v2/def" 5 | ) 6 | 7 | // AsBool checks codes and returns the got bytes as bool 8 | func (d *Decoder) AsBool(offset int) (bool, int, error) { 9 | code := d.data[offset] 10 | offset++ 11 | 12 | switch code { 13 | case def.True: 14 | return true, offset, nil 15 | case def.False, def.Nil: 16 | return false, offset, nil 17 | } 18 | return false, 0, d.errorTemplate(code, "AsBool") 19 | } 20 | -------------------------------------------------------------------------------- /msgpack/enc/bool.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | import "github.com/shamaton/msgpack/v2/def" 4 | 5 | // CalcBool returns data size that need. 6 | func (e *Encoder) CalcBool(v bool) int { 7 | return def.Byte1 8 | } 9 | 10 | // WriteBool sets the contents of v to the buffer. 11 | func (e *Encoder) WriteBool(v bool, offset int) int { 12 | if v { 13 | offset = e.setByte1Int(def.True, offset) 14 | } else { 15 | offset = e.setByte1Int(def.False, offset) 16 | } 17 | return offset 18 | } 19 | -------------------------------------------------------------------------------- /msgpack/enc/enc.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | // A Encoder calculates and writes the required byte size. 4 | type Encoder struct { 5 | d []byte 6 | } 7 | 8 | // NewEncoder creates a new Encoder for serialization. 9 | func NewEncoder() *Encoder { 10 | return &Encoder{} 11 | } 12 | 13 | // MakeBytes reserves the required byte array 14 | func (e *Encoder) MakeBytes(size int) { 15 | e.d = make([]byte, size) 16 | } 17 | 18 | // EncodedBytes gets encoded bytes. 19 | func (e *Encoder) EncodedBytes() []byte { 20 | return e.d 21 | } 22 | -------------------------------------------------------------------------------- /msgpack/dec/dec.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // A Decoder holds an encoded bytes array and has methods for encoding. 8 | type Decoder struct { 9 | data []byte 10 | } 11 | 12 | // NewDecoder creates a new Decoder for deserialization. 13 | func NewDecoder(data []byte) *Decoder { 14 | return &Decoder{data: data} 15 | } 16 | 17 | // Len get encoded data length. 18 | func (d *Decoder) Len() int { return len(d.data) } 19 | 20 | func (d *Decoder) errorTemplate(code byte, str string) error { 21 | return fmt.Errorf("msgpackgen : invalid code %x decoding %s", code, str) 22 | } 23 | -------------------------------------------------------------------------------- /msgpack/dec/read.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import ( 4 | "github.com/shamaton/msgpack/v2/def" 5 | ) 6 | 7 | func (d *Decoder) readSize1(index int) (byte, int) { 8 | rb := def.Byte1 9 | return d.data[index], index + rb 10 | } 11 | 12 | func (d *Decoder) readSize2(index int) ([]byte, int) { 13 | rb := def.Byte2 14 | return d.data[index : index+rb], index + rb 15 | } 16 | 17 | func (d *Decoder) readSize4(index int) ([]byte, int) { 18 | rb := def.Byte4 19 | return d.data[index : index+rb], index + rb 20 | } 21 | 22 | func (d *Decoder) readSize8(index int) ([]byte, int) { 23 | rb := def.Byte8 24 | return d.data[index : index+rb], index + rb 25 | } 26 | 27 | func (d *Decoder) readSizeN(index, n int) ([]byte, int) { 28 | return d.data[index : index+n], index + n 29 | } 30 | -------------------------------------------------------------------------------- /.github/no-response.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-no-response - https://github.com/probot/no-response 2 | 3 | # Number of days of inactivity before an Issue is closed for lack of response 4 | daysUntilClose: 28 5 | # Label requiring a response 6 | responseRequiredLabel: more-information-needed 7 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable 8 | closeComment: > 9 | This issue has been automatically closed because there has been no response 10 | to our request for more information from the original author. With only the 11 | information that is currently in the issue, we don't have enough information 12 | to take action. Please reach out if you have or find the answers we need so 13 | that we can investigate further. 14 | 15 | -------------------------------------------------------------------------------- /msgpack/encode.go: -------------------------------------------------------------------------------- 1 | package msgpack 2 | 3 | import "github.com/shamaton/msgpack/v2" 4 | 5 | // MarshalAsMap encodes data as map format. 6 | // This is the same thing that StructAsArray sets false. 7 | func MarshalAsMap(v interface{}) ([]byte, error) { 8 | if b, err := encAsMapResolver(v); err != nil { 9 | return nil, err 10 | } else if b != nil { 11 | return b, nil 12 | } 13 | 14 | return msgpack.MarshalAsMap(v) 15 | } 16 | 17 | // MarshalAsArray encodes data as array format. 18 | // This is the same thing that StructAsArray sets true. 19 | func MarshalAsArray(v interface{}) ([]byte, error) { 20 | if b, err := encAsArrayResolver(v); err != nil { 21 | return nil, err 22 | } else if b != nil { 23 | return b, nil 24 | } 25 | 26 | return msgpack.MarshalAsArray(v) 27 | } 28 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | project_name: msgpackgen 4 | 5 | before: 6 | hooks: 7 | - go mod tidy 8 | builds: 9 | - main: . 10 | binary: msgpackgen 11 | ldflags: 12 | - -s -w 13 | - -X main.Version={{.Version}} 14 | - -X main.Revision={{.ShortCommit}} 15 | env: 16 | - CGO_ENABLED=0 17 | goos: 18 | - darwin 19 | - linux 20 | - windows 21 | archives: 22 | - name_template: >- 23 | {{ .ProjectName }}_{{ tolower .Os }}_ 24 | {{- if eq .Arch "amd64" -}}x86_64 25 | {{- else if eq .Arch "386" -}}i386 26 | {{- else -}}{{ .Arch }}{{- end -}} 27 | {{- if .Arm -}}v{{ .Arm }}{{- end -}} 28 | 29 | format_overrides: 30 | - goos: windows 31 | formats: [zip] 32 | release: 33 | prerelease: auto -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Mark stale issues and pull requests 2 | 3 | on: 4 | schedule: 5 | - cron: '30 11 * * *' 6 | 7 | jobs: 8 | stale: 9 | 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | pull-requests: write 14 | 15 | steps: 16 | - uses: actions/stale@v3 17 | with: 18 | days-before-issue-stale: 30 19 | days-before-issue-close: 14 20 | stale-issue-label: "stale" 21 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." 22 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." 23 | days-before-pr-stale: -1 24 | days-before-pr-close: -1 25 | repo-token: ${{ secrets.GITHUB_TOKEN }} 26 | -------------------------------------------------------------------------------- /msgpack/decode.go: -------------------------------------------------------------------------------- 1 | package msgpack 2 | 3 | import "github.com/shamaton/msgpack/v2" 4 | 5 | // UnmarshalAsMap decodes data that is encoded as map format. 6 | // This is the same thing that StructAsArray sets false. 7 | func UnmarshalAsMap(data []byte, v interface{}) error { 8 | b, err := decAsMapResolver(data, v) 9 | if err != nil { 10 | return err 11 | } 12 | if b { 13 | return nil 14 | } 15 | return msgpack.UnmarshalAsMap(data, v) 16 | } 17 | 18 | // UnmarshalAsArray decodes data that is encoded as array format. 19 | // This is the same thing that StructAsArray sets true. 20 | func UnmarshalAsArray(data []byte, v interface{}) error { 21 | b, err := decAsArrayResolver(data, v) 22 | if err != nil { 23 | return err 24 | } 25 | if b { 26 | return nil 27 | } 28 | return msgpack.UnmarshalAsArray(data, v) 29 | } 30 | -------------------------------------------------------------------------------- /msgpack/dec/map.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/shamaton/msgpack/v2/def" 7 | ) 8 | 9 | func (d *Decoder) isFixMap(v byte) bool { 10 | return def.FixMap <= v && v <= def.FixMap+0x0f 11 | } 12 | 13 | // MapLength reads the need bytes and convert to length value. 14 | func (d *Decoder) MapLength(offset int) (int, int, error) { 15 | code, offset := d.readSize1(offset) 16 | 17 | switch { 18 | case d.isFixMap(code): 19 | return int(code - def.FixMap), offset, nil 20 | case code == def.Map16: 21 | bs, offset := d.readSize2(offset) 22 | return int(binary.BigEndian.Uint16(bs)), offset, nil 23 | case code == def.Map32: 24 | bs, offset := d.readSize4(offset) 25 | return int(binary.BigEndian.Uint32(bs)), offset, nil 26 | } 27 | return 0, 0, d.errorTemplate(code, "MapLength") 28 | } 29 | -------------------------------------------------------------------------------- /msgpack/enc/float.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/shamaton/msgpack/v2/def" 7 | ) 8 | 9 | // CalcFloat32 returns data size that need. 10 | func (e *Encoder) CalcFloat32(v float32) int { 11 | return def.Byte1 + def.Byte4 12 | } 13 | 14 | // CalcFloat64 returns data size that need. 15 | func (e *Encoder) CalcFloat64(v float64) int { 16 | return def.Byte1 + def.Byte8 17 | } 18 | 19 | // WriteFloat32 sets the contents of v to the buffer. 20 | func (e *Encoder) WriteFloat32(v float32, offset int) int { 21 | offset = e.setByte1Int(def.Float32, offset) 22 | offset = e.setByte4Uint64(uint64(math.Float32bits(v)), offset) 23 | return offset 24 | } 25 | 26 | // WriteFloat64 sets the contents of v to the buffer. 27 | func (e *Encoder) WriteFloat64(v float64, offset int) int { 28 | offset = e.setByte1Int(def.Float64, offset) 29 | offset = e.setByte8Uint64(math.Float64bits(v), offset) 30 | return offset 31 | } 32 | -------------------------------------------------------------------------------- /msgpack/resolver.go: -------------------------------------------------------------------------------- 1 | package msgpack 2 | 3 | type ( 4 | // EncResolver is a definition to resolve serialization. 5 | EncResolver func(i interface{}) ([]byte, error) 6 | // DecResolver is a definition to resolve de-serialization. 7 | DecResolver func(data []byte, i interface{}) (bool, error) 8 | ) 9 | 10 | var ( 11 | encAsMapResolver EncResolver = func(i interface{}) ([]byte, error) { 12 | return nil, nil 13 | } 14 | encAsArrayResolver = encAsMapResolver 15 | 16 | decAsMapResolver DecResolver = func(data []byte, i interface{}) (bool, error) { 17 | return false, nil 18 | } 19 | decAsArrayResolver = decAsMapResolver 20 | ) 21 | 22 | // SetResolver sets generated resolvers to bridge variables. 23 | func SetResolver(encAsMap, encAsArray EncResolver, decAsMap, decAsArray DecResolver) { 24 | encAsMapResolver = encAsMap 25 | encAsArrayResolver = encAsArray 26 | decAsMapResolver = decAsMap 27 | decAsArrayResolver = decAsArray 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: 5 | - "v[0-9]+.[0-9]+.[0-9]+" 6 | 7 | env: 8 | GOPATH: ${{ github.workspace }} 9 | WORKSPACE: ${{ github.workspace }}/src/github.com/${{ github.repository }} 10 | 11 | jobs: 12 | goreleaser: 13 | defaults: 14 | run: 15 | working-directory: ${{ env.WORKSPACE }} 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v5 20 | with: 21 | fetch-depth: 1 22 | path: ${{ env.WORKSPACE }} 23 | - name: Setup Go 24 | uses: actions/setup-go@v5 25 | with: 26 | go-version: 1.25 27 | - name: Run GoReleaser 28 | uses: goreleaser/goreleaser-action@v6 29 | with: 30 | distribution: goreleaser 31 | version: '~> v2' 32 | args: release --clean 33 | workdir: ${{ env.WORKSPACE }} 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /msgpack/enc/map.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | 7 | "github.com/shamaton/msgpack/v2/def" 8 | ) 9 | 10 | // CalcMapLength checks value and returns data size that need. 11 | func (e *Encoder) CalcMapLength(l int) (int, error) { 12 | ret := def.Byte1 13 | 14 | if l <= 0x0f { 15 | // do nothing 16 | } else if l <= math.MaxUint16 { 17 | ret += def.Byte2 18 | } else if uint(l) <= math.MaxUint32 { 19 | ret += def.Byte4 20 | } else { 21 | // not supported error 22 | return 0, fmt.Errorf("not support this map length : %d", l) 23 | } 24 | return ret, nil 25 | } 26 | 27 | // WriteMapLength sets the contents of l to the buffer. 28 | func (e *Encoder) WriteMapLength(l int, offset int) int { 29 | 30 | // format 31 | if l <= 0x0f { 32 | offset = e.setByte1Int(def.FixMap+l, offset) 33 | } else if l <= math.MaxUint16 { 34 | offset = e.setByte1Int(def.Map16, offset) 35 | offset = e.setByte2Int(l, offset) 36 | } else if uint(l) <= math.MaxUint32 { 37 | offset = e.setByte1Int(def.Map32, offset) 38 | offset = e.setByte4Int(l, offset) 39 | } 40 | return offset 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Masayuki Shamoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /msgpack/msgpack.go: -------------------------------------------------------------------------------- 1 | package msgpack 2 | 3 | import ( 4 | "github.com/shamaton/msgpack/v2" 5 | ) 6 | 7 | // Marshal returns the MessagePack-encoded byte array of v. 8 | func Marshal(v interface{}) ([]byte, error) { 9 | if StructAsArray() { 10 | return MarshalAsArray(v) 11 | } 12 | return MarshalAsMap(v) 13 | } 14 | 15 | // Unmarshal analyzes the MessagePack-encoded data and stores 16 | // the result into the pointer of v. 17 | func Unmarshal(data []byte, v interface{}) error { 18 | if StructAsArray() { 19 | return UnmarshalAsArray(data, v) 20 | } 21 | return UnmarshalAsMap(data, v) 22 | } 23 | 24 | // SetStructAsArray sets default encoding option. 25 | // If this option sets true, default encoding sets to array-format. 26 | func SetStructAsArray(on bool) { 27 | msgpack.StructAsArray = on 28 | } 29 | 30 | // StructAsArray gets default encoding option. 31 | // If this option sets true, default encoding sets to array-format. 32 | func StructAsArray() bool { 33 | return msgpack.StructAsArray 34 | } 35 | 36 | // SetComplexTypeCode sets def.complexTypeCode in github.com/shamaton/msgpack 37 | func SetComplexTypeCode(code int8) { 38 | msgpack.SetComplexTypeCode(code) 39 | } 40 | -------------------------------------------------------------------------------- /msgpack/dec/slice.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/shamaton/msgpack/v2/def" 7 | ) 8 | 9 | func (d *Decoder) isFixSlice(v byte) bool { 10 | return def.FixArray <= v && v <= def.FixArray+0x0f 11 | } 12 | 13 | // SliceLength reads the need bytes and convert to length value. 14 | func (d *Decoder) SliceLength(offset int) (int, int, error) { 15 | code, offset := d.readSize1(offset) 16 | 17 | switch { 18 | case d.isFixSlice(code): 19 | return int(code - def.FixArray), offset, nil 20 | case code == def.Array16: 21 | bs, offset := d.readSize2(offset) 22 | return int(binary.BigEndian.Uint16(bs)), offset, nil 23 | case code == def.Array32: 24 | bs, offset := d.readSize4(offset) 25 | return int(binary.BigEndian.Uint32(bs)), offset, nil 26 | 27 | case code == def.Bin8: 28 | l, offset := d.readSize1(offset) 29 | return int(uint8(l)), offset, nil 30 | case code == def.Bin16: 31 | bs, offset := d.readSize2(offset) 32 | return int(binary.BigEndian.Uint16(bs)), offset, nil 33 | case code == def.Bin32: 34 | bs, offset := d.readSize4(offset) 35 | return int(binary.BigEndian.Uint32(bs)), offset, nil 36 | } 37 | return 0, 0, d.errorTemplate(code, "SliceLength") 38 | } 39 | -------------------------------------------------------------------------------- /msgpack/enc/complex.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/shamaton/msgpack/v2/def" 7 | ) 8 | 9 | // CalcComplex64 returns data size that need. 10 | func (e *Encoder) CalcComplex64(v complex64) int { 11 | return def.Byte1 + def.Byte1 + def.Byte8 12 | } 13 | 14 | // CalcComplex128 returns data size that need. 15 | func (e *Encoder) CalcComplex128(v complex128) int { 16 | return def.Byte1 + def.Byte1 + def.Byte16 17 | } 18 | 19 | // WriteComplex64 sets the contents of v to the buffer. 20 | func (e *Encoder) WriteComplex64(v complex64, offset int) int { 21 | offset = e.setByte1Int(def.Fixext8, offset) 22 | offset = e.setByte1Int(int(def.ComplexTypeCode()), offset) 23 | offset = e.setByte4Uint64(uint64(math.Float32bits(real(v))), offset) 24 | offset = e.setByte4Uint64(uint64(math.Float32bits(imag(v))), offset) 25 | return offset 26 | } 27 | 28 | // WriteComplex128 sets the contents of v to the buffer. 29 | func (e *Encoder) WriteComplex128(v complex128, offset int) int { 30 | offset = e.setByte1Int(def.Fixext16, offset) 31 | offset = e.setByte1Int(int(def.ComplexTypeCode()), offset) 32 | offset = e.setByte8Uint64(math.Float64bits(real(v)), offset) 33 | offset = e.setByte8Uint64(math.Float64bits(imag(v)), offset) 34 | return offset 35 | } 36 | -------------------------------------------------------------------------------- /msgpack/enc/slice.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | 7 | "github.com/shamaton/msgpack/v2/def" 8 | ) 9 | 10 | // CalcSliceLength checks values and returns data size that need. 11 | func (e *Encoder) CalcSliceLength(l int, isChildTypeByte bool) (int, error) { 12 | 13 | if isChildTypeByte { 14 | return e.calcByteSlice(l) 15 | } 16 | 17 | if l <= 0x0f { 18 | // format code only 19 | return def.Byte1, nil 20 | } else if l <= math.MaxUint16 { 21 | return def.Byte1 + def.Byte2, nil 22 | } else if uint(l) <= math.MaxUint32 { 23 | return def.Byte1 + def.Byte4, nil 24 | } 25 | return 0, fmt.Errorf("not support this array length : %d", l) 26 | } 27 | 28 | // WriteSliceLength sets the contents of l to the buffer. 29 | func (e *Encoder) WriteSliceLength(l int, offset int, isChildTypeByte bool) int { 30 | if isChildTypeByte { 31 | return e.writeByteSliceLength(l, offset) 32 | } 33 | 34 | // format size 35 | if l <= 0x0f { 36 | offset = e.setByte1Int(def.FixArray+l, offset) 37 | } else if l <= math.MaxUint16 { 38 | offset = e.setByte1Int(def.Array16, offset) 39 | offset = e.setByte2Int(l, offset) 40 | } else if uint(l) <= math.MaxUint32 { 41 | offset = e.setByte1Int(def.Array32, offset) 42 | offset = e.setByte4Int(l, offset) 43 | } 44 | return offset 45 | } 46 | -------------------------------------------------------------------------------- /msgpack/enc/byte.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | 7 | "github.com/shamaton/msgpack/v2/def" 8 | ) 9 | 10 | func (e *Encoder) calcByteSlice(l int) (int, error) { 11 | if l <= math.MaxUint8 { 12 | return def.Byte1 + def.Byte1, nil 13 | } else if l <= math.MaxUint16 { 14 | return def.Byte1 + def.Byte2, nil 15 | } else if uint(l) <= math.MaxUint32 { 16 | return def.Byte1 + def.Byte4, nil 17 | } 18 | // not supported error 19 | return 0, fmt.Errorf("not support this array length : %d", l) 20 | } 21 | 22 | func (e *Encoder) writeByteSliceLength(l int, offset int) int { 23 | if l <= math.MaxUint8 { 24 | offset = e.setByte1Int(def.Bin8, offset) 25 | offset = e.setByte1Int(l, offset) 26 | } else if l <= math.MaxUint16 { 27 | offset = e.setByte1Int(def.Bin16, offset) 28 | offset = e.setByte2Int(l, offset) 29 | } else if uint(l) <= math.MaxUint32 { 30 | offset = e.setByte1Int(def.Bin32, offset) 31 | offset = e.setByte4Int(l, offset) 32 | } 33 | return offset 34 | } 35 | 36 | // CalcByte returns data size that need. 37 | func (e *Encoder) CalcByte(b byte) int { 38 | return def.Byte1 39 | } 40 | 41 | // WriteByte sets the contents of v to the buffer. 42 | //nolint:govet // custom WriteByte signature is intentional (used only by generated code) 43 | func (e *Encoder) WriteByte(b byte, offset int) int { 44 | return e.setByte(b, offset) 45 | } 46 | -------------------------------------------------------------------------------- /msgpack/enc/time.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/shamaton/msgpack/v2/def" 7 | ) 8 | 9 | // CalcTime check value and returns data size that need. 10 | func (e *Encoder) CalcTime(t time.Time) int { 11 | secs := uint64(t.Unix()) 12 | if secs>>34 == 0 { 13 | data := uint64(t.Nanosecond())<<34 | secs 14 | if data&0xffffffff00000000 == 0 { 15 | return def.Byte1 + def.Byte1 + def.Byte4 16 | } 17 | return def.Byte1 + def.Byte1 + def.Byte8 18 | } 19 | 20 | return def.Byte1 + def.Byte1 + def.Byte1 + def.Byte4 + def.Byte8 21 | } 22 | 23 | // WriteTime sets the contents of t to the buffer. 24 | func (e *Encoder) WriteTime(t time.Time, offset int) int { 25 | secs := uint64(t.Unix()) 26 | if secs>>34 == 0 { 27 | data := uint64(t.Nanosecond())<<34 | secs 28 | if data&0xffffffff00000000 == 0 { 29 | offset = e.setByte1Int(def.Fixext4, offset) 30 | offset = e.setByte1Int(def.TimeStamp, offset) 31 | offset = e.setByte4Uint64(data, offset) 32 | return offset 33 | } 34 | 35 | offset = e.setByte1Int(def.Fixext8, offset) 36 | offset = e.setByte1Int(def.TimeStamp, offset) 37 | offset = e.setByte8Uint64(data, offset) 38 | return offset 39 | } 40 | 41 | offset = e.setByte1Int(def.Ext8, offset) 42 | offset = e.setByte1Int(12, offset) 43 | offset = e.setByte1Int(def.TimeStamp, offset) 44 | offset = e.setByte4Int(t.Nanosecond(), offset) 45 | offset = e.setByte8Uint64(secs, offset) 46 | return offset 47 | } 48 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "io" 6 | "log" 7 | "os" 8 | 9 | "github.com/shamaton/msgpackgen/internal/generator" 10 | ) 11 | 12 | var ( 13 | inputDir = flag.String("input-dir", ".", "input directory. input-file cannot be used at the same time") 14 | inputFile = flag.String("input-file", "", "input a specific file. input-dir cannot be used at the same time") 15 | outputDir = flag.String("output-dir", ".", "output directory") 16 | filename = flag.String("output-file", defaultFileName, "name of generated file") 17 | pointer = flag.Int("pointer", defaultPointerLevel, "pointer level to consider") 18 | useGopath = flag.Bool("use-gopath", false, "use GOPATH instead of go.mod") 19 | dryRun = flag.Bool("dry-run", false, "dry run mode") 20 | strict = flag.Bool("strict", false, "strict mode") 21 | verbose = flag.Bool("v", false, "verbose diagnostics") 22 | ) 23 | 24 | const ( 25 | defaultFileName = "resolver.msgpackgen.go" 26 | defaultPointerLevel = 1 27 | ) 28 | 29 | func main() { 30 | flag.Parse() 31 | err := generate( 32 | *inputDir, 33 | *inputFile, 34 | *outputDir, 35 | *filename, 36 | *pointer, 37 | *useGopath, 38 | *dryRun, 39 | *strict, 40 | *verbose, 41 | os.Stdout, 42 | ) 43 | if err != nil { 44 | log.Fatal(err) 45 | } 46 | } 47 | 48 | func generate(iDir, iFile, oDir, oFile string, p int, ugo, dry, s, v bool, w io.Writer) error { 49 | return generator.Run(iDir, iFile, oDir, oFile, p, ugo, dry, s, v, w) 50 | } 51 | -------------------------------------------------------------------------------- /msgpack/dec/complex.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "math" 7 | 8 | "github.com/shamaton/msgpack/v2/def" 9 | ) 10 | 11 | // AsComplex64 checks codes and returns the got bytes as complex64 12 | func (d *Decoder) AsComplex64(offset int) (complex64, int, error) { 13 | code, offset := d.readSize1(offset) 14 | 15 | switch code { 16 | case def.Fixext8: 17 | t, offset := d.readSize1(offset) 18 | if int8(t) != def.ComplexTypeCode() { 19 | return 0, 0, fmt.Errorf("fixext8. complex type is different %d, %d", int8(t), def.ComplexTypeCode()) 20 | } 21 | rb, offset := d.readSize4(offset) 22 | ib, offset := d.readSize4(offset) 23 | r := math.Float32frombits(binary.BigEndian.Uint32(rb)) 24 | i := math.Float32frombits(binary.BigEndian.Uint32(ib)) 25 | return complex(r, i), offset, nil 26 | } 27 | 28 | return 0, 0, d.errorTemplate(code, "AsComplex64") 29 | } 30 | 31 | // AsComplex128 checks codes and returns the got bytes as complex128 32 | func (d *Decoder) AsComplex128(offset int) (complex128, int, error) { 33 | code, offset := d.readSize1(offset) 34 | 35 | switch code { 36 | case def.Fixext16: 37 | t, offset := d.readSize1(offset) 38 | if int8(t) != def.ComplexTypeCode() { 39 | return 0, 0, fmt.Errorf("fixext16. complex type is different %d, %d", int8(t), def.ComplexTypeCode()) 40 | } 41 | rb, offset := d.readSize8(offset) 42 | ib, offset := d.readSize8(offset) 43 | r := math.Float64frombits(binary.BigEndian.Uint64(rb)) 44 | i := math.Float64frombits(binary.BigEndian.Uint64(ib)) 45 | return complex(r, i), offset, nil 46 | } 47 | 48 | return 0, 0, d.errorTemplate(code, "AsComplex128") 49 | } 50 | -------------------------------------------------------------------------------- /msgpack/dec/string.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/shamaton/msgpack/v2/def" 7 | ) 8 | 9 | var emptyString = "" 10 | var emptyBytes = []byte{} 11 | 12 | // AsString checks codes and returns the got bytes as string 13 | func (d *Decoder) AsString(offset int) (string, int, error) { 14 | bs, offset, err := d.AsStringBytes(offset) 15 | if err != nil { 16 | return emptyString, 0, err 17 | } 18 | return string(bs), offset, nil 19 | } 20 | 21 | func (d *Decoder) AsStringBytes(offset int) ([]byte, int, error) { 22 | l, offset, err := d.stringByteLength(offset) 23 | if err != nil { 24 | return emptyBytes, 0, err 25 | } 26 | bs, offset := d.asStringByte(offset, l) 27 | return bs, offset, nil 28 | } 29 | 30 | func (d *Decoder) stringByteLength(offset int) (int, int, error) { 31 | code := d.data[offset] 32 | offset++ 33 | 34 | if d.isFixString(code) { 35 | l := int(code - def.FixStr) 36 | return l, offset, nil 37 | } else if code == def.Str8 { 38 | b, offset := d.readSize1(offset) 39 | return int(b), offset, nil 40 | } else if code == def.Str16 { 41 | b, offset := d.readSize2(offset) 42 | return int(binary.BigEndian.Uint16(b)), offset, nil 43 | } else if code == def.Str32 { 44 | b, offset := d.readSize4(offset) 45 | return int(binary.BigEndian.Uint32(b)), offset, nil 46 | } else if code == def.Nil { 47 | return 0, offset, nil 48 | } 49 | return 0, 0, d.errorTemplate(code, "StringByteLength") 50 | } 51 | 52 | func (d *Decoder) isFixString(v byte) bool { 53 | return def.FixStr <= v && v <= def.FixStr+0x1f 54 | } 55 | 56 | func (d *Decoder) asStringByte(offset int, l int) ([]byte, int) { 57 | if l < 1 { 58 | return emptyBytes, offset 59 | } 60 | 61 | return d.readSizeN(offset, l) 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | GOPATH: ${{ github.workspace }} 7 | WORKSPACE: ${{ github.workspace }}/src/github.com/${{ github.repository }} 8 | 9 | jobs: 10 | 11 | test: 12 | defaults: 13 | run: 14 | working-directory: ${{ env.WORKSPACE }} 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest, macos-latest, windows-latest] 18 | go: [1.23, 1.24, 1.25] 19 | name: ${{ matrix.os }} @ Go ${{ matrix.go }} 20 | runs-on: ${{ matrix.os }} 21 | steps: 22 | - name: Set up Go ${{ matrix.go }} 23 | uses: actions/setup-go@v5 24 | with: 25 | go-version: ${{ matrix.go }} 26 | 27 | - name: Cache 28 | uses: actions/cache@v4 29 | with: 30 | path: ~/go/pkg/mod 31 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 32 | restore-keys: | 33 | ${{ runner.os }}-go- 34 | 35 | - name: Checkout 36 | uses: actions/checkout@v5 37 | with: 38 | path: ${{ env.WORKSPACE }} 39 | 40 | - name: Build 41 | run: go build -v ./... 42 | 43 | - name: Test 44 | run: sh testdata/test.sh 45 | 46 | - name: Upload coverage to Codecov 47 | if: success() && matrix.go == 1.25 && matrix.os == 'windows-latest' 48 | uses: codecov/codecov-action@v1 49 | with: 50 | token: 51 | fail_ci_if_error: false 52 | working-directory: ${{ env.WORKSPACE }} 53 | 54 | lint: 55 | needs: test 56 | runs-on: ubuntu-latest 57 | steps: 58 | - name: Checkout 59 | uses: actions/checkout@v5 60 | with: 61 | path: ${{ env.WORKSPACE }} 62 | - name: golangci-lint 63 | uses: golangci/golangci-lint-action@v8 64 | with: 65 | version: latest 66 | working-directory: ${{ env.WORKSPACE }} 67 | -------------------------------------------------------------------------------- /internal/generator/structure/code_time.go: -------------------------------------------------------------------------------- 1 | package structure 2 | 3 | import ( 4 | . "github.com/dave/jennifer/jen" 5 | "github.com/shamaton/msgpackgen/internal/generator/ptn" 6 | ) 7 | 8 | type timeCodeGen struct { 9 | } 10 | 11 | func (st *Structure) createTimeCode(encodeFieldName, decodeFieldName string, node *Node) (cArray []Code, cMap []Code, eArray []Code, eMap []Code, dArray []Code, dMap []Code) { 12 | g := timeCodeGen{} 13 | cArray = g.createCalcCode("CalcTime", Id(encodeFieldName)) 14 | cMap = g.createCalcCode("CalcTime", Id(encodeFieldName)) 15 | 16 | eArray = g.createEncCode("WriteTime", Id(encodeFieldName), Id("offset")) 17 | eMap = g.createEncCode("WriteTime", Id(encodeFieldName), Id("offset")) 18 | 19 | dArray = g.createDecCode(node, st.Others, decodeFieldName, "AsDateTime") 20 | dMap = g.createDecCode(node, st.Others, decodeFieldName, "AsDateTime") 21 | return 22 | } 23 | 24 | func (g timeCodeGen) createCalcCode(funcName string, params ...Code) []Code { 25 | return []Code{ 26 | createAddSizeCode(funcName, params...), 27 | } 28 | } 29 | 30 | func (g timeCodeGen) createEncCode(funcName string, params ...Code) []Code { 31 | 32 | return []Code{ 33 | Id("offset").Op("=").Id(ptn.IdEncoder).Dot(funcName).Call(params...), 34 | } 35 | } 36 | 37 | func (g timeCodeGen) createDecCode(node *Node, structures []*Structure, fieldName, funcName string) []Code { 38 | varName := fieldName + "v" 39 | if isRootField(fieldName) { 40 | varName = "vv" 41 | } 42 | 43 | _, isParentTypeArrayOrMap := node.GetPointerInfo() 44 | 45 | codes, receiverName := createDecodeDefineVarCode(node, structures, varName) 46 | 47 | codes = append(codes, 48 | List(Id(receiverName), Id("offset"), Err()).Op("=").Id(ptn.IdDecoder).Dot(funcName).Call(Id("offset")), 49 | If(Err().Op("!=").Nil()).Block( 50 | Return(Lit(0), Err()), 51 | ), 52 | ) 53 | 54 | codes = append(codes, createDecodeSetValueCode(node, varName, fieldName)...) 55 | 56 | // array or map 57 | if isParentTypeArrayOrMap { 58 | return codes 59 | } 60 | 61 | return []Code{Block(codes...)} 62 | } 63 | -------------------------------------------------------------------------------- /msgpack/dec/time.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/shamaton/msgpack/v2/def" 9 | ) 10 | 11 | // AsDateTime checks codes and returns the got bytes as time.Time 12 | func (d *Decoder) AsDateTime(offset int) (time.Time, int, error) { 13 | code, offset := d.readSize1(offset) 14 | 15 | switch code { 16 | case def.Fixext4: 17 | t, offset := d.readSize1(offset) 18 | if int8(t) != def.TimeStamp { 19 | return time.Time{}, 0, fmt.Errorf("fixext4. time type is different %d, %d", t, def.TimeStamp) 20 | } 21 | bs, offset := d.readSize4(offset) 22 | return time.Unix(int64(binary.BigEndian.Uint32(bs)), 0), offset, nil 23 | 24 | case def.Fixext8: 25 | t, offset := d.readSize1(offset) 26 | if int8(t) != def.TimeStamp { 27 | return time.Time{}, 0, fmt.Errorf("fixext8. time type is different %d, %d", t, def.TimeStamp) 28 | } 29 | bs, offset := d.readSize8(offset) 30 | data64 := binary.BigEndian.Uint64(bs) 31 | nano := int64(data64 >> 34) 32 | if nano > 999999999 { 33 | return time.Time{}, 0, fmt.Errorf("in timestamp 64 formats, nanoseconds must not be larger than 999999999 : %d", nano) 34 | } 35 | return time.Unix(int64(data64&0x00000003ffffffff), nano), offset, nil 36 | 37 | case def.Ext8: 38 | c, offset := d.readSize1(offset) 39 | if int8(c) != 12 { 40 | return time.Time{}, 0, fmt.Errorf("ext8. time ext length is different %d, %d", c, 12) 41 | } 42 | t, offset := d.readSize1(offset) 43 | if int8(t) != def.TimeStamp { 44 | return time.Time{}, 0, fmt.Errorf("ext8. time type is different %d, %d", t, def.TimeStamp) 45 | } 46 | nanobs, offset := d.readSize4(offset) 47 | secbs, offset := d.readSize8(offset) 48 | nano := binary.BigEndian.Uint32(nanobs) 49 | if nano > 999999999 { 50 | return time.Time{}, 0, fmt.Errorf("in timestamp 96 formats, nanoseconds must not be larger than 999999999 : %d", nano) 51 | } 52 | sec := binary.BigEndian.Uint64(secbs) 53 | return time.Unix(int64(sec), int64(nano)), offset, nil 54 | } 55 | 56 | return time.Time{}, 0, d.errorTemplate(code, "AsDateTime") 57 | } 58 | -------------------------------------------------------------------------------- /msgpack/dec/float.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import ( 4 | "encoding/binary" 5 | "math" 6 | 7 | "github.com/shamaton/msgpack/v2/def" 8 | ) 9 | 10 | // AsFloat32 checks codes and returns the got bytes as float32 11 | func (d *Decoder) AsFloat32(offset int) (float32, int, error) { 12 | code := d.data[offset] 13 | 14 | switch { 15 | case code == def.Float32: 16 | offset++ 17 | bs, offset := d.readSize4(offset) 18 | v := math.Float32frombits(binary.BigEndian.Uint32(bs)) 19 | return v, offset, nil 20 | 21 | case d.isPositiveFixNum(code), code == def.Uint8, code == def.Uint16, code == def.Uint32, code == def.Uint64: 22 | v, offset, _ := d.AsUint(offset) 23 | return float32(v), offset, nil 24 | 25 | case d.isNegativeFixNum(code), code == def.Int8, code == def.Int16, code == def.Int32, code == def.Int64: 26 | v, offset, _ := d.AsInt(offset) 27 | return float32(v), offset, nil 28 | 29 | case code == def.Nil: 30 | offset++ 31 | return 0, offset, nil 32 | } 33 | return 0, 0, d.errorTemplate(code, "AsFloat32") 34 | } 35 | 36 | // AsFloat64 checks codes and returns the got bytes as float64 37 | func (d *Decoder) AsFloat64(offset int) (float64, int, error) { 38 | code := d.data[offset] 39 | 40 | switch { 41 | case code == def.Float64: 42 | offset++ 43 | bs, offset := d.readSize8(offset) 44 | v := math.Float64frombits(binary.BigEndian.Uint64(bs)) 45 | return v, offset, nil 46 | 47 | case code == def.Float32: 48 | offset++ 49 | bs, offset := d.readSize4(offset) 50 | v := math.Float32frombits(binary.BigEndian.Uint32(bs)) 51 | return float64(v), offset, nil 52 | 53 | case d.isPositiveFixNum(code), code == def.Uint8, code == def.Uint16, code == def.Uint32, code == def.Uint64: 54 | v, offset, _ := d.AsUint(offset) 55 | return float64(v), offset, nil 56 | 57 | case d.isNegativeFixNum(code), code == def.Int8, code == def.Int16, code == def.Int32, code == def.Int64: 58 | v, offset, _ := d.AsInt(offset) 59 | return float64(v), offset, nil 60 | 61 | case code == def.Nil: 62 | offset++ 63 | return 0, offset, nil 64 | } 65 | return 0, 0, d.errorTemplate(code, "AsFloat64") 66 | } 67 | -------------------------------------------------------------------------------- /msgpack/enc/struct.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | import ( 4 | "github.com/shamaton/msgpack/v2/def" 5 | ) 6 | 7 | // CalcStructHeaderFix returns data size that need. 8 | func (e *Encoder) CalcStructHeaderFix(fieldNum int) int { 9 | return def.Byte1 10 | } 11 | 12 | // CalcStructHeader16 returns data size that need. 13 | func (e *Encoder) CalcStructHeader16(fieldNum int) int { 14 | return def.Byte1 + def.Byte2 15 | } 16 | 17 | // CalcStructHeader32 returns data size that need. 18 | func (e *Encoder) CalcStructHeader32(fieldNum int) int { 19 | return def.Byte1 + def.Byte4 20 | } 21 | 22 | // WriteStructHeaderFixAsArray sets num of fields to the buffer as array type. 23 | func (e *Encoder) WriteStructHeaderFixAsArray(fieldNum, offset int) int { 24 | offset = e.setByte1Int(def.FixArray+fieldNum, offset) 25 | return offset 26 | } 27 | 28 | // WriteStructHeader16AsArray sets num of fields to the buffer as array type. 29 | func (e *Encoder) WriteStructHeader16AsArray(fieldNum, offset int) int { 30 | offset = e.setByte1Int(def.Array16, offset) 31 | offset = e.setByte2Int(fieldNum, offset) 32 | return offset 33 | } 34 | 35 | // WriteStructHeader32AsArray sets num of fields to the buffer as array type. 36 | func (e *Encoder) WriteStructHeader32AsArray(fieldNum, offset int) int { 37 | offset = e.setByte1Int(def.Array32, offset) 38 | offset = e.setByte4Int(fieldNum, offset) 39 | return offset 40 | } 41 | 42 | // WriteStructHeaderFixAsMap sets num of fields to the buffer as map type. 43 | func (e *Encoder) WriteStructHeaderFixAsMap(fieldNum, offset int) int { 44 | offset = e.setByte1Int(def.FixMap+fieldNum, offset) 45 | return offset 46 | } 47 | 48 | // WriteStructHeader16AsMap sets num of fields to the buffer as map type. 49 | func (e *Encoder) WriteStructHeader16AsMap(fieldNum, offset int) int { 50 | offset = e.setByte1Int(def.Map16, offset) 51 | offset = e.setByte2Int(fieldNum, offset) 52 | return offset 53 | } 54 | 55 | // WriteStructHeader32AsMap sets num of fields to the buffer as map type. 56 | func (e *Encoder) WriteStructHeader32AsMap(fieldNum, offset int) int { 57 | offset = e.setByte1Int(def.Map32, offset) 58 | offset = e.setByte4Int(fieldNum, offset) 59 | return offset 60 | } 61 | -------------------------------------------------------------------------------- /internal/generator/structure/code_pointer.go: -------------------------------------------------------------------------------- 1 | package structure 2 | 3 | import ( 4 | . "github.com/dave/jennifer/jen" 5 | "github.com/shamaton/msgpackgen/internal/generator/ptn" 6 | ) 7 | 8 | type pointerCodeGen struct { 9 | } 10 | 11 | func (st *Structure) createPointerCode(node *Node, encodeFieldName, decodeFieldName string) (cArray []Code, cMap []Code, eArray []Code, eMap []Code, dArray []Code, dMap []Code) { 12 | 13 | encodeChildName := encodeFieldName + "p" 14 | if isRootField(encodeFieldName) { 15 | encodeChildName = "vp" 16 | } 17 | 18 | ca, cm, ea, em, da, dm := st.createFieldCode(node.Elm(), encodeChildName, decodeFieldName) 19 | 20 | g := pointerCodeGen{} 21 | cArray = g.createPointerCalcCode(encodeFieldName, encodeChildName, ca) 22 | cMap = g.createPointerCalcCode(encodeFieldName, encodeChildName, cm) 23 | 24 | eArray = g.createPointerEncCode(encodeFieldName, encodeChildName, ea) 25 | eMap = g.createPointerEncCode(encodeFieldName, encodeChildName, em) 26 | 27 | dArray = g.createPointerDecCode(node, da) 28 | dMap = g.createPointerDecCode(node, dm) 29 | 30 | return 31 | } 32 | 33 | func (g pointerCodeGen) createPointerCalcCode(encodeFieldName, encodeChildName string, elmCodes []Code) []Code { 34 | codes := make([]Code, 0) 35 | codes = append(codes, If(Id(encodeFieldName).Op("!=").Nil()).Block( 36 | append([]Code{ 37 | Id(encodeChildName).Op(":=").Op("*").Id(encodeFieldName), 38 | }, elmCodes...)..., 39 | ).Else().Block( 40 | Id("size").Op("+=").Id(ptn.IdEncoder).Dot("CalcNil").Call(), 41 | )) 42 | return codes 43 | } 44 | 45 | func (g pointerCodeGen) createPointerEncCode(encodeFieldName, encodeChildName string, elmCodes []Code) []Code { 46 | codes := make([]Code, 0) 47 | codes = append(codes, If(Id(encodeFieldName).Op("!=").Nil()).Block( 48 | append([]Code{ 49 | Id(encodeChildName).Op(":=").Op("*").Id(encodeFieldName), 50 | }, elmCodes...)..., 51 | ).Else().Block( 52 | Id("offset").Op("=").Id(ptn.IdEncoder).Dot("WriteNil").Call(Id("offset")), 53 | )) 54 | return codes 55 | } 56 | 57 | func (g pointerCodeGen) createPointerDecCode(node *Node, elmCodes []Code) []Code { 58 | var codes []Code 59 | if node.IsParentPointer() { 60 | codes = elmCodes 61 | } else { 62 | codes = make([]Code, 0) 63 | codes = append(codes, If(Op("!").Id(ptn.IdDecoder).Dot("IsCodeNil").Call(Id("offset"))).Block( 64 | elmCodes..., 65 | ).Else().Block( 66 | Id("offset").Op("++"), 67 | )) 68 | } 69 | return codes 70 | } 71 | -------------------------------------------------------------------------------- /msgpack/enc/set.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | func (e *Encoder) setByte1Int64(value int64, offset int) int { 4 | e.d[offset] = byte(value) 5 | return offset + 1 6 | } 7 | 8 | func (e *Encoder) setByte2Int64(value int64, offset int) int { 9 | e.d[offset+0] = byte(value >> 8) 10 | e.d[offset+1] = byte(value) 11 | return offset + 2 12 | } 13 | 14 | func (e *Encoder) setByte4Int64(value int64, offset int) int { 15 | e.d[offset+0] = byte(value >> 24) 16 | e.d[offset+1] = byte(value >> 16) 17 | e.d[offset+2] = byte(value >> 8) 18 | e.d[offset+3] = byte(value) 19 | return offset + 4 20 | } 21 | 22 | func (e *Encoder) setByte8Int64(value int64, offset int) int { 23 | e.d[offset] = byte(value >> 56) 24 | e.d[offset+1] = byte(value >> 48) 25 | e.d[offset+2] = byte(value >> 40) 26 | e.d[offset+3] = byte(value >> 32) 27 | e.d[offset+4] = byte(value >> 24) 28 | e.d[offset+5] = byte(value >> 16) 29 | e.d[offset+6] = byte(value >> 8) 30 | e.d[offset+7] = byte(value) 31 | return offset + 8 32 | } 33 | 34 | func (e *Encoder) setByte1Uint64(value uint64, offset int) int { 35 | e.d[offset] = byte(value) 36 | return offset + 1 37 | } 38 | 39 | func (e *Encoder) setByte2Uint64(value uint64, offset int) int { 40 | e.d[offset] = byte(value >> 8) 41 | e.d[offset+1] = byte(value) 42 | return offset + 2 43 | } 44 | 45 | func (e *Encoder) setByte4Uint64(value uint64, offset int) int { 46 | e.d[offset] = byte(value >> 24) 47 | e.d[offset+1] = byte(value >> 16) 48 | e.d[offset+2] = byte(value >> 8) 49 | e.d[offset+3] = byte(value) 50 | return offset + 4 51 | } 52 | 53 | func (e *Encoder) setByte8Uint64(value uint64, offset int) int { 54 | e.d[offset] = byte(value >> 56) 55 | e.d[offset+1] = byte(value >> 48) 56 | e.d[offset+2] = byte(value >> 40) 57 | e.d[offset+3] = byte(value >> 32) 58 | e.d[offset+4] = byte(value >> 24) 59 | e.d[offset+5] = byte(value >> 16) 60 | e.d[offset+6] = byte(value >> 8) 61 | e.d[offset+7] = byte(value) 62 | return offset + 8 63 | } 64 | 65 | func (e *Encoder) setByte1Int(code, offset int) int { 66 | e.d[offset] = byte(code) 67 | return offset + 1 68 | } 69 | 70 | func (e *Encoder) setByte2Int(value int, offset int) int { 71 | e.d[offset] = byte(value >> 8) 72 | e.d[offset+1] = byte(value) 73 | return offset + 2 74 | } 75 | 76 | func (e *Encoder) setByte4Int(value int, offset int) int { 77 | e.d[offset] = byte(value >> 24) 78 | e.d[offset+1] = byte(value >> 16) 79 | e.d[offset+2] = byte(value >> 8) 80 | e.d[offset+3] = byte(value) 81 | return offset + 4 82 | } 83 | 84 | func (e *Encoder) setByte(b byte, offset int) int { 85 | e.d[offset] = b 86 | return offset + 1 87 | } 88 | -------------------------------------------------------------------------------- /internal/generator/gen_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "runtime" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestSearchGoMod(t *testing.T) { 13 | 14 | g := generator{} 15 | _, err := g.searchGoModFile("dummy", true) 16 | if err == nil { 17 | t.Fatal("error should occur") 18 | } 19 | 20 | sub := "no such file or directory" 21 | if runtime.GOOS == "windows" { 22 | sub = "The system cannot find" 23 | } 24 | if !strings.Contains(err.Error(), sub) { 25 | t.Fatal("something wrong", err) 26 | } 27 | 28 | sep := fmt.Sprintf("%s..", string(filepath.Separator)) 29 | upper := filepath.Join(".", sep, sep, sep) 30 | _, err = g.searchGoModFile(upper, true) 31 | if err == nil { 32 | t.Fatal("error should occur") 33 | } 34 | 35 | if !strings.Contains(err.Error(), "not found go.mod") { 36 | t.Fatal("something wrong", err) 37 | } 38 | 39 | _, err = filepath.Abs("./fuga/hoge") 40 | if err != nil { 41 | t.Fatal(err) 42 | } 43 | } 44 | 45 | func TestSetModuleName(t *testing.T) { 46 | 47 | g := generator{} 48 | g.goModFilePath = "dummy" 49 | err := g.setModuleName() 50 | if err == nil { 51 | t.Fatal("error should occur") 52 | } 53 | 54 | sub := "no such file or directory" 55 | if runtime.GOOS == "windows" { 56 | sub = "The system cannot find" 57 | } 58 | if !strings.Contains(err.Error(), sub) { 59 | t.Fatal("something wrong", err) 60 | } 61 | 62 | g.goModFilePath = "gen.go" 63 | err = g.setModuleName() 64 | if err == nil { 65 | t.Fatal("error should occur") 66 | } 67 | if !strings.Contains(err.Error(), "not found module name in go.mod") { 68 | t.Fatal("something wrong", err) 69 | } 70 | } 71 | 72 | func TestOutput(t *testing.T) { 73 | g := generator{} 74 | 75 | err := g.output(nil, "") 76 | if err == nil { 77 | t.Fatal("error should occur") 78 | } 79 | 80 | sub := "no such file or directory" 81 | if runtime.GOOS == "windows" { 82 | sub = "The system cannot find" 83 | } 84 | if !strings.Contains(err.Error(), sub) { 85 | t.Fatal("something wrong", err) 86 | } 87 | 88 | err = os.MkdirAll("tmp/resolver.go", 0777) 89 | if err != nil { 90 | t.Fatal("unexpected error", err) 91 | } 92 | 93 | g.outputDir = "tmp" 94 | err = g.output(nil, "resolver.go") 95 | if err == nil { 96 | t.Error("error should occur") 97 | } 98 | if err != nil && !strings.Contains(err.Error(), "is a directory") { 99 | t.Error("something wrong", err) 100 | } 101 | 102 | err = os.RemoveAll("tmp/resolver.go") 103 | if err != nil { 104 | t.Fatal(err) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /msgpack/enc/string.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/shamaton/msgpack/v2/def" 7 | ) 8 | 9 | // CalcString check value and returns data size that need. 10 | func (e *Encoder) CalcString(v string) int { 11 | l := len(v) 12 | if l < 32 { 13 | return e.CalcStringFix(l) 14 | } else if l <= math.MaxUint8 { 15 | return e.CalcString8(l) 16 | } else if l <= math.MaxUint16 { 17 | return e.CalcString16(l) 18 | } 19 | return e.CalcString32(l) 20 | // NOTE : length over uint32 21 | } 22 | 23 | // CalcStringFix returns data size that need. 24 | func (e *Encoder) CalcStringFix(length int) int { 25 | return def.Byte1 + length 26 | } 27 | 28 | // CalcString8 returns data size that need. 29 | func (e *Encoder) CalcString8(length int) int { 30 | return def.Byte1 + def.Byte1 + length 31 | } 32 | 33 | // CalcString16 returns data size that need. 34 | func (e *Encoder) CalcString16(length int) int { 35 | return def.Byte1 + def.Byte2 + length 36 | } 37 | 38 | // CalcString32 returns data size that need. 39 | func (e *Encoder) CalcString32(length int) int { 40 | return def.Byte1 + def.Byte4 + length 41 | } 42 | 43 | // WriteString sets the contents of str to the buffer. 44 | func (e *Encoder) WriteString(str string, offset int) int { 45 | l := len(str) 46 | if l < 32 { 47 | return e.WriteStringFix(str, l, offset) 48 | } else if l <= math.MaxUint8 { 49 | return e.WriteString8(str, l, offset) 50 | } else if l <= math.MaxUint16 { 51 | return e.WriteString16(str, l, offset) 52 | } else { 53 | return e.WriteString32(str, l, offset) 54 | } 55 | } 56 | 57 | // WriteStringFix sets the contents of str to the buffer. 58 | func (e *Encoder) WriteStringFix(str string, length, offset int) int { 59 | offset = e.setByte1Int(def.FixStr+length, offset) 60 | offset += copy(e.d[offset:], str) 61 | return offset 62 | } 63 | 64 | // WriteString8 sets the contents of str to the buffer. 65 | func (e *Encoder) WriteString8(str string, length, offset int) int { 66 | offset = e.setByte1Int(def.Str8, offset) 67 | offset = e.setByte1Int(length, offset) 68 | offset += copy(e.d[offset:], str) 69 | return offset 70 | } 71 | 72 | // WriteString16 sets the contents of str to the buffer. 73 | func (e *Encoder) WriteString16(str string, length, offset int) int { 74 | offset = e.setByte1Int(def.Str16, offset) 75 | offset = e.setByte2Int(length, offset) 76 | offset += copy(e.d[offset:], str) 77 | return offset 78 | } 79 | 80 | // WriteString32 sets the contents of str to the buffer. 81 | func (e *Encoder) WriteString32(str string, length, offset int) int { 82 | offset = e.setByte1Int(def.Str32, offset) 83 | offset = e.setByte4Int(length, offset) 84 | offset += copy(e.d[offset:], str) 85 | return offset 86 | } 87 | -------------------------------------------------------------------------------- /internal/generator/structure/code_named.go: -------------------------------------------------------------------------------- 1 | package structure 2 | 3 | import ( 4 | "strings" 5 | 6 | . "github.com/dave/jennifer/jen" 7 | "github.com/shamaton/msgpackgen/internal/generator/ptn" 8 | ) 9 | 10 | type namedCodeGen struct { 11 | } 12 | 13 | func (st *Structure) createNamedCode(encodeFieldName, decodeFieldName string, ast *Node) (cArray []Code, cMap []Code, eArray []Code, eMap []Code, dArray []Code, dMap []Code) { 14 | 15 | sizeName := "size_" + encodeFieldName 16 | if isRootField(encodeFieldName) { 17 | sizeName = strings.ReplaceAll(sizeName, ".", "_") 18 | } 19 | 20 | g := namedCodeGen{} 21 | cArray = g.createCalcCode(ast, encodeFieldName, sizeName, "calcArraySize") 22 | cMap = g.createCalcCode(ast, encodeFieldName, sizeName, "calcMapSize") 23 | 24 | eArray = g.createEncCode(ast, encodeFieldName, "encodeArray") 25 | eMap = g.createEncCode(ast, encodeFieldName, "encodeMap") 26 | 27 | dArray = g.createDecCode(ast, st.Others, decodeFieldName, "decodeArray") 28 | dMap = g.createDecCode(ast, st.Others, decodeFieldName, "decodeMap") 29 | 30 | return 31 | } 32 | 33 | func (g namedCodeGen) createCalcCode(node *Node, fieldName, sizeName, funcName string) []Code { 34 | 35 | return []Code{ 36 | List(Id(sizeName), Err()). 37 | Op(":="). 38 | Id(createFuncName(funcName, node.StructName, node.ImportPath)).Call(Id(fieldName), Id(ptn.IdEncoder)), 39 | If(Err().Op("!=").Nil()).Block( 40 | Return(Lit(0), Err()), 41 | ), 42 | Id("size").Op("+=").Id(sizeName), 43 | } 44 | } 45 | 46 | func (g namedCodeGen) createEncCode(node *Node, fieldName, funcName string) []Code { 47 | 48 | return []Code{ 49 | List(Id("_"), Id("offset"), Err()). 50 | Op("="). 51 | Id(createFuncName(funcName, node.StructName, node.ImportPath)).Call(Id(fieldName), Id(ptn.IdEncoder), Id("offset")), 52 | If(Err().Op("!=").Nil()).Block( 53 | Return(Nil(), Lit(0), Err()), 54 | ), 55 | } 56 | } 57 | 58 | func (g namedCodeGen) createDecCode(node *Node, structures []*Structure, fieldName, funcName string) []Code { 59 | 60 | varName := fieldName + "v" 61 | if isRootField(fieldName) { 62 | varName = "vv" 63 | } 64 | 65 | _, isParentTypeArrayOrMap := node.GetPointerInfo() 66 | 67 | codes, receiverName := createDecodeDefineVarCode(node, structures, varName) 68 | 69 | codes = append(codes, 70 | List(Id("offset"), Err()).Op("=").Id( 71 | createFuncName(funcName, node.StructName, node.ImportPath)).Call(Op("&").Id(receiverName), 72 | Id(ptn.IdDecoder), Id("offset")), 73 | If(Err().Op("!=").Nil()).Block( 74 | Return(Lit(0), Err()), 75 | ), 76 | ) 77 | 78 | codes = append(codes, createDecodeSetValueCode(node, varName, fieldName)...) 79 | 80 | // array or map 81 | if isParentTypeArrayOrMap { 82 | return codes 83 | } 84 | 85 | return []Code{Block(codes...)} 86 | } 87 | -------------------------------------------------------------------------------- /internal/generator/structure/code_ident.go: -------------------------------------------------------------------------------- 1 | package structure 2 | 3 | import ( 4 | "strings" 5 | "unicode" 6 | 7 | . "github.com/dave/jennifer/jen" 8 | "github.com/shamaton/msgpackgen/internal/generator/ptn" 9 | ) 10 | 11 | type identCodeGen struct { 12 | } 13 | 14 | func (st *Structure) createIdentCode(node *Node, encodeFieldName, decodeFieldName string) (cArray []Code, cMap []Code, eArray []Code, eMap []Code, dArray []Code, dMap []Code) { 15 | 16 | g := identCodeGen{} 17 | 18 | funcSuffix := g.toPascalCase(node.IdenticalName) 19 | 20 | cArray = g.createCalcCode("Calc"+funcSuffix, Id(encodeFieldName)) 21 | cMap = g.createCalcCode("Calc"+funcSuffix, Id(encodeFieldName)) 22 | 23 | eArray = g.createEncCode("Write"+funcSuffix, Id(encodeFieldName), Id("offset")) 24 | eMap = g.createEncCode("Write"+funcSuffix, Id(encodeFieldName), Id("offset")) 25 | 26 | dArray = g.createDecCode(node, st.Others, decodeFieldName, "As"+funcSuffix) 27 | dMap = g.createDecCode(node, st.Others, decodeFieldName, "As"+funcSuffix) 28 | 29 | return 30 | } 31 | 32 | func (g identCodeGen) createCalcCode(funcName string, params ...Code) []Code { 33 | return []Code{ 34 | createAddSizeCode(funcName, params...), 35 | } 36 | } 37 | 38 | func (g identCodeGen) createEncCode(funcName string, params ...Code) []Code { 39 | return []Code{ 40 | Id("offset").Op("=").Id(ptn.IdEncoder).Dot(funcName).Call(params...), 41 | } 42 | } 43 | 44 | func (g identCodeGen) createDecCode(node *Node, structures []*Structure, fieldName, funcName string) []Code { 45 | 46 | varName := fieldName + "v" 47 | if isRootField(fieldName) { 48 | varName = "vv" 49 | } 50 | 51 | _, isParentTypeArrayOrMap := node.GetPointerInfo() 52 | 53 | codes, receiverName := createDecodeDefineVarCode(node, structures, varName) 54 | 55 | codes = append(codes, 56 | List(Id(receiverName), Id("offset"), Err()).Op("=").Id(ptn.IdDecoder).Dot(funcName).Call(Id("offset")), 57 | If(Err().Op("!=").Nil()).Block( 58 | Return(Lit(0), Err()), 59 | ), 60 | ) 61 | 62 | codes = append(codes, createDecodeSetValueCode(node, varName, fieldName)...) 63 | 64 | // array or map 65 | if isParentTypeArrayOrMap { 66 | return codes 67 | } 68 | 69 | return []Code{Block(codes...)} 70 | } 71 | 72 | func (g identCodeGen) toPascalCase(s string) string { 73 | parts := strings.FieldsFunc(s, func(r rune) bool { 74 | // character that is not a letter or digit 75 | return !unicode.IsLetter(r) && !unicode.IsDigit(r) 76 | }) 77 | 78 | if len(parts) == 0 { 79 | return "" 80 | } 81 | 82 | result := "" 83 | for _, p := range parts { 84 | if p == "" { 85 | continue 86 | } 87 | // Convert first character to uppercase and the rest to lowercase 88 | result += strings.ToUpper(string(p[0])) + strings.ToLower(p[1:]) 89 | } 90 | 91 | return result 92 | } 93 | -------------------------------------------------------------------------------- /msgpack/enc/uint.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/shamaton/msgpack/v2/def" 7 | ) 8 | 9 | //func (e *Encoder) isPositiveFixUint64(v uint64) bool { 10 | // return def.PositiveFixIntMin <= v && v <= def.PositiveFixIntMax 11 | //} 12 | 13 | // CalcUint check value and returns data size that need. 14 | func (e *Encoder) CalcUint(v uint) int { 15 | return e.calcUint(uint64(v)) 16 | } 17 | 18 | // CalcUint8 check value and returns data size that need. 19 | func (e *Encoder) CalcUint8(v uint8) int { 20 | return e.calcUint(uint64(v)) 21 | } 22 | 23 | // CalcUint16 check value and returns data size that need. 24 | func (e *Encoder) CalcUint16(v uint16) int { 25 | return e.calcUint(uint64(v)) 26 | } 27 | 28 | // CalcUint32 check value and returns data size that need. 29 | func (e *Encoder) CalcUint32(v uint32) int { 30 | return e.calcUint(uint64(v)) 31 | } 32 | 33 | // CalcUint64 check value and returns data size that need. 34 | func (e *Encoder) CalcUint64(v uint64) int { 35 | return e.calcUint(v) 36 | } 37 | 38 | func (e *Encoder) calcUint(v uint64) int { 39 | if v <= math.MaxInt8 { 40 | // format code only 41 | return def.Byte1 42 | } else if v <= math.MaxUint8 { 43 | return def.Byte1 + def.Byte1 44 | } else if v <= math.MaxUint16 { 45 | return def.Byte1 + def.Byte2 46 | } else if v <= math.MaxUint32 { 47 | return def.Byte1 + def.Byte4 48 | } 49 | return def.Byte1 + def.Byte8 50 | } 51 | 52 | // WriteUint sets the contents of v to the buffer. 53 | func (e *Encoder) WriteUint(v uint, offset int) int { 54 | return e.writeUint(uint64(v), offset) 55 | } 56 | 57 | // WriteUint8 sets the contents of v to the buffer. 58 | func (e *Encoder) WriteUint8(v uint8, offset int) int { 59 | return e.writeUint(uint64(v), offset) 60 | } 61 | 62 | // WriteUint16 sets the contents of v to the buffer. 63 | func (e *Encoder) WriteUint16(v uint16, offset int) int { 64 | return e.writeUint(uint64(v), offset) 65 | } 66 | 67 | // WriteUint32 sets the contents of v to the buffer. 68 | func (e *Encoder) WriteUint32(v uint32, offset int) int { 69 | return e.writeUint(uint64(v), offset) 70 | } 71 | 72 | // WriteUint64 sets the contents of v to the buffer. 73 | func (e *Encoder) WriteUint64(v uint64, offset int) int { 74 | return e.writeUint(v, offset) 75 | } 76 | 77 | func (e *Encoder) writeUint(v uint64, offset int) int { 78 | if v <= math.MaxInt8 { 79 | offset = e.setByte1Uint64(v, offset) 80 | } else if v <= math.MaxUint8 { 81 | offset = e.setByte1Int(def.Uint8, offset) 82 | offset = e.setByte1Uint64(v, offset) 83 | } else if v <= math.MaxUint16 { 84 | offset = e.setByte1Int(def.Uint16, offset) 85 | offset = e.setByte2Uint64(v, offset) 86 | } else if v <= math.MaxUint32 { 87 | offset = e.setByte1Int(def.Uint32, offset) 88 | offset = e.setByte4Uint64(v, offset) 89 | } else { 90 | offset = e.setByte1Int(def.Uint64, offset) 91 | offset = e.setByte8Uint64(v, offset) 92 | } 93 | return offset 94 | } 95 | -------------------------------------------------------------------------------- /msgpack/dec/uint.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/shamaton/msgpack/v2/def" 7 | ) 8 | 9 | // AsUint checks codes and returns the got bytes as uint 10 | func (d *Decoder) AsUint(offset int) (uint, int, error) { 11 | v, offset, err := d.asUint(offset) 12 | return uint(v), offset, err 13 | } 14 | 15 | // AsUint8 checks codes and returns the got bytes as uint8 16 | func (d *Decoder) AsUint8(offset int) (uint8, int, error) { 17 | v, offset, err := d.asUint(offset) 18 | return uint8(v), offset, err 19 | } 20 | 21 | // AsUint16 checks codes and returns the got bytes as uint16 22 | func (d *Decoder) AsUint16(offset int) (uint16, int, error) { 23 | v, offset, err := d.asUint(offset) 24 | return uint16(v), offset, err 25 | } 26 | 27 | // AsUint32 checks codes and returns the got bytes as uint32 28 | func (d *Decoder) AsUint32(offset int) (uint32, int, error) { 29 | v, offset, err := d.asUint(offset) 30 | return uint32(v), offset, err 31 | } 32 | 33 | // AsUint64 checks codes and returns the got bytes as uint64 34 | func (d *Decoder) AsUint64(offset int) (uint64, int, error) { 35 | return d.asUint(offset) 36 | } 37 | 38 | func (d *Decoder) asUint(offset int) (uint64, int, error) { 39 | 40 | code := d.data[offset] 41 | 42 | switch { 43 | case d.isPositiveFixNum(code): 44 | b, offset := d.readSize1(offset) 45 | return uint64(b), offset, nil 46 | 47 | case d.isNegativeFixNum(code): 48 | b, offset := d.readSize1(offset) 49 | return uint64(int8(b)), offset, nil 50 | 51 | case code == def.Uint8: 52 | offset++ 53 | b, offset := d.readSize1(offset) 54 | return uint64(uint8(b)), offset, nil 55 | 56 | case code == def.Int8: 57 | offset++ 58 | b, offset := d.readSize1(offset) 59 | return uint64(int8(b)), offset, nil 60 | 61 | case code == def.Uint16: 62 | offset++ 63 | bs, offset := d.readSize2(offset) 64 | v := binary.BigEndian.Uint16(bs) 65 | return uint64(v), offset, nil 66 | 67 | case code == def.Int16: 68 | offset++ 69 | bs, offset := d.readSize2(offset) 70 | v := int16(binary.BigEndian.Uint16(bs)) 71 | return uint64(v), offset, nil 72 | 73 | case code == def.Uint32: 74 | offset++ 75 | bs, offset := d.readSize4(offset) 76 | v := binary.BigEndian.Uint32(bs) 77 | return uint64(v), offset, nil 78 | 79 | case code == def.Int32: 80 | offset++ 81 | bs, offset := d.readSize4(offset) 82 | v := int32(binary.BigEndian.Uint32(bs)) 83 | return uint64(v), offset, nil 84 | 85 | case code == def.Uint64: 86 | offset++ 87 | bs, offset := d.readSize8(offset) 88 | return binary.BigEndian.Uint64(bs), offset, nil 89 | 90 | case code == def.Int64: 91 | offset++ 92 | bs, offset := d.readSize8(offset) 93 | return binary.BigEndian.Uint64(bs), offset, nil 94 | 95 | case code == def.Nil: 96 | offset++ 97 | return 0, offset, nil 98 | } 99 | 100 | return 0, 0, d.errorTemplate(code, "AsUint") 101 | } 102 | -------------------------------------------------------------------------------- /msgpack/enc/int.go: -------------------------------------------------------------------------------- 1 | package enc 2 | 3 | import ( 4 | "math" 5 | 6 | "github.com/shamaton/msgpack/v2/def" 7 | ) 8 | 9 | //func (e *Encoder) isPositiveFixInt64(v int64) bool { 10 | // return def.PositiveFixIntMin <= v && v <= def.PositiveFixIntMax 11 | //} 12 | 13 | func (e *Encoder) isNegativeFixInt64(v int64) bool { 14 | return def.NegativeFixintMin <= v && v <= def.NegativeFixintMax 15 | } 16 | 17 | // CalcInt checks value and returns data size that need. 18 | func (e *Encoder) CalcInt(v int) int { 19 | return e.calcInt(int64(v)) 20 | } 21 | 22 | // CalcInt8 checks value and returns data size that need. 23 | func (e *Encoder) CalcInt8(v int8) int { 24 | return e.calcInt(int64(v)) 25 | } 26 | 27 | // CalcInt16 checks value and returns data size that need. 28 | func (e *Encoder) CalcInt16(v int16) int { 29 | return e.calcInt(int64(v)) 30 | } 31 | 32 | // CalcInt32 checks value and returns data size that need. 33 | func (e *Encoder) CalcInt32(v int32) int { 34 | return e.calcInt(int64(v)) 35 | } 36 | 37 | // CalcInt64 checks value and returns data size that need. 38 | func (e *Encoder) CalcInt64(v int64) int { 39 | return e.calcInt(v) 40 | } 41 | 42 | func (e *Encoder) calcInt(v int64) int { 43 | if v >= 0 { 44 | return e.calcUint(uint64(v)) 45 | } else if e.isNegativeFixInt64(v) { 46 | // format code only 47 | return def.Byte1 48 | } else if v >= math.MinInt8 { 49 | return def.Byte1 + def.Byte1 50 | } else if v >= math.MinInt16 { 51 | return def.Byte1 + def.Byte2 52 | } else if v >= math.MinInt32 { 53 | return def.Byte1 + def.Byte4 54 | } 55 | return def.Byte1 + def.Byte8 56 | } 57 | 58 | // WriteInt sets the contents of v to the buffer. 59 | func (e *Encoder) WriteInt(v int, offset int) int { 60 | return e.writeInt(int64(v), offset) 61 | } 62 | 63 | // WriteInt8 sets the contents of v to the buffer. 64 | func (e *Encoder) WriteInt8(v int8, offset int) int { 65 | return e.writeInt(int64(v), offset) 66 | } 67 | 68 | // WriteInt16 sets the contents of v to the buffer. 69 | func (e *Encoder) WriteInt16(v int16, offset int) int { 70 | return e.writeInt(int64(v), offset) 71 | } 72 | 73 | // WriteInt32 sets the contents of v to the buffer. 74 | func (e *Encoder) WriteInt32(v int32, offset int) int { 75 | return e.writeInt(int64(v), offset) 76 | } 77 | 78 | // WriteInt64 sets the contents of v to the buffer. 79 | func (e *Encoder) WriteInt64(v int64, offset int) int { 80 | return e.writeInt(v, offset) 81 | } 82 | 83 | func (e *Encoder) writeInt(v int64, offset int) int { 84 | if v >= 0 { 85 | offset = e.writeUint(uint64(v), offset) 86 | } else if e.isNegativeFixInt64(v) { 87 | offset = e.setByte1Int64(v, offset) 88 | } else if v >= math.MinInt8 { 89 | offset = e.setByte1Int(def.Int8, offset) 90 | offset = e.setByte1Int64(v, offset) 91 | } else if v >= math.MinInt16 { 92 | offset = e.setByte1Int(def.Int16, offset) 93 | offset = e.setByte2Int64(v, offset) 94 | } else if v >= math.MinInt32 { 95 | offset = e.setByte1Int(def.Int32, offset) 96 | offset = e.setByte4Int64(v, offset) 97 | } else { 98 | offset = e.setByte1Int(def.Int64, offset) 99 | offset = e.setByte8Int64(v, offset) 100 | } 101 | return offset 102 | } 103 | -------------------------------------------------------------------------------- /internal/generator/structure/pattern.go: -------------------------------------------------------------------------------- 1 | package structure 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "strings" 7 | 8 | . "github.com/dave/jennifer/jen" 9 | "github.com/shamaton/msgpackgen/internal/generator/ptn" 10 | ) 11 | 12 | func isRootField(name string) bool { 13 | return strings.Contains(name, ".") 14 | } 15 | 16 | func isRecursiveChildArraySliceMap(node *Node) bool { 17 | n := node.Elm() 18 | if node.IsMap() { 19 | _, n = node.KeyValue() 20 | } 21 | 22 | for n != nil { 23 | if n.IsIdentical() || n.IsStruct() { 24 | return false 25 | } else if n.IsPointer() { 26 | n = n.Elm() 27 | } else { 28 | return true 29 | } 30 | } 31 | panic("unreachable code") 32 | } 33 | 34 | func createFuncName(prefix, name, importPath string) string { 35 | suffix := fmt.Sprintf("%x", sha256.Sum256([]byte(importPath))) 36 | return ptn.PrivateFuncName(fmt.Sprintf("%s%s_%s", prefix, name, suffix)) 37 | } 38 | 39 | func createAddSizeCode(funcName string, params ...Code) Code { 40 | return Id("size").Op("+=").Id(ptn.IdEncoder).Dot(funcName).Call(params...) 41 | } 42 | 43 | func createAddSizeErrCheckCode(funcName string, params ...Code) []Code { 44 | return []Code{ 45 | List(Id("s"), Err()).Op(":=").Id(ptn.IdEncoder).Dot(funcName).Call(params...), 46 | If(Err().Op("!=").Nil()).Block( 47 | Return(Lit(0), Err()), 48 | ), 49 | Id("size").Op("+=").Id("s"), 50 | } 51 | 52 | } 53 | 54 | func createDecodeDefineVarCode(node *Node, structures []*Structure, varName string) ([]Code, string) { 55 | 56 | ptrCount, isParentTypeArrayOrMap := node.GetPointerInfo() 57 | 58 | codes := make([]Code, 0) 59 | receiverName := varName 60 | 61 | if ptrCount < 1 && !isParentTypeArrayOrMap { 62 | codes = append(codes, node.TypeJenChain(structures, Var().Id(receiverName))) 63 | } else if isParentTypeArrayOrMap { 64 | 65 | for i := 0; i < ptrCount; i++ { 66 | p := strings.Repeat("p", i+1) 67 | ptr := strings.Repeat("*", ptrCount-1-i) 68 | 69 | codes = append(codes, node.TypeJenChain(structures, Var().Id(varName+p).Op(ptr))) 70 | } 71 | receiverName = varName + strings.Repeat("p", ptrCount) 72 | } else { 73 | for i := 0; i < ptrCount; i++ { 74 | p := strings.Repeat("p", i) 75 | ptr := strings.Repeat("*", ptrCount-1-i) 76 | 77 | codes = append(codes, node.TypeJenChain(structures, Var().Id(varName+p).Op(ptr))) 78 | } 79 | receiverName = varName + strings.Repeat("p", ptrCount-1) 80 | } 81 | return codes, receiverName 82 | } 83 | 84 | func createDecodeSetValueCode(node *Node, varName, fieldName string) []Code { 85 | 86 | ptrCount, isParentTypeArrayOrMap := node.GetPointerInfo() 87 | 88 | var codes []Code 89 | if isParentTypeArrayOrMap { 90 | for i := 0; i < ptrCount; i++ { 91 | tmp1 := varName + strings.Repeat("p", ptrCount-1-i) 92 | tmp2 := varName + strings.Repeat("p", ptrCount-i) 93 | codes = append(codes, Id(tmp1).Op("=").Op("&").Id(tmp2)) 94 | } 95 | } else { 96 | 97 | for i := 0; i < ptrCount; i++ { 98 | if i != ptrCount-1 { 99 | tmp1 := varName + strings.Repeat("p", ptrCount-2-i) 100 | tmp2 := varName + strings.Repeat("p", ptrCount-1-i) 101 | codes = append(codes, Id(tmp1).Op("=").Op("&").Id(tmp2)) 102 | } else { 103 | // last 104 | tmp := varName + strings.Repeat("p", 0) 105 | codes = append(codes, Id(fieldName).Op("=").Op("&").Id(tmp)) 106 | } 107 | } 108 | if ptrCount < 1 { 109 | codes = append(codes, Id(fieldName).Op("=").Op("").Id(varName)) 110 | } 111 | } 112 | 113 | return codes 114 | } 115 | -------------------------------------------------------------------------------- /msgpack/dec/int.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/shamaton/msgpack/v2/def" 7 | ) 8 | 9 | // AsInt checks codes and returns the got bytes as int 10 | func (d *Decoder) AsInt(offset int) (int, int, error) { 11 | v, offset, err := d.asInt(offset) 12 | return int(v), offset, err 13 | } 14 | 15 | // AsInt8 checks codes and returns the got bytes as int8 16 | func (d *Decoder) AsInt8(offset int) (int8, int, error) { 17 | v, offset, err := d.asInt(offset) 18 | return int8(v), offset, err 19 | } 20 | 21 | // AsInt16 checks codes and returns the got bytes as int16 22 | func (d *Decoder) AsInt16(offset int) (int16, int, error) { 23 | v, offset, err := d.asInt(offset) 24 | return int16(v), offset, err 25 | } 26 | 27 | // AsInt32 checks codes and returns the got bytes as int32 28 | func (d *Decoder) AsInt32(offset int) (int32, int, error) { 29 | v, offset, err := d.asInt(offset) 30 | return int32(v), offset, err 31 | } 32 | 33 | // AsInt64 checks codes and returns the got bytes as int64 34 | func (d *Decoder) AsInt64(offset int) (int64, int, error) { 35 | return d.asInt(offset) 36 | } 37 | 38 | func (d *Decoder) asInt(offset int) (int64, int, error) { 39 | 40 | code := d.data[offset] 41 | 42 | switch { 43 | case d.isPositiveFixNum(code): 44 | b, offset := d.readSize1(offset) 45 | return int64(b), offset, nil 46 | 47 | case d.isNegativeFixNum(code): 48 | b, offset := d.readSize1(offset) 49 | return int64(int8(b)), offset, nil 50 | 51 | case code == def.Uint8: 52 | offset++ 53 | b, offset := d.readSize1(offset) 54 | return int64(uint8(b)), offset, nil 55 | 56 | case code == def.Int8: 57 | offset++ 58 | b, offset := d.readSize1(offset) 59 | return int64(int8(b)), offset, nil 60 | 61 | case code == def.Uint16: 62 | offset++ 63 | bs, offset := d.readSize2(offset) 64 | v := binary.BigEndian.Uint16(bs) 65 | return int64(v), offset, nil 66 | 67 | case code == def.Int16: 68 | offset++ 69 | bs, offset := d.readSize2(offset) 70 | v := int16(binary.BigEndian.Uint16(bs)) 71 | return int64(v), offset, nil 72 | 73 | case code == def.Uint32: 74 | offset++ 75 | bs, offset := d.readSize4(offset) 76 | v := binary.BigEndian.Uint32(bs) 77 | return int64(v), offset, nil 78 | 79 | case code == def.Int32: 80 | offset++ 81 | bs, offset := d.readSize4(offset) 82 | v := int32(binary.BigEndian.Uint32(bs)) 83 | return int64(v), offset, nil 84 | 85 | case code == def.Uint64: 86 | offset++ 87 | bs, offset := d.readSize8(offset) 88 | return int64(binary.BigEndian.Uint64(bs)), offset, nil 89 | 90 | case code == def.Int64: 91 | offset++ 92 | bs, offset := d.readSize8(offset) 93 | return int64(binary.BigEndian.Uint64(bs)), offset, nil 94 | 95 | case code == def.Float32: 96 | v, offset, err := d.AsFloat32(offset) 97 | if err != nil { 98 | return 0, 0, err 99 | } 100 | return int64(v), offset, nil 101 | 102 | case code == def.Float64: 103 | v, offset, err := d.AsFloat64(offset) 104 | if err != nil { 105 | return 0, 0, err 106 | } 107 | return int64(v), offset, nil 108 | 109 | case code == def.Nil: 110 | offset++ 111 | return 0, offset, nil 112 | } 113 | 114 | return 0, 0, d.errorTemplate(code, "AsInt") 115 | } 116 | 117 | func (d *Decoder) isPositiveFixNum(v byte) bool { 118 | return def.PositiveFixIntMin <= v && v <= def.PositiveFixIntMax 119 | } 120 | 121 | func (d *Decoder) isNegativeFixNum(v byte) bool { 122 | return def.NegativeFixintMin <= int8(v) && int8(v) <= def.NegativeFixintMax 123 | } 124 | -------------------------------------------------------------------------------- /msgpack/dec/struct.go: -------------------------------------------------------------------------------- 1 | package dec 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | 7 | "github.com/shamaton/msgpack/v2/def" 8 | ) 9 | 10 | // CheckStructHeader checks if fieldNum matches the number of fields on the data. 11 | func (d *Decoder) CheckStructHeader(fieldNum, offset int) (int, error) { 12 | code, offset := d.readSize1(offset) 13 | var l int 14 | switch { 15 | case d.isFixSlice(code): 16 | l = int(code - def.FixArray) 17 | 18 | case code == def.Array16: 19 | bs, o := d.readSize2(offset) 20 | l = int(binary.BigEndian.Uint16(bs)) 21 | offset = o 22 | case code == def.Array32: 23 | bs, o := d.readSize4(offset) 24 | l = int(binary.BigEndian.Uint32(bs)) 25 | offset = o 26 | 27 | case d.isFixMap(code): 28 | l = int(code - def.FixMap) 29 | case code == def.Map16: 30 | bs, o := d.readSize2(offset) 31 | l = int(binary.BigEndian.Uint16(bs)) 32 | offset = o 33 | case code == def.Map32: 34 | bs, o := d.readSize4(offset) 35 | l = int(binary.BigEndian.Uint32(bs)) 36 | offset = o 37 | } 38 | 39 | if fieldNum != l { 40 | return 0, fmt.Errorf("data length wrong %d : %d", fieldNum, l) 41 | } 42 | return offset, nil 43 | } 44 | 45 | //func (d *Decoder) JumpOffset(offset int) int { 46 | // code, offset := d.readSize1(offset) 47 | // switch { 48 | // case code == def.True, code == def.False, code == def.Nil: 49 | // // do nothing 50 | // 51 | // case d.isPositiveFixNum(code) || d.isNegativeFixNum(code): 52 | // // do nothing 53 | // case code == def.Uint8, code == def.Int8: 54 | // offset += def.Byte1 55 | // case code == def.Uint16, code == def.Int16: 56 | // offset += def.Byte2 57 | // case code == def.Uint32, code == def.Int32, code == def.Float32: 58 | // offset += def.Byte4 59 | // case code == def.Uint64, code == def.Int64, code == def.Float64: 60 | // offset += def.Byte8 61 | // 62 | // case d.isFixString(code): 63 | // offset += int(code - def.FixStr) 64 | // case code == def.Str8, code == def.Bin8: 65 | // b, o := d.readSize1(offset) 66 | // o += int(b) 67 | // offset = o 68 | // case code == def.Str16, code == def.Bin16: 69 | // bs, o := d.readSize2(offset) 70 | // o += int(binary.BigEndian.Uint16(bs)) 71 | // offset = o 72 | // case code == def.Str32, code == def.Bin32: 73 | // bs, o := d.readSize4(offset) 74 | // o += int(binary.BigEndian.Uint32(bs)) 75 | // offset = o 76 | // 77 | // case d.isFixSlice(code): 78 | // l := int(code - def.FixArray) 79 | // for i := 0; i < l; i++ { 80 | // offset = d.JumpOffset(offset) 81 | // } 82 | // case code == def.Array16: 83 | // bs, o := d.readSize2(offset) 84 | // l := int(binary.BigEndian.Uint16(bs)) 85 | // for i := 0; i < l; i++ { 86 | // o = d.JumpOffset(o) 87 | // } 88 | // offset = o 89 | // case code == def.Array32: 90 | // bs, o := d.readSize4(offset) 91 | // l := int(binary.BigEndian.Uint32(bs)) 92 | // for i := 0; i < l; i++ { 93 | // o = d.JumpOffset(o) 94 | // } 95 | // offset = o 96 | // 97 | // case d.isFixMap(code): 98 | // l := int(code - def.FixMap) 99 | // for i := 0; i < l*2; i++ { 100 | // offset = d.JumpOffset(offset) 101 | // } 102 | // case code == def.Map16: 103 | // bs, o := d.readSize2(offset) 104 | // l := int(binary.BigEndian.Uint16(bs)) 105 | // for i := 0; i < l*2; i++ { 106 | // o = d.JumpOffset(o) 107 | // } 108 | // offset = o 109 | // case code == def.Map32: 110 | // bs, o := d.readSize4(offset) 111 | // l := int(binary.BigEndian.Uint32(bs)) 112 | // for i := 0; i < l*2; i++ { 113 | // o = d.JumpOffset(o) 114 | // } 115 | // offset = o 116 | // 117 | // case code == def.Fixext1: 118 | // offset += def.Byte1 + def.Byte1 119 | // case code == def.Fixext2: 120 | // offset += def.Byte1 + def.Byte2 121 | // case code == def.Fixext4: 122 | // offset += def.Byte1 + def.Byte4 123 | // case code == def.Fixext8: 124 | // offset += def.Byte1 + def.Byte8 125 | // case code == def.Fixext16: 126 | // offset += def.Byte1 + def.Byte16 127 | // 128 | // case code == def.Ext8: 129 | // b, o := d.readSize1(offset) 130 | // o += def.Byte1 + int(b) 131 | // offset = o 132 | // case code == def.Ext16: 133 | // bs, o := d.readSize2(offset) 134 | // o += def.Byte1 + int(binary.BigEndian.Uint16(bs)) 135 | // offset = o 136 | // case code == def.Ext32: 137 | // bs, o := d.readSize4(offset) 138 | // o += def.Byte1 + int(binary.BigEndian.Uint32(bs)) 139 | // offset = o 140 | // 141 | // } 142 | // return offset 143 | //} 144 | -------------------------------------------------------------------------------- /def_a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "time" 6 | 7 | define2 "github.com/shamaton/msgpackgen/internal/fortest/define" 8 | . "github.com/shamaton/msgpackgen/internal/fortest/define/define" 9 | ) 10 | 11 | //go:generate go run github.com/shamaton/msgpackgen -output-file resolver_test.go -pointer 2 -strict -v 12 | 13 | type testingValue struct { 14 | Int int 15 | Int8 int8 16 | Int16 int16 17 | Int32 int32 18 | Int64 int64 19 | Uint uint 20 | Uint8 uint8 21 | Uint16 uint16 22 | Uint32 uint32 23 | Uint64 uint64 24 | Float32 float32 25 | Float64 float64 26 | String string 27 | Bool bool 28 | Byte byte 29 | Rune rune 30 | Complex64 complex64 31 | Complex128 complex128 32 | 33 | Slice []int8 34 | Bytes []byte 35 | 36 | DoubleSlice [][]int16 37 | DoubleArray [3][4]int16 38 | 39 | TripleBytes [][][]byte 40 | 41 | MapIntInt map[string]int 42 | 43 | Pint *int 44 | P2string **string 45 | P3float32 ***float32 46 | 47 | IntPointers []*int 48 | MapPointers map[*uint]**string 49 | 50 | P2IntSlice **[]int 51 | P2MapStringInt **map[string]int 52 | P2IntArray **[1]int 53 | 54 | DoubleSlicePointerMap [][]**map[string]int 55 | MapDoubleSlicePointerInt map[string][][]**int 56 | 57 | Abcdefghijabcdefghijabcdefghijabcdefghij int 58 | 59 | AbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghij int 60 | } 61 | 62 | func (v testingValue) Function() int { 63 | type notGenerated9 struct { 64 | Int int 65 | } 66 | ng := notGenerated9{} 67 | return v.Int + v.Int + ng.Int 68 | } 69 | 70 | type testingInt struct { 71 | I int 72 | } 73 | 74 | type testingUint struct { 75 | U uint 76 | } 77 | 78 | type testingFloat32 struct { 79 | F float32 80 | } 81 | 82 | type testingFloat64 struct { 83 | F float64 84 | } 85 | 86 | type testingString struct { 87 | S string 88 | } 89 | 90 | type testingBool struct { 91 | B bool 92 | } 93 | 94 | type testingComplex64 struct { 95 | C complex64 96 | } 97 | 98 | type testingComplex128 struct { 99 | C complex128 100 | } 101 | 102 | type testingSlice struct { 103 | Slice []int8 104 | } 105 | 106 | type testingMap struct { 107 | Map map[string]int 108 | } 109 | 110 | type testingTime struct { 111 | Time time.Time 112 | } 113 | 114 | type testingTimePointer struct { 115 | Time *time.Time 116 | Times []*time.Time 117 | } 118 | 119 | type testingArrays struct { 120 | Array1 [8]float32 121 | Array2 [31280]string 122 | Array3 [1031280]bool 123 | Array4 [0b11]int 124 | Array5 [0o22]int 125 | Array6 [0x33]int 126 | } 127 | 128 | type testingTag struct { 129 | Tag int `msgpack:"tag_tag_tag_tag_tag"` 130 | Ignore int `msgpack:"ignore"` 131 | Omit int `msgpack:"-"` 132 | } 133 | 134 | type testingStruct struct { 135 | Int int 136 | 137 | Inside inside 138 | Outside outside 139 | 140 | // package name 141 | define2.A 142 | 143 | // dot import 144 | BB DotImport 145 | Time 146 | 147 | // recursive 148 | R *recursive 149 | 150 | TmpSlice [][]inside 151 | TmpArray [1]inside 152 | TmpMap map[inside]inside 153 | TmpPointer *inside 154 | } 155 | 156 | type inside struct { 157 | Int int 158 | } 159 | 160 | type recursive struct { 161 | Int int 162 | R *recursive 163 | } 164 | 165 | type private struct { 166 | i int 167 | } 168 | 169 | func (p *private) SetInt() { 170 | p.i = 1 171 | } 172 | 173 | type notGenerated1 struct { 174 | Int int 175 | Interface interface{} 176 | } 177 | 178 | type notGenerated2 struct { 179 | Int int 180 | Ptr uintptr 181 | } 182 | 183 | type notGenerated3 struct { 184 | Error error 185 | Int int 186 | } 187 | 188 | type notGenerated4 struct { 189 | Chan chan int 190 | Int int 191 | } 192 | 193 | type notGenerated5 struct { 194 | InnerStruct struct { 195 | Int int 196 | } 197 | Int int 198 | } 199 | 200 | type notGenerated6 struct { 201 | Func func() int 202 | Int int 203 | } 204 | 205 | type notGenerated7 struct { 206 | Child define2.NotGeneratedChild 207 | Int int 208 | } 209 | 210 | type notGenerated8 struct { 211 | Child bytes.Buffer 212 | Int int 213 | } 214 | 215 | type notGeneratedInt int 216 | type notGenerated10 struct { 217 | Int notGeneratedInt 218 | } 219 | -------------------------------------------------------------------------------- /internal/generator/structure/code_array.go: -------------------------------------------------------------------------------- 1 | package structure 2 | 3 | import ( 4 | "fmt" 5 | 6 | . "github.com/dave/jennifer/jen" 7 | "github.com/shamaton/msgpackgen/internal/generator/ptn" 8 | ) 9 | 10 | type arrayCodeGen struct { 11 | } 12 | 13 | func (st *Structure) createArrayCode(node *Node, encodeFieldName, decodeFieldName string) (cArray []Code, cMap []Code, eArray []Code, eMap []Code, dArray []Code, dMap []Code) { 14 | 15 | encodeChildName := encodeFieldName + "v" 16 | if isRootField(encodeFieldName) { 17 | encodeChildName = "vv" 18 | } 19 | 20 | decodeChildName := decodeFieldName + "v" 21 | if isRootField(decodeFieldName) { 22 | decodeChildName = "vv" 23 | } 24 | 25 | ca, cm, ea, em, da, dm := st.createFieldCode(node.Elm(), encodeChildName, decodeChildName) 26 | // double or more ... 27 | if isRecursiveChildArraySliceMap(node) { 28 | _, _, _, _, da, dm = st.createFieldCode(node.Elm(), encodeChildName, decodeChildName+"v") 29 | } 30 | isChildByte := node.Elm().IsIdentical() && node.Elm().IdenticalName == "byte" 31 | 32 | g := arrayCodeGen{} 33 | cArray = g.createCalcCode(encodeFieldName, encodeChildName, isChildByte, ca) 34 | cMap = g.createCalcCode(encodeFieldName, encodeChildName, isChildByte, cm) 35 | 36 | eArray = g.createEncCode(encodeFieldName, encodeChildName, isChildByte, ea) 37 | eMap = g.createEncCode(encodeFieldName, encodeChildName, isChildByte, em) 38 | 39 | dArray = g.createDecCode(node, st.Others, decodeFieldName, decodeChildName, da) 40 | dMap = g.createDecCode(node, st.Others, decodeFieldName, decodeChildName, dm) 41 | 42 | return 43 | } 44 | 45 | func (g arrayCodeGen) createCalcCode(fieldName, childName string, isChildByte bool, elmCodes []Code) []Code { 46 | blockCodes := createAddSizeErrCheckCode("CalcSliceLength", Len(Id(fieldName)), Lit(isChildByte)) 47 | blockCodes = append(blockCodes, For(List(Id("_"), Id(childName)).Op(":=").Range().Id(fieldName)).Block( 48 | elmCodes..., 49 | )) 50 | 51 | codes := make([]Code, 0) 52 | codes = append(codes, Block( 53 | blockCodes..., 54 | )) 55 | return codes 56 | } 57 | 58 | func (g arrayCodeGen) createEncCode(fieldName, childName string, isChildByte bool, elmCodes []Code) []Code { 59 | 60 | blockCodes := make([]Code, 0) 61 | blockCodes = append(blockCodes, Id("offset").Op("=").Id(ptn.IdEncoder).Dot("WriteSliceLength").Call(Len(Id(fieldName)), Id("offset"), Lit(isChildByte))) 62 | blockCodes = append(blockCodes, For(List(Id("_"), Id(childName)).Op(":=").Range().Id(fieldName)).Block( 63 | elmCodes..., 64 | )) 65 | 66 | codes := make([]Code, 0) 67 | codes = append(codes, Block( 68 | blockCodes..., 69 | )) 70 | return codes 71 | } 72 | 73 | func (g arrayCodeGen) createDecCode(node *Node, structures []*Structure, fieldName, childName string, elmCodes []Code) []Code { 74 | 75 | blockCodes := make([]Code, 0) 76 | blockCodes = append(blockCodes, node.TypeJenChain(structures, Var().Id(childName))) 77 | blockCodes = append(blockCodes, Var().Id(childName+"l").Int()) 78 | blockCodes = append(blockCodes, List(Id(childName+"l"), Id("offset"), Err()).Op("=").Id(ptn.IdDecoder).Dot("SliceLength").Call(Id("offset"))) 79 | blockCodes = append(blockCodes, If(Err().Op("!=").Nil()).Block( 80 | Return(Lit(0), Err()), 81 | )) 82 | blockCodes = append(blockCodes, If(Id(childName+"l").Op(">").Id(fmt.Sprint(node.ArrayLen))).Block( 83 | Return(Lit(0), Qual("fmt", "Errorf").Call(Lit("length size(%d) is over array size(%d)"), Id(childName+"l"), Id(fmt.Sprint(node.ArrayLen)))), 84 | )) 85 | 86 | elmCodes = append([]Code{node.Elm().TypeJenChain(structures, Var().Id(childName+"v"))}, elmCodes...) 87 | elmCodes = append(elmCodes, Id(childName).Index(Id(childName+"i")).Op("=").Id(childName+"v")) 88 | 89 | blockCodes = append(blockCodes, For(Id(childName+"i").Op(":=").Range().Id(childName).Index(Id(":"+childName+"l"))).Block( 90 | elmCodes..., 91 | )) 92 | 93 | name := childName 94 | andOp := "" 95 | prtCount, _ := node.GetPointerInfo() 96 | if prtCount > 0 { 97 | andOp = "&" 98 | } 99 | for i := 0; i < prtCount-1; i++ { 100 | n := "_" + name 101 | blockCodes = append(blockCodes, Id(n).Op(":=").Op("&").Id(name)) 102 | name = n 103 | } 104 | 105 | blockCodes = append(blockCodes, Id(fieldName).Op("=").Op(andOp).Id(name)) 106 | 107 | var codes []Code 108 | if node.HasParent() && node.Parent.IsPointer() { 109 | codes = blockCodes 110 | } else { 111 | codes = append(codes, If(Op("!").Id(ptn.IdDecoder).Dot("IsCodeNil").Call(Id("offset"))).Block( 112 | blockCodes..., 113 | ).Else().Block( 114 | Id("offset").Op("++"), 115 | )) 116 | } 117 | return codes 118 | } 119 | -------------------------------------------------------------------------------- /internal/generator/structure/code_slice.go: -------------------------------------------------------------------------------- 1 | package structure 2 | 3 | import ( 4 | . "github.com/dave/jennifer/jen" 5 | "github.com/shamaton/msgpackgen/internal/generator/ptn" 6 | ) 7 | 8 | type sliceCodeGen struct { 9 | } 10 | 11 | func (st *Structure) createSliceCode(node *Node, encodeFieldName, decodeFieldName string) (cArray []Code, cMap []Code, eArray []Code, eMap []Code, dArray []Code, dMap []Code) { 12 | 13 | encodeChildName, decodeChildName := encodeFieldName+"v", decodeFieldName+"v" 14 | if isRootField(encodeFieldName) { 15 | encodeChildName = "vv" 16 | } 17 | if isRootField(decodeFieldName) { 18 | decodeChildName = "vv" 19 | } 20 | 21 | ca, cm, ea, em, da, dm := st.createFieldCode(node.Elm(), encodeChildName, decodeChildName) 22 | // double or more ... 23 | if isRecursiveChildArraySliceMap(node) { 24 | _, _, _, _, da, dm = st.createFieldCode(node.Elm(), encodeChildName, decodeChildName+"v") 25 | } 26 | isChildByte := node.Elm().IsIdentical() && node.Elm().IdenticalName == "byte" 27 | 28 | g := sliceCodeGen{} 29 | 30 | cArray = g.createCalcCode(encodeFieldName, encodeChildName, isChildByte, ca) 31 | cMap = g.createCalcCode(encodeFieldName, encodeChildName, isChildByte, cm) 32 | 33 | eArray = g.createEncCode(encodeFieldName, encodeChildName, isChildByte, ea) 34 | eMap = g.createEncCode(encodeFieldName, encodeChildName, isChildByte, em) 35 | 36 | dArray = g.createDecCode(node, st.Others, decodeFieldName, decodeChildName, da) 37 | dMap = g.createDecCode(node, st.Others, decodeFieldName, decodeChildName, dm) 38 | return 39 | } 40 | 41 | func (g sliceCodeGen) createCalcCode(fieldName, childName string, isChildTypeByte bool, elmCodes []Code) []Code { 42 | 43 | blockCodes := createAddSizeErrCheckCode("CalcSliceLength", Len(Id(fieldName)), Lit(isChildTypeByte)) 44 | blockCodes = append(blockCodes, For(List(Id("_"), Id(childName)).Op(":=").Range(). /*Op(ptrOp).*/ Id(fieldName)).Block( 45 | elmCodes..., 46 | )) 47 | 48 | codes := make([]Code, 0) 49 | codes = append(codes, If(Id(fieldName).Op("!=").Nil()).Block( 50 | blockCodes..., 51 | ).Else().Block( 52 | createAddSizeCode("CalcNil"), 53 | )) 54 | return codes 55 | } 56 | 57 | func (g sliceCodeGen) createEncCode(fieldName, childName string, isChildTypeByte bool, elmCodes []Code) []Code { 58 | 59 | blockCodes := make([]Code, 0) 60 | blockCodes = append(blockCodes, Id("offset").Op("=").Id(ptn.IdEncoder).Dot("WriteSliceLength").Call(Len(Id(fieldName)), Id("offset"), Lit(isChildTypeByte))) 61 | blockCodes = append(blockCodes, For(List(Id("_"), Id(childName)).Op(":=").Range().Id(fieldName)).Block( 62 | elmCodes..., 63 | )) 64 | 65 | codes := make([]Code, 0) 66 | codes = append(codes, If(Id(fieldName).Op("!=").Nil()).Block( 67 | blockCodes..., 68 | ).Else().Block( 69 | Id("offset").Op("=").Id(ptn.IdEncoder).Dot("WriteNil").Call(Id("offset")), 70 | )) 71 | return codes 72 | } 73 | 74 | func (g sliceCodeGen) createDecCode(node *Node, structures []*Structure, fieldName, childName string, elmCodes []Code) []Code { 75 | 76 | childLengthName := childName + "l" 77 | childIndexName := childName + "i" 78 | childChildName := childName + "v" 79 | 80 | blockCodes := make([]Code, 0) 81 | blockCodes = append(blockCodes, node.TypeJenChain(structures, Var().Id(childName))) 82 | blockCodes = append(blockCodes, Var().Id(childLengthName).Int()) 83 | blockCodes = append(blockCodes, List(Id(childLengthName), Id("offset"), Err()).Op("=").Id(ptn.IdDecoder).Dot("SliceLength").Call(Id("offset"))) 84 | blockCodes = append(blockCodes, If(Err().Op("!=").Nil()).Block( 85 | Return(Lit(0), Err()), 86 | )) 87 | blockCodes = append(blockCodes, Id(childName).Op("=").Make(node.TypeJenChain(structures), Id(childLengthName))) 88 | 89 | elmCodes = append([]Code{node.Elm().TypeJenChain(structures, Var().Id(childChildName))}, elmCodes...) 90 | elmCodes = append(elmCodes, Id(childName).Index(Id(childIndexName)).Op("=").Id(childChildName)) 91 | 92 | blockCodes = append(blockCodes, For(Id(childIndexName).Op(":=").Range().Id(childName)).Block( 93 | elmCodes..., 94 | )) 95 | 96 | name := childName 97 | andOp := "" 98 | prtCount, _ := node.GetPointerInfo() 99 | if prtCount > 0 { 100 | andOp = "&" 101 | } 102 | for i := 0; i < prtCount-1; i++ { 103 | n := "_" + name 104 | blockCodes = append(blockCodes, Id(n).Op(":=").Op("&").Id(name)) 105 | name = n 106 | } 107 | 108 | blockCodes = append(blockCodes, Id(fieldName).Op("=").Op(andOp).Id(name)) 109 | 110 | var codes []Code 111 | if node.HasParent() && node.Parent.IsPointer() { 112 | codes = blockCodes 113 | } else { 114 | codes = append(codes, If(Op("!").Id(ptn.IdDecoder).Dot("IsCodeNil").Call(Id("offset"))).Block( 115 | blockCodes..., 116 | ).Else().Block( 117 | Id("offset").Op("++"), 118 | )) 119 | } 120 | return codes 121 | } 122 | -------------------------------------------------------------------------------- /internal/generator/structure/code_map.go: -------------------------------------------------------------------------------- 1 | package structure 2 | 3 | import ( 4 | . "github.com/dave/jennifer/jen" 5 | "github.com/shamaton/msgpackgen/internal/generator/ptn" 6 | ) 7 | 8 | type mapCodeGen struct { 9 | } 10 | 11 | func (st *Structure) createMapCode(node *Node, encodeFieldName, decodeFieldName string) (cArray []Code, cMap []Code, eArray []Code, eMap []Code, dArray []Code, dMap []Code) { 12 | 13 | key, value := node.KeyValue() 14 | 15 | encodeChildKey, encodeChildValue := encodeFieldName+"k", encodeFieldName+"v" 16 | if isRootField(encodeFieldName) { 17 | encodeChildKey = "kk" 18 | encodeChildValue = "vv" 19 | } 20 | 21 | decodeChildKey, decodeChildValue := decodeFieldName+"k", decodeFieldName+"v" 22 | if isRootField(decodeFieldName) { 23 | decodeChildKey = "kk" 24 | decodeChildValue = "vv" 25 | } 26 | 27 | caKey, cmKey, eaKey, emKey, daKey, dmKey := st.createFieldCode(key, encodeChildKey, decodeChildKey) 28 | caValue, cmValue, eaValue, emValue, daValue, dmValue := st.createFieldCode(value, encodeChildValue, decodeChildValue) 29 | 30 | // double or more ... 31 | if isRecursiveChildArraySliceMap(node) { 32 | _, _, _, _, daValue, dmValue = st.createFieldCode(value, encodeChildValue, decodeChildValue+"v") 33 | } 34 | 35 | g := mapCodeGen{} 36 | cArray = g.createCalcCode(encodeFieldName, encodeChildKey, encodeChildValue, caKey, caValue) 37 | cMap = g.createCalcCode(encodeFieldName, encodeChildKey, encodeChildValue, cmKey, cmValue) 38 | 39 | eArray = g.createEncCode(encodeFieldName, encodeChildKey, encodeChildValue, eaKey, eaValue) 40 | eMap = g.createEncCode(encodeFieldName, encodeChildKey, encodeChildValue, emKey, emValue) 41 | 42 | dArray = g.createDecCode(node, st.Others, decodeFieldName, decodeChildKey, decodeChildValue, daKey, daValue) 43 | dMap = g.createDecCode(node, st.Others, decodeFieldName, decodeChildKey, decodeChildValue, dmKey, dmValue) 44 | 45 | return 46 | } 47 | 48 | func (g mapCodeGen) createCalcCode( 49 | fieldName, childKeyName, childValueName string, 50 | elmKeyCodes, elmValueCodes []Code) []Code { 51 | calcCodes := createAddSizeErrCheckCode("CalcMapLength", Len(Id(fieldName))) 52 | calcCodes = append(calcCodes, For(List(Id(childKeyName), Id(childValueName)).Op(":=").Range().Id(fieldName)).Block( 53 | append(elmKeyCodes, elmValueCodes...)..., 54 | )) 55 | 56 | var codes []Code 57 | codes = append(codes, If(Id(fieldName).Op("!=").Nil()).Block( 58 | calcCodes..., 59 | ).Else().Block( 60 | createAddSizeCode("CalcNil"), 61 | )) 62 | return codes 63 | } 64 | 65 | func (g mapCodeGen) createEncCode( 66 | fieldName, childKeyName, childValueName string, 67 | elmKeyCodes, elmValueCodes []Code) []Code { 68 | 69 | encCodes := make([]Code, 0) 70 | encCodes = append(encCodes, Id("offset").Op("=").Id(ptn.IdEncoder).Dot("WriteMapLength").Call(Len(Id(fieldName)), Id("offset"))) 71 | encCodes = append(encCodes, For(List(Id(childKeyName), Id(childValueName)).Op(":=").Range().Id(fieldName)).Block( 72 | append(elmKeyCodes, elmValueCodes...)..., 73 | )) 74 | 75 | var codes []Code 76 | codes = append(codes, If(Id(fieldName).Op("!=").Nil()).Block( 77 | encCodes..., 78 | ).Else().Block( 79 | Id("offset").Op("=").Id(ptn.IdEncoder).Dot("WriteNil").Call(Id("offset")), 80 | )) 81 | return codes 82 | } 83 | 84 | func (g mapCodeGen) createDecCode( 85 | ast *Node, structures []*Structure, 86 | fieldName, childKeyName, childValueName string, 87 | elmKeyCodes, elmValueCodes []Code) []Code { 88 | 89 | decCodes := make([]Code, 0) 90 | decCodes = append(decCodes, ast.TypeJenChain(structures, Var().Id(childValueName))) 91 | decCodes = append(decCodes, Var().Id(childValueName+"l").Int()) 92 | decCodes = append(decCodes, List(Id(childValueName+"l"), Id("offset"), Err()).Op("=").Id(ptn.IdDecoder).Dot("MapLength").Call(Id("offset"))) 93 | decCodes = append(decCodes, If(Err().Op("!=").Nil()).Block( 94 | Return(Lit(0), Err()), 95 | )) 96 | decCodes = append(decCodes, Id(childValueName).Op("=").Make(ast.TypeJenChain(structures), Id(childValueName+"l"))) 97 | 98 | da := []Code{ast.Key.TypeJenChain(structures, Var().Id(childKeyName+"v"))} 99 | da = append(da, elmKeyCodes...) 100 | da = append(da, ast.Value.TypeJenChain(structures, Var().Id(childValueName+"v"))) 101 | da = append(da, elmValueCodes...) 102 | da = append(da, Id(childValueName).Index(Id(childKeyName+"v")).Op("=").Id(childValueName+"v")) 103 | 104 | decCodes = append(decCodes, For(Id(childValueName+"i").Op(":=").Lit(0).Op(";").Id(childValueName+"i").Op("<").Id(childValueName+"l").Op(";").Id(childValueName+"i").Op("++")).Block( 105 | da..., 106 | )) 107 | 108 | name := childValueName 109 | andOp := "" 110 | prtCount, _ := ast.GetPointerInfo() 111 | 112 | if prtCount > 0 { 113 | andOp = "&" 114 | } 115 | 116 | for i := 0; i < prtCount-1; i++ { 117 | n := "_" + name 118 | decCodes = append(decCodes, Id(n).Op(":=").Op("&").Id(name)) 119 | name = n 120 | } 121 | 122 | decCodes = append(decCodes, Id(fieldName).Op("=").Op(andOp).Id(name)) 123 | 124 | var codes []Code 125 | if ast.HasParent() && ast.Parent.IsPointer() { 126 | codes = decCodes 127 | } else { 128 | 129 | codes = append(codes, If(Op("!").Id(ptn.IdDecoder).Dot("IsCodeNil").Call(Id("offset"))).Block( 130 | decCodes..., 131 | ).Else().Block( 132 | Id("offset").Op("++"), 133 | )) 134 | } 135 | return codes 136 | } 137 | -------------------------------------------------------------------------------- /generator_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io" 7 | "math/rand" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | "testing" 12 | 13 | "github.com/shamaton/msgpackgen/msgpack" 14 | ) 15 | 16 | var ( 17 | iDir = "." 18 | iFile = "" 19 | oDir = "." 20 | oFile = defaultFileName 21 | ptr = defaultPointerLevel 22 | ) 23 | 24 | func TestMain(m *testing.M) { 25 | testBeforeRegister() 26 | RegisterGeneratedResolver() 27 | 28 | code := m.Run() 29 | 30 | os.Exit(code) 31 | } 32 | 33 | func testBeforeRegister() { 34 | { 35 | v := rand.Int() 36 | b, err := msgpack.Marshal(v) 37 | if err != nil { 38 | fmt.Println(err) 39 | os.Exit(1) 40 | } 41 | 42 | var vv int 43 | err = msgpack.Unmarshal(b, &vv) 44 | if err != nil { 45 | fmt.Println(err) 46 | os.Exit(1) 47 | } 48 | 49 | if v != vv { 50 | fmt.Println(v, vv, "different") 51 | os.Exit(1) 52 | } 53 | } 54 | msgpack.SetStructAsArray(true) 55 | { 56 | v := rand.Int() 57 | b, err := msgpack.Marshal(v) 58 | if err != nil { 59 | fmt.Println(err) 60 | os.Exit(1) 61 | } 62 | 63 | var vv int 64 | err = msgpack.Unmarshal(b, &vv) 65 | if err != nil { 66 | fmt.Println(err) 67 | os.Exit(1) 68 | } 69 | 70 | if v != vv { 71 | fmt.Println(v, vv, "different") 72 | os.Exit(1) 73 | } 74 | } 75 | msgpack.SetStructAsArray(false) 76 | } 77 | 78 | func TestGenerateCodeErrorInput(t *testing.T) { 79 | { 80 | d := "./noname" 81 | 82 | err := generate(d, iFile, oDir, oFile, ptr, false, true, false, false, io.Discard) 83 | if err == nil { 84 | t.Fatal("error has to return") 85 | } 86 | if !strings.Contains(err.Error(), "input directory error") { 87 | t.Fatal(err) 88 | } 89 | } 90 | { 91 | d := "./noname" 92 | f := "foo.go" 93 | 94 | err := generate(d, f, oDir, oFile, ptr, false, true, false, false, io.Discard) 95 | if err == nil { 96 | t.Fatal("error has to return") 97 | } 98 | if !strings.Contains(err.Error(), "at same time") { 99 | t.Fatal(err) 100 | } 101 | } 102 | { 103 | d := "main.go" 104 | 105 | err := generate(d, iFile, oDir, oFile, ptr, false, true, false, false, io.Discard) 106 | if err == nil { 107 | t.Fatal("error has to return") 108 | } 109 | if !strings.Contains(err.Error(), "is not directory") { 110 | t.Fatal(err) 111 | } 112 | } 113 | { 114 | f := "foo.go" 115 | 116 | err := generate(iDir, f, oDir, oFile, ptr, false, true, false, false, io.Discard) 117 | if err == nil { 118 | t.Fatal("error has to return") 119 | } 120 | if !strings.Contains(err.Error(), "input file error") { 121 | t.Fatal(err) 122 | } 123 | } 124 | { 125 | f := "internal" 126 | 127 | err := generate(iDir, f, oDir, oFile, ptr, false, true, false, false, io.Discard) 128 | if err == nil { 129 | t.Fatal("error has to return") 130 | } 131 | if !strings.Contains(err.Error(), "is a directory") { 132 | t.Fatal(err) 133 | } 134 | } 135 | { 136 | f := "./testdata/test.sh" 137 | 138 | err := generate(iDir, f, oDir, oFile, ptr, false, true, false, false, io.Discard) 139 | if err == nil { 140 | t.Fatal("error has to return") 141 | } 142 | if !strings.Contains(err.Error(), "is not .go file") { 143 | t.Fatal(err) 144 | } 145 | } 146 | { 147 | d := "./noname" 148 | 149 | err := generate(iDir, iFile, d, oFile, ptr, false, true, false, false, io.Discard) 150 | if err != nil { 151 | t.Fatal(err) 152 | } 153 | } 154 | { 155 | d := "./main.go" 156 | 157 | err := generate(iDir, iFile, d, oFile, ptr, false, true, false, false, io.Discard) 158 | if err == nil { 159 | t.Fatal("error has to return") 160 | } 161 | if !strings.Contains(err.Error(), "path is not directory") { 162 | t.Fatal(err) 163 | } 164 | } 165 | } 166 | 167 | func TestGenerateCodeGoPathOutside(t *testing.T) { 168 | 169 | g := os.Getenv("GOPATH") 170 | path := os.Getenv("PATH") 171 | err := os.Setenv("GOPATH", path) 172 | if err != nil { 173 | t.Fatal(err) 174 | } 175 | 176 | err = generate(iDir, iFile, oDir, oFile, ptr, true, true, false, false, io.Discard) 177 | if err == nil { 178 | t.Fatal("error has to return") 179 | } 180 | if !strings.Contains(err.Error(), "outside gopath") { 181 | t.Fatal(err) 182 | } 183 | 184 | err = os.Setenv("GOPATH", g) 185 | if err != nil { 186 | t.Fatal(err) 187 | } 188 | } 189 | 190 | func TestGenerateCodeDuplicateTag(t *testing.T) { 191 | 192 | f := "./testdata/def.go" 193 | 194 | err := generate(iDir, f, oDir, oFile, ptr, false, true, false, false, io.Discard) 195 | if err == nil { 196 | t.Fatal("error has to return") 197 | } 198 | if !strings.Contains(err.Error(), "duplicate tags") { 199 | t.Fatal(err) 200 | } 201 | } 202 | 203 | func TestGenerateCodeDryRun(t *testing.T) { 204 | 205 | err := generate(iDir, iFile, "", oFile, -1, false, true, false, false, io.Discard) 206 | if err != nil { 207 | t.Fatal(err) 208 | } 209 | } 210 | 211 | func TestGenerateCodeOK(t *testing.T) { 212 | var err error 213 | err = flag.CommandLine.Set("strict", "true") 214 | if err != nil { 215 | t.Fatal(err) 216 | } 217 | err = flag.CommandLine.Set("v", "true") 218 | if err != nil { 219 | t.Fatal(err) 220 | } 221 | err = flag.CommandLine.Set("pointer", "2") 222 | if err != nil { 223 | t.Fatal(err) 224 | } 225 | err = flag.CommandLine.Set("output-file", "resolver_test.go") 226 | if err != nil { 227 | t.Fatal(err) 228 | } 229 | 230 | // diff resolver_test.go main.go | wc -l 231 | main() 232 | 233 | // gopath 234 | wd, err := os.Getwd() 235 | if err != nil { 236 | t.Fatal(err) 237 | } 238 | goPath := strings.SplitN(filepath.ToSlash(wd), "/src", 2)[0] 239 | err = os.Setenv("GOPATH", filepath.FromSlash(goPath)) 240 | if err != nil { 241 | t.Fatal(err) 242 | } 243 | err = generate(iDir, iFile, oDir, oFile, ptr, true, true, false, false, io.Discard) 244 | if err != nil { 245 | t.Fatal(err) 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MessagePack Code Generator for Go 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/github.com/shamaton/msgpackgen.svg)](https://pkg.go.dev/github.com/shamaton/msgpackgen) 4 | ![test](https://github.com/shamaton/msgpackgen/workflows/test/badge.svg) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/shamaton/msgpackgen)](https://goreportcard.com/report/github.com/shamaton/msgpackgen) 6 | [![codecov](https://codecov.io/gh/shamaton/msgpackgen/branch/main/graph/badge.svg?token=K7M3778X7C)](https://codecov.io/gh/shamaton/msgpackgen) 7 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fshamaton%2Fmsgpackgen.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fshamaton%2Fmsgpackgen?ref=badge_shield) 8 | 9 | **msgpackgen** provides a code generation tool and serialization library for [MessagePack](http://msgpack.org/). 10 | 11 | * 🚀 Extremely Fast 12 | * ♻️ Easy Maintenance 13 | * 💯 Compliant with [specifications](https://github.com/msgpack/msgpack/blob/master/spec.md) 14 | 15 | ## Quickstart 16 | Create go.mod file, if you still have not created. 17 | 18 | ```shell 19 | # example 20 | go mod init github.com/user/awesome 21 | ``` 22 | 23 | In a source file(ex. main.go), include the following directive: 24 | 25 | ```go 26 | //go:generate msgpackgen 27 | or 28 | //go:generate go run github.com/shamaton/msgpackgen 29 | ``` 30 | 31 | And run the following command in your shell: 32 | 33 | ```shell 34 | go generate 35 | ``` 36 | 37 | It will generate one `.go` file for serialization, default is `resolver.msgpackgen.go`. 38 | You can call one method to use generated code. 39 | 40 | ```go 41 | func main() { 42 | // this method is defined in resolver.msgpackgen.go 43 | RegisterGeneratedResolver() 44 | 45 | // ... your code ... 46 | } 47 | ``` 48 | 49 | `Marshal` and `Unmarshal` look like this: 50 | ```go 51 | // import github.com/shamaton/msgpackgen/msgpack 52 | v := ResolvedStruct{} 53 | b, err := msgpack.Marshal(v) 54 | if err != nil { 55 | panic(err) 56 | } 57 | 58 | var vv ResolvedStruct 59 | err = msgpack.Unmarshal(b, &vv) 60 | if err != nil { 61 | panic(err) 62 | } 63 | ``` 64 | 65 | ## Serializer 66 | ### Supported Types 67 | primitive types: 68 | `int`, `int8`, `int16`, `int32`, `int64`, 69 | `uint`, `uint8`, `uint16`, `uint32`, `uint64`, 70 | `float32`, `float64`, `string`, `bool`, `byte`, `rune`, 71 | `complex64`, `complex128` 72 | 73 | slice, array: `[]`, `[cap]` 74 | 75 | map: `map[key]value` 76 | 77 | struct: `time.Time` and structures you defined 78 | 79 | ### Tags 80 | Renaming or omitting are available. 81 | 82 | * Renaming fields via `msgpack:"field_name"` 83 | * Omitting fields via `msgpack:"-"` 84 | 85 | 86 | ### Switch Default Behaviour 87 | Default serialization behaviour is map type. But the performance of array type is better. 88 | If you want to switch array type as default, use `SetStructAsArray`. 89 | 90 | Also, you can use `MarshalAsArray`, `UnmarshalAsArray`. 91 | 92 | ## Code Generator 93 | ### Easy maintenance 94 | This tool generates only one `.go` file. 95 | All you have to delete is one generated `.go` file. 96 | 97 | ### Analyzing 98 | `-input-dir` needs to be in $GOPATH. 99 | 100 | Resolver is generated by recursively searching directories, 101 | but some directories and files are ignored. 102 | * Prefix `_` and `.` directory. 103 | * `testdata` and `vendor` directory 104 | * `_test.go` file 105 | 106 | If you use `-input-file` option, it will work without considering the above conditions. 107 | 108 | --- 109 | 110 | Compatible with various import rules. 111 | 112 | ```go 113 | import (" 114 | "example.com/user/a/b" 115 | d "example.com/user/a/c" 116 | . "example.com/user/a/e" 117 | ) 118 | ``` 119 | 120 | ### Not Generated Case 121 | Not generated in the following cases: 122 | 123 | ```go 124 | // ex. a/example.go 125 | type Example struct { 126 | // unsupported types 127 | Interface interface{} 128 | Uintptr uintptr 129 | Error error 130 | Chan chan 131 | Func func() 132 | 133 | // nested struct is also unsupported 134 | NestedStruct struct {} 135 | 136 | // because b.Example is not generated 137 | B b.Example 138 | 139 | // because bytes.Butffer is in outside package 140 | Buf bytes.Buffer 141 | } 142 | 143 | func (e Example) F() { 144 | // unsupported struct defined in func 145 | type InFunction struct {} 146 | } 147 | 148 | // ex a/b/example.go 149 | type Example struct { 150 | Interface interface{} 151 | } 152 | ``` 153 | If you serialize a struct that wasn't code generated, it will be processed by [shamaton/msgpack](https://github.com/shamaton/msgpack). 154 | 155 | ### Strict Mode 156 | If you use strict mode(option `-strict`), you will get an error if an unrecognized structure is passed. 157 | In other words, [shamaton/msgpack](https://github.com/shamaton/msgpack) is not used. 158 | 159 | --- 160 | 161 | See also `msgpackgen -h` 162 | ```shell 163 | Usage of msgpackgen: 164 | -dry-run 165 | dry run mode 166 | -input-dir string 167 | input directory. input-file cannot be used at the same time (default ".") 168 | -input-file string 169 | input a specific file. input-dir cannot be used at the same time 170 | -output-dir string 171 | output directory (default ".") 172 | -output-file string 173 | name of generated file (default "resolver.msgpackgen.go") 174 | -pointer int 175 | pointer level to consider (default 1) 176 | -strict 177 | strict mode 178 | -use-gopath 179 | use GOPATH instead of go.mod 180 | -v verbose diagnostics 181 | ``` 182 | 183 | ## Benchmarks 184 | 185 | These results are recorded by [msgpack_bench](https://github.com/shamaton/msgpack_bench) at 2021/08. 186 | The result of this package is that the suffix has `ShamatonGen`. 187 | ![msgpack_bench](https://user-images.githubusercontent.com/4637556/128298988-0e3c96fd-6014-42a0-9050-36e2b58316b1.png) 188 | The result of [go_serialization_benchmarks](https://github.com/alecthomas/go_serialization_benchmarks) is here. 189 | ![go_serialization_benchmarks](https://user-images.githubusercontent.com/4637556/128299037-06b3a645-2726-4205-848a-cccebb9a3d7f.png) -------------------------------------------------------------------------------- /internal/generator/structure/node.go: -------------------------------------------------------------------------------- 1 | package structure 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | 7 | . "github.com/dave/jennifer/jen" 8 | ) 9 | 10 | const ( 11 | fieldTypeIdent = iota + 1 12 | fieldTypeSlice 13 | fieldTypeArray 14 | fieldTypeStruct 15 | fieldTypeMap 16 | fieldTypePointer 17 | ) 18 | 19 | // Node is a collection of field information. 20 | type Node struct { 21 | fieldType int 22 | 23 | // for identical 24 | IdenticalName string 25 | 26 | // for array 27 | ArrayLen uint64 28 | 29 | // for struct 30 | ImportPath string 31 | PackageName string 32 | StructName string 33 | 34 | // for array / map / pointer 35 | Key *Node 36 | Value *Node 37 | 38 | Parent *Node 39 | } 40 | 41 | // Elm gets the child node, if field-type is slice, array or pointer. 42 | func (n Node) Elm() *Node { return n.Key } 43 | 44 | // KeyValue gets child nodes, if field-type is map. 45 | func (n Node) KeyValue() (*Node, *Node) { return n.Key, n.Value } 46 | 47 | // IsIdentical returns true, if field-type is ident. 48 | func (n Node) IsIdentical() bool { return n.fieldType == fieldTypeIdent } 49 | 50 | // IsSlice returns true, if field-type is slice. 51 | func (n Node) IsSlice() bool { return n.fieldType == fieldTypeSlice } 52 | 53 | // IsArray returns true, if field-type is array. 54 | func (n Node) IsArray() bool { return n.fieldType == fieldTypeArray } 55 | 56 | // IsStruct returns true, if field-type is struct. 57 | func (n Node) IsStruct() bool { return n.fieldType == fieldTypeStruct } 58 | 59 | // IsMap returns true, if field-type is map. 60 | func (n Node) IsMap() bool { return n.fieldType == fieldTypeMap } 61 | 62 | // IsPointer returns true, if field-type is pointer. 63 | func (n Node) IsPointer() bool { return n.fieldType == fieldTypePointer } 64 | 65 | // HasParent returns true, if parent node exists. 66 | func (n Node) HasParent() bool { return n.Parent != nil } 67 | 68 | // IsParentPointer returns true, if HasParent is true and parent node is pointer. 69 | func (n Node) IsParentPointer() bool { return n.HasParent() && n.Parent.IsPointer() } 70 | 71 | // SetKeyNode sets node to key field. 72 | func (n *Node) SetKeyNode(key *Node) { n.Key = key } 73 | 74 | // SetValueNode sets node to value field. 75 | func (n *Node) SetValueNode(value *Node) { n.Value = value } 76 | 77 | // GetPointerInfo gets some pointer information to create codes. 78 | func (n *Node) GetPointerInfo() (ptrCount int, isParentTypeArrayOrMap bool) { 79 | node := n 80 | for node.HasParent() { 81 | node = node.Parent 82 | if node.IsPointer() { 83 | // pointer 84 | ptrCount++ 85 | } else { 86 | // slice / array / map 87 | isParentTypeArrayOrMap = true 88 | break 89 | } 90 | } 91 | return 92 | } 93 | 94 | // CanGenerate return true, if it satisfied conditions by this node. 95 | func (n Node) CanGenerate(structures []*Structure) (bool, []string) { 96 | messages := make([]string, 0) 97 | switch { 98 | case n.IsIdentical(): 99 | return true, messages 100 | 101 | case n.IsStruct(): 102 | if n.ImportPath == "time" && n.StructName == "Time" { 103 | return true, messages 104 | } 105 | for _, v := range structures { 106 | if v.ImportPath == n.ImportPath && v.Name == n.StructName { 107 | return true, messages 108 | } 109 | } 110 | return false, append(messages, fmt.Sprintf("struct %s.%s is not generated.", n.ImportPath, n.StructName)) 111 | 112 | case n.IsSlice(): 113 | return n.Elm().CanGenerate(structures) 114 | 115 | case n.IsArray(): 116 | return n.Elm().CanGenerate(structures) 117 | 118 | case n.IsMap(): 119 | k, v := n.KeyValue() 120 | kb, kMessages := k.CanGenerate(structures) 121 | vb, vMessages := v.CanGenerate(structures) 122 | messages = append(messages, kMessages...) 123 | messages = append(messages, vMessages...) 124 | return kb && vb, messages 125 | 126 | case n.IsPointer(): 127 | return n.Elm().CanGenerate(structures) 128 | } 129 | return false, append(messages, "unreachable code") 130 | } 131 | 132 | // TypeJenChain is a helper method to create code. 133 | func (n Node) TypeJenChain(structures []*Structure, statements ...*Statement) *Statement { 134 | var str *Statement 135 | if len(statements) > 0 { 136 | str = statements[0] 137 | } else { 138 | str = Id("") 139 | } 140 | 141 | switch { 142 | case n.IsIdentical(): 143 | str = str.Id(n.IdenticalName) 144 | 145 | case n.IsStruct(): 146 | if n.ImportPath == "time" && n.StructName == "Time" { 147 | str = str.Qual(n.ImportPath, n.StructName) 148 | } else { 149 | var asRef *Structure 150 | for _, v := range structures { 151 | if v.ImportPath == n.ImportPath && v.Name == n.StructName { 152 | asRef = v 153 | break 154 | } 155 | } 156 | if asRef == nil { 157 | // unreachable 158 | panic(fmt.Sprintf("not found struct %s.%s", n.ImportPath, n.StructName)) 159 | } 160 | 161 | if asRef.NoUseQual { 162 | str = str.Id(n.StructName) 163 | } else { 164 | str = str.Qual(n.ImportPath, n.StructName) 165 | } 166 | } 167 | 168 | case n.IsSlice(): 169 | str = str.Id("[]") 170 | str = n.Elm().TypeJenChain(structures, str) 171 | 172 | case n.IsArray(): 173 | str = str.Id(fmt.Sprintf("[%d]", n.ArrayLen)) 174 | str = n.Elm().TypeJenChain(structures, str) 175 | 176 | case n.IsMap(): 177 | str = str.Id("map[") 178 | k, v := n.KeyValue() 179 | str = k.TypeJenChain(structures, str) 180 | str = str.Id("]") 181 | str = v.TypeJenChain(structures, str) 182 | 183 | case n.IsPointer(): 184 | str = str.Id("*") 185 | str = n.Elm().TypeJenChain(structures, str) 186 | } 187 | return str 188 | } 189 | 190 | // CreateIdentNode creates a node of ident type. 191 | func CreateIdentNode(ident *ast.Ident, parent *Node) *Node { 192 | return &Node{ 193 | fieldType: fieldTypeIdent, 194 | IdenticalName: ident.Name, 195 | Parent: parent, 196 | } 197 | } 198 | 199 | // CreateStructNode creates a node of struct type. 200 | func CreateStructNode(importPath, packageName, structName string, parent *Node) *Node { 201 | return &Node{ 202 | fieldType: fieldTypeStruct, 203 | ImportPath: importPath, 204 | PackageName: packageName, 205 | StructName: structName, 206 | Parent: parent, 207 | } 208 | } 209 | 210 | // CreateSliceNode creates a node of slice type. 211 | func CreateSliceNode(parent *Node) *Node { 212 | return &Node{ 213 | fieldType: fieldTypeSlice, 214 | Parent: parent, 215 | } 216 | } 217 | 218 | // CreateArrayNode creates a node of array type. 219 | func CreateArrayNode(len uint64, parent *Node) *Node { 220 | return &Node{ 221 | fieldType: fieldTypeArray, 222 | ArrayLen: len, 223 | Parent: parent, 224 | } 225 | } 226 | 227 | // CreateMapNode creates a node of map type. 228 | func CreateMapNode(parent *Node) *Node { 229 | return &Node{ 230 | fieldType: fieldTypeMap, 231 | Parent: parent, 232 | } 233 | } 234 | 235 | // CreatePointerNode creates a node of pointer type. 236 | func CreatePointerNode(parent *Node) *Node { 237 | return &Node{ 238 | fieldType: fieldTypePointer, 239 | Parent: parent, 240 | } 241 | } 242 | 243 | // IsPrimitive returns true, if name matches any case. 244 | func IsPrimitive(name string) bool { 245 | switch name { 246 | case "int", "int8", "int16", "int32", "int64": 247 | return true 248 | 249 | case "uint", "uint8", "uint16", "uint32", "uint64": 250 | return true 251 | 252 | case "float32", "float64": 253 | return true 254 | 255 | case "string", "rune": 256 | return true 257 | 258 | case "bool", "byte", "complex64", "complex128": 259 | return true 260 | } 261 | return false 262 | } 263 | -------------------------------------------------------------------------------- /internal/generator/structure/structure.go: -------------------------------------------------------------------------------- 1 | package structure 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "math" 7 | 8 | . "github.com/dave/jennifer/jen" 9 | "github.com/shamaton/msgpackgen/internal/generator/ptn" 10 | ) 11 | 12 | // Structure has information needed for code generation 13 | type Structure struct { 14 | ImportPath string 15 | Package string 16 | Name string 17 | Fields []Field 18 | NoUseQual bool 19 | 20 | Others []*Structure 21 | File *ast.File 22 | 23 | CanGen bool 24 | Reasons []string 25 | } 26 | 27 | // Field has a field information in structure 28 | type Field struct { 29 | Name string 30 | Tag string 31 | Node *Node 32 | } 33 | 34 | // CalcArraySizeFuncName gets the function name for each structure 35 | func (st *Structure) CalcArraySizeFuncName() string { 36 | return st.createFuncName("calcArraySize") 37 | } 38 | 39 | // CalcMapSizeFuncName gets the function name for each structure 40 | func (st *Structure) CalcMapSizeFuncName() string { 41 | return st.createFuncName("calcMapSize") 42 | } 43 | 44 | // EncodeArrayFuncName gets the function name for each structure 45 | func (st *Structure) EncodeArrayFuncName() string { 46 | return st.createFuncName("encodeArray") 47 | } 48 | 49 | // EncodeMapFuncName gets the function name for each structure 50 | func (st *Structure) EncodeMapFuncName() string { 51 | return st.createFuncName("encodeMap") 52 | } 53 | 54 | // DecodeArrayFuncName gets the function name for each structure 55 | func (st *Structure) DecodeArrayFuncName() string { 56 | return st.createFuncName("decodeArray") 57 | } 58 | 59 | // DecodeMapFuncName gets the function name for each structure 60 | func (st *Structure) DecodeMapFuncName() string { 61 | return st.createFuncName("decodeMap") 62 | } 63 | 64 | func (st *Structure) createFuncName(prefix string) string { 65 | return createFuncName(prefix, st.Name, st.ImportPath) 66 | } 67 | 68 | // CreateCode creates codes to serialize structure 69 | func (st *Structure) CreateCode(f *File) { 70 | v := "v" 71 | 72 | calcStruct, encStructArray, encStructMap := st.createStructCode(len(st.Fields)) 73 | 74 | calcArraySizeCodes := make([]Code, 0) 75 | calcArraySizeCodes = append(calcArraySizeCodes, Id("size").Op(":=").Lit(0)) 76 | calcArraySizeCodes = append(calcArraySizeCodes, calcStruct) 77 | 78 | calcMapSizeCodes := make([]Code, 0) 79 | calcMapSizeCodes = append(calcMapSizeCodes, Id("size").Op(":=").Lit(0)) 80 | calcMapSizeCodes = append(calcMapSizeCodes, calcStruct) 81 | 82 | encArrayCodes := make([]Code, 0) 83 | encArrayCodes = append(encArrayCodes, Var().Err().Error()) 84 | encArrayCodes = append(encArrayCodes, encStructArray) 85 | 86 | encMapCodes := make([]Code, 0) 87 | encMapCodes = append(encMapCodes, Var().Err().Error()) 88 | encMapCodes = append(encMapCodes, encStructMap) 89 | 90 | decArrayCodes := make([]Code, 0) 91 | decArrayCodes = append(decArrayCodes, List(Id("offset"), Err()).Op(":=").Id(ptn.IdDecoder).Dot("CheckStructHeader").Call(Lit(len(st.Fields)), Id("offset"))) 92 | decArrayCodes = append(decArrayCodes, If(Err().Op("!=").Nil()).Block( 93 | Return(Lit(0), Err()), 94 | )) 95 | 96 | decMapCodeSwitchCases := make([]Code, 0) 97 | decKeySliceVar := make([]Code, 0) 98 | 99 | for i, field := range st.Fields { 100 | fieldName := "v." + field.Name 101 | 102 | calcKeyStringCode, writeKeyStringCode := st.createKeyStringCode(field.Tag) 103 | calcMapSizeCodes = append(calcMapSizeCodes, calcKeyStringCode) 104 | encMapCodes = append(encMapCodes, writeKeyStringCode) 105 | 106 | cArray, cMap, eArray, eMap, dArray, dMap := st.createFieldCode(field.Node, fieldName, fieldName) 107 | calcArraySizeCodes = append(calcArraySizeCodes, cArray...) 108 | 109 | calcMapSizeCodes = append(calcMapSizeCodes, cMap...) 110 | 111 | encArrayCodes = append(encArrayCodes, eArray...) 112 | encMapCodes = append(encMapCodes, eMap...) 113 | 114 | decArrayCodes = append(decArrayCodes, dArray...) 115 | 116 | tagBytes := []byte(field.Tag) 117 | lits := make([]Code, len(tagBytes)) 118 | for i := range tagBytes { 119 | lits = append(lits, Lit(tagBytes[i])) 120 | } 121 | decKeySliceVar = append(decKeySliceVar, Values(lits...).Id(",").Commentf("%s", field.Tag)) 122 | 123 | decMapCodeSwitchCases = append(decMapCodeSwitchCases, Case(Lit(i)).Block( 124 | append(dMap, Id("count").Op("++"))..., 125 | // dMap..., 126 | ), 127 | ) 128 | } 129 | 130 | // not use jump offset 131 | //decMapCodeSwitchCases = append(decMapCodeSwitchCases, Default().Block( 132 | // Id("offset").Op("=").Id(ptn.IdDecoder).Dot("JumpOffset").Call(Id("offset")), 133 | // ), 134 | //) 135 | decMapCodeSwitchCases = append(decMapCodeSwitchCases, Default().Block( 136 | Return(Lit(0), Qual("fmt", "Errorf").Call(Lit("unknown key[%s] found"), String().Id("(dataKey)"))), 137 | ), 138 | ) 139 | 140 | decMapCodes := make([]Code, 0) 141 | 142 | decMapCodes = append(decMapCodes, Id("keys").Op(":=").Index().Index().Byte().Block(decKeySliceVar...)) 143 | 144 | decMapCodes = append(decMapCodes, List(Id("offset"), Err()).Op(":=").Id(ptn.IdDecoder).Dot("CheckStructHeader").Call(Lit(len(st.Fields)), Id("offset"))) 145 | decMapCodes = append(decMapCodes, If(Err().Op("!=").Nil()).Block( 146 | Return(Lit(0), Err()), 147 | )) 148 | //decMapCodes = append(decMapCodes, Id("dataLen").Op(":=").Id(ptn.IdDecoder).Dot("Len").Call()) 149 | //decMapCodes = append(decMapCodes, For(Id("count").Op("<").Id("dataLen").Block( 150 | decMapCodes = append(decMapCodes, Id("count").Op(":=").Lit(0)) 151 | decMapCodes = append(decMapCodes, For(Id("count").Op("<").Lit(len(st.Fields)).Block( 152 | Var().Id("dataKey").Index().Byte(), 153 | List(Id("dataKey"), Id("offset"), Err()).Op("=").Id(ptn.IdDecoder).Dot("AsStringBytes").Call(Id("offset")), 154 | If(Err().Op("!=").Nil()).Block( 155 | Return(Lit(0), Err()), 156 | ), 157 | 158 | Id("fieldIndex").Op(":=").Lit(-1), 159 | For(List(Id("i"), Id("key"))).Op(":=").Range().Id("keys").Block( 160 | If(Len(Id("dataKey")).Op("!=").Len(Id("key"))).Block( 161 | Continue(), 162 | ), 163 | 164 | Id("fieldIndex").Op("=").Id("i"), 165 | For(Id("dataKeyIndex")).Op(":=").Range().Id("dataKey").Block( 166 | If(Id("dataKey").Index(Id("dataKeyIndex")).Op("!=").Id("key").Index(Id("dataKeyIndex"))).Block( 167 | Id("fieldIndex").Op("=").Lit(-1), 168 | Break(), 169 | ), 170 | ), 171 | If(Id("fieldIndex").Op(">=").Lit(0)).Block( 172 | Break(), 173 | ), 174 | ), 175 | 176 | Switch(Id("fieldIndex")).Block( 177 | decMapCodeSwitchCases..., 178 | ), 179 | ))) 180 | 181 | var firstEncParam, firstDecParam *Statement 182 | if st.NoUseQual { 183 | firstEncParam = Id(v).Id(st.Name) 184 | firstDecParam = Id(v).Op("*").Id(st.Name) 185 | } else { 186 | firstEncParam = Id(v).Qual(st.ImportPath, st.Name) 187 | firstDecParam = Id(v).Op("*").Qual(st.ImportPath, st.Name) 188 | } 189 | 190 | f.Comment(fmt.Sprintf("// calculate size from %s.%s\n", st.ImportPath, st.Name)). 191 | Func().Id(st.CalcArraySizeFuncName()).Params(firstEncParam, Id(ptn.IdEncoder).Op("*").Qual(ptn.PkEnc, "Encoder")).Params(Int(), Error()).Block( 192 | append(calcArraySizeCodes, Return(Id("size"), Nil()))..., 193 | ) 194 | 195 | f.Comment(fmt.Sprintf("// calculate size from %s.%s\n", st.ImportPath, st.Name)). 196 | Func().Id(st.CalcMapSizeFuncName()).Params(firstEncParam, Id(ptn.IdEncoder).Op("*").Qual(ptn.PkEnc, "Encoder")).Params(Int(), Error()).Block( 197 | append(calcMapSizeCodes, Return(Id("size"), Nil()))..., 198 | ) 199 | 200 | f.Comment(fmt.Sprintf("// encode from %s.%s\n", st.ImportPath, st.Name)). 201 | Func().Id(st.EncodeArrayFuncName()).Params(firstEncParam, Id(ptn.IdEncoder).Op("*").Qual(ptn.PkEnc, "Encoder"), Id("offset").Int()).Params(Index().Byte(), Int(), Error()).Block( 202 | append(encArrayCodes, Return(Id(ptn.IdEncoder).Dot("EncodedBytes").Call(), Id("offset"), Err()))..., 203 | ) 204 | 205 | f.Comment(fmt.Sprintf("// encode from %s.%s\n", st.ImportPath, st.Name)). 206 | Func().Id(st.EncodeMapFuncName()).Params(firstEncParam, Id(ptn.IdEncoder).Op("*").Qual(ptn.PkEnc, "Encoder"), Id("offset").Int()).Params(Index().Byte(), Int(), Error()).Block( 207 | append(encMapCodes, Return(Id(ptn.IdEncoder).Dot("EncodedBytes").Call(), Id("offset"), Err()))..., 208 | ) 209 | 210 | f.Comment(fmt.Sprintf("// decode to %s.%s\n", st.ImportPath, st.Name)). 211 | Func().Id(st.DecodeArrayFuncName()).Params(firstDecParam, Id(ptn.IdDecoder).Op("*").Qual(ptn.PkDec, "Decoder"), Id("offset").Int()).Params(Int(), Error()).Block( 212 | append(decArrayCodes, Return(Id("offset"), Err()))..., 213 | ) 214 | 215 | f.Comment(fmt.Sprintf("// decode to %s.%s\n", st.ImportPath, st.Name)). 216 | Func().Id(st.DecodeMapFuncName()).Params(firstDecParam, Id(ptn.IdDecoder).Op("*").Qual(ptn.PkDec, "Decoder"), Id("offset").Int()).Params(Int(), Error()).Block( 217 | 218 | append(decMapCodes, Return(Id("offset"), Err()))..., 219 | ) 220 | } 221 | 222 | func (st *Structure) createStructCode(fieldNum int) (Code, Code, Code) { 223 | 224 | suffix := "" 225 | if fieldNum <= 0x0f { 226 | suffix = "Fix" 227 | } else if fieldNum <= math.MaxUint16 { 228 | suffix = "16" 229 | } else if uint(fieldNum) <= math.MaxUint32 { 230 | suffix = "32" 231 | } 232 | 233 | return Id("size").Op("+=").Id(ptn.IdEncoder).Dot("CalcStructHeader" + suffix).Call(Lit(fieldNum)), 234 | Id("offset").Op("=").Id(ptn.IdEncoder).Dot(" WriteStructHeader"+suffix+"AsArray").Call(Lit(fieldNum), Id("offset")), 235 | Id("offset").Op("=").Id(ptn.IdEncoder).Dot(" WriteStructHeader"+suffix+"AsMap").Call(Lit(fieldNum), Id("offset")) 236 | } 237 | 238 | func (st *Structure) createKeyStringCode(v string) (Code, Code) { 239 | l := len(v) 240 | suffix := "" 241 | if l < 32 { 242 | suffix = "Fix" 243 | } else if l <= math.MaxUint8 { 244 | suffix = "8" 245 | } else if l <= math.MaxUint16 { 246 | suffix = "16" 247 | } else { 248 | suffix = "32" 249 | } 250 | 251 | return Id("size").Op("+=").Id(ptn.IdEncoder).Dot("CalcString" + suffix).Call(Lit(l)), 252 | Id("offset").Op("=").Id(ptn.IdEncoder).Dot("WriteString"+suffix).Call(Lit(v), Lit(l), Id("offset")) 253 | } 254 | 255 | func (st *Structure) createFieldCode(node *Node, encodeFieldName, decodeFieldName string) (cArray []Code, cMap []Code, eArray []Code, eMap []Code, dArray []Code, dMap []Code) { 256 | 257 | switch { 258 | case node.IsIdentical(): 259 | cArray, cMap, eArray, eMap, dArray, dMap = st.createIdentCode(node, encodeFieldName, decodeFieldName) 260 | 261 | case node.IsSlice(): 262 | cArray, cMap, eArray, eMap, dArray, dMap = st.createSliceCode(node, encodeFieldName, decodeFieldName) 263 | 264 | case node.IsArray(): 265 | cArray, cMap, eArray, eMap, dArray, dMap = st.createArrayCode(node, encodeFieldName, decodeFieldName) 266 | 267 | case node.IsMap(): 268 | cArray, cMap, eArray, eMap, dArray, dMap = st.createMapCode(node, encodeFieldName, decodeFieldName) 269 | 270 | case node.IsPointer(): 271 | cArray, cMap, eArray, eMap, dArray, dMap = st.createPointerCode(node, encodeFieldName, decodeFieldName) 272 | 273 | case node.IsStruct(): 274 | 275 | if node.ImportPath == "time" { 276 | cArray, cMap, eArray, eMap, dArray, dMap = st.createTimeCode(encodeFieldName, decodeFieldName, node) 277 | } else { 278 | cArray, cMap, eArray, eMap, dArray, dMap = st.createNamedCode(encodeFieldName, decodeFieldName, node) 279 | } 280 | } 281 | 282 | return 283 | } 284 | -------------------------------------------------------------------------------- /internal/generator/analyze.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/importer" 7 | "go/parser" 8 | "go/token" 9 | "go/types" 10 | "math/big" 11 | "os" 12 | "path/filepath" 13 | "reflect" 14 | "strings" 15 | "unicode" 16 | 17 | "github.com/shamaton/msgpackgen/internal/generator/structure" 18 | ) 19 | 20 | func (g *generator) getPackages(files []string) error { 21 | 22 | for _, file := range files { 23 | 24 | importPath, packageName, parseFile, err := g.getImportPathAndParseFile(file) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | if filepath.Dir(file) == g.outputDir { 30 | g.noUserQualMap[importPath] = true 31 | } else if packageName == "main" { 32 | if g.verbose { 33 | fmt.Println("skipping other main package ", file) 34 | } 35 | continue 36 | } 37 | 38 | g.parseFiles = append(g.parseFiles, parseFile) 39 | g.parseFile2ImportPath[parseFile] = importPath 40 | g.importPath2package[importPath] = packageName 41 | g.targetPackages[packageName] = true 42 | if _, ok := g.importPath2ParseFiles[importPath]; !ok { 43 | g.importPath2ParseFiles[importPath] = make([]*ast.File, 0) 44 | } 45 | g.importPath2ParseFiles[importPath] = append(g.importPath2ParseFiles[importPath], parseFile) 46 | } 47 | return nil 48 | } 49 | 50 | func (g *generator) getImportPathAndParseFile(file string) (string, string, *ast.File, error) { 51 | 52 | dir := filepath.Dir(file) 53 | importPath, err := g.getImportPath(dir) 54 | if err != nil { 55 | return "", "", nil, err 56 | } 57 | 58 | source, err := os.ReadFile(file) 59 | if err != nil { 60 | return "", "", nil, err 61 | } 62 | 63 | parseFile, err := parser.ParseFile(g.fileSet, file, source, parser.AllErrors) 64 | if err != nil { 65 | return "", "", nil, err 66 | } 67 | 68 | var packageName string 69 | ast.Inspect(parseFile, func(n ast.Node) bool { 70 | 71 | switch x := n.(type) { 72 | case *ast.File: 73 | packageName = x.Name.String() 74 | } 75 | 76 | return true 77 | }) 78 | return importPath, packageName, parseFile, nil 79 | } 80 | 81 | func (g *generator) analyze() error { 82 | analyzedMap := map[*ast.File]bool{} 83 | for _, parseFile := range g.parseFiles { 84 | 85 | fullPackageName, ok := g.parseFile2ImportPath[parseFile] 86 | if !ok { 87 | return fmt.Errorf("not found fullPackageName") 88 | } 89 | packageName, ok := g.importPath2package[fullPackageName] 90 | if !ok { 91 | return fmt.Errorf("not found package name") 92 | } 93 | 94 | err := g.createAnalyzedStructs(parseFile, packageName, fullPackageName, analyzedMap) 95 | if err != nil { 96 | return err 97 | } 98 | } 99 | 100 | return g.setFieldToStructs() 101 | } 102 | 103 | func (g *generator) createAnalyzedStructs(parseFile *ast.File, packageName, importPath string, analyzedMap map[*ast.File]bool) error { 104 | // done analysis 105 | if _, ok := analyzedMap[parseFile]; ok { 106 | return nil 107 | } 108 | 109 | importMap, dotImports := g.createImportMap(parseFile) 110 | // dot imports 111 | dotStructs := map[string]*structure.Structure{} 112 | for _, dotImport := range dotImports { 113 | pfs, ok := g.importPath2ParseFiles[dotImport] 114 | if !ok { 115 | continue 116 | } 117 | name, ok := g.importPath2package[dotImport] 118 | if !ok { 119 | continue 120 | } 121 | 122 | for _, pf := range pfs { 123 | err := g.createAnalyzedStructs(pf, name, dotImport, analyzedMap) 124 | if err != nil { 125 | return err 126 | } 127 | analyzedMap[pf] = true 128 | } 129 | 130 | for _, st := range analyzedStructs { 131 | if st.ImportPath == dotImport { 132 | dotStructs[st.Name] = st 133 | } 134 | } 135 | } 136 | 137 | var lBrace, rBrace token.Pos 138 | 139 | structNames := make([]string, 0) 140 | ast.Inspect(parseFile, func(n ast.Node) bool { 141 | 142 | xx, ok := n.(*ast.FuncDecl) 143 | if ok { 144 | lBrace = xx.Body.Lbrace 145 | rBrace = xx.Body.Rbrace 146 | } 147 | 148 | x, ok := n.(*ast.TypeSpec) 149 | if !ok { 150 | return true 151 | } 152 | 153 | if _, ok = x.Type.(*ast.StructType); ok { 154 | structName := x.Name.String() 155 | 156 | if lBrace <= n.Pos() && n.End() <= rBrace { 157 | structsInBrace = append(structsInBrace, fmt.Sprintf("%s.%s", importPath, structName)) 158 | return true 159 | } 160 | 161 | if importPath != g.outputImportPath && !unicode.IsUpper(rune(structName[0])) { 162 | return true 163 | } 164 | structNames = append(structNames, structName) 165 | } 166 | return true 167 | }) 168 | 169 | structs := make([]*structure.Structure, len(structNames)) 170 | for i, structName := range structNames { 171 | structs[i] = &structure.Structure{ 172 | ImportPath: importPath, 173 | Package: packageName, 174 | Name: structName, 175 | NoUseQual: g.noUserQualMap[importPath], 176 | File: parseFile, 177 | } 178 | } 179 | analyzedStructs = append(analyzedStructs, structs...) 180 | analyzedMap[parseFile] = true 181 | 182 | g.parseFile2ImportMap[parseFile] = importMap 183 | g.parseFile2DotImportMap[parseFile] = dotStructs 184 | return nil 185 | } 186 | 187 | func (g *generator) createImportMap(parseFile *ast.File) (map[string]string, []string) { 188 | 189 | importMap := map[string]string{} 190 | dotImports := make([]string, 0) 191 | 192 | for _, imp := range parseFile.Imports { 193 | 194 | value := strings.ReplaceAll(imp.Path.Value, "\"", "") 195 | 196 | if imp.Name == nil || imp.Name.Name == "" { 197 | key := strings.Split(value, "/") 198 | importMap[key[len(key)-1]] = value 199 | } else if imp.Name.Name == "." { 200 | dotImports = append(dotImports, value) 201 | } else { 202 | key := strings.ReplaceAll(imp.Name.Name, "\"", "") 203 | importMap[key] = value 204 | } 205 | } 206 | return importMap, dotImports 207 | } 208 | 209 | func (g *generator) setFieldToStructs() error { 210 | for _, analyzedStruct := range analyzedStructs { 211 | 212 | importMap := g.parseFile2ImportMap[analyzedStruct.File] 213 | dotStructs := g.parseFile2DotImportMap[analyzedStruct.File] 214 | 215 | sameHierarchyStructs := map[string]bool{} 216 | for _, aast := range analyzedStructs { 217 | if analyzedStruct.ImportPath == aast.ImportPath { 218 | sameHierarchyStructs[aast.Name] = true 219 | } 220 | } 221 | 222 | err := g.setFieldToStruct(analyzedStruct, importMap, dotStructs, sameHierarchyStructs) 223 | if err != nil { 224 | return err 225 | } 226 | } 227 | return nil 228 | } 229 | 230 | func (g *generator) setFieldToStruct(target *structure.Structure, 231 | importMap map[string]string, dotStructs map[string]*structure.Structure, sameHierarchyStructs map[string]bool, 232 | ) (err error) { 233 | 234 | analyzedFieldMap := map[string]*structure.Node{} 235 | ast.Inspect(target.File, func(n ast.Node) bool { 236 | 237 | x, ok := n.(*ast.TypeSpec) 238 | if !ok { 239 | return true 240 | } 241 | 242 | if st, ok := x.Type.(*ast.StructType); ok { 243 | if x.Name.String() != target.Name { 244 | return true 245 | } 246 | 247 | canGen := true 248 | reasons := make([]string, 0) 249 | for i, field := range st.Fields.List { 250 | 251 | key := fmt.Sprint(i) 252 | 253 | value, ok, rs := g.createNodeRecursive(field.Type, nil, importMap, dotStructs, sameHierarchyStructs, target.ImportPath, target.Package) 254 | canGen = canGen && ok 255 | if ok { 256 | analyzedFieldMap[key+"@"+x.Name.String()] = value 257 | } 258 | reasons = append(reasons, rs...) 259 | } 260 | 261 | if canGen { 262 | target.CanGen = true 263 | target.Fields, err = g.createAnalyzedFields(target.Package, target.Name, analyzedFieldMap, g.fileSet, target.File) 264 | if err != nil { 265 | return false 266 | } 267 | } else { 268 | target.CanGen = false 269 | target.Reasons = reasons 270 | } 271 | } 272 | return true 273 | }) 274 | return 275 | } 276 | func (g *generator) createNodeRecursive(expr ast.Expr, parent *structure.Node, 277 | importMap map[string]string, dotStructs map[string]*structure.Structure, sameHierarchyStructs map[string]bool, 278 | importPath, packageName string) (*structure.Node, bool, []string) { 279 | 280 | reasons := make([]string, 0) 281 | if ident, ok := expr.(*ast.Ident); ok { 282 | // dot import 283 | if dot, found := dotStructs[ident.Name]; found { 284 | return structure.CreateStructNode(dot.ImportPath, dot.Name, ident.Name, parent), true, reasons 285 | } 286 | // time 287 | if ident.Name == "Time" { 288 | return structure.CreateStructNode("time", "time", ident.Name, parent), true, reasons 289 | } 290 | // same hierarchy struct 291 | if ident.Obj != nil && ident.Obj.Kind == ast.Typ { 292 | return structure.CreateStructNode(importPath, packageName, ident.Name, parent), true, reasons 293 | } 294 | 295 | // same hierarchy struct 296 | if _, found := sameHierarchyStructs[ident.Name]; found { 297 | return structure.CreateStructNode(importPath, packageName, ident.Name, parent), true, reasons 298 | } 299 | 300 | if structure.IsPrimitive(ident.Name) { 301 | return structure.CreateIdentNode(ident, parent), true, reasons 302 | } 303 | return nil, false, []string{fmt.Sprintf("identifier %s is not suppoted or unknown struct ", ident.Name)} 304 | } 305 | 306 | // struct 307 | if selector, ok := expr.(*ast.SelectorExpr); ok { 308 | pkgName := fmt.Sprint(selector.X) 309 | return structure.CreateStructNode(importMap[pkgName], pkgName, selector.Sel.Name, parent), true, reasons 310 | } 311 | 312 | // slice or array 313 | if array, ok := expr.(*ast.ArrayType); ok { 314 | var node *structure.Node 315 | if array.Len == nil { 316 | node = structure.CreateSliceNode(parent) 317 | } else { 318 | lit := array.Len.(*ast.BasicLit) 319 | // parse num 320 | n := new(big.Int) 321 | if litValue := strings.ToLower(lit.Value); strings.HasPrefix(litValue, "0b") { 322 | n.SetString(strings.ReplaceAll(litValue, "0b", ""), 2) 323 | } else if strings.HasPrefix(litValue, "0o") { 324 | n.SetString(strings.ReplaceAll(litValue, "0o", ""), 8) 325 | } else if strings.HasPrefix(litValue, "0x") { 326 | n.SetString(strings.ReplaceAll(litValue, "0x", ""), 16) 327 | } else { 328 | n.SetString(litValue, 10) 329 | } 330 | node = structure.CreateArrayNode(n.Uint64(), parent) 331 | } 332 | key, check, rs := g.createNodeRecursive(array.Elt, node, importMap, dotStructs, sameHierarchyStructs, importPath, packageName) 333 | node.SetKeyNode(key) 334 | reasons = append(reasons, rs...) 335 | return node, check, reasons 336 | } 337 | 338 | // map 339 | if mp, ok := expr.(*ast.MapType); ok { 340 | node := structure.CreateMapNode(parent) 341 | key, c1, krs := g.createNodeRecursive(mp.Key, node, importMap, dotStructs, sameHierarchyStructs, importPath, packageName) 342 | value, c2, vrs := g.createNodeRecursive(mp.Value, node, importMap, dotStructs, sameHierarchyStructs, importPath, packageName) 343 | node.SetKeyNode(key) 344 | node.SetValueNode(value) 345 | reasons = append(reasons, krs...) 346 | reasons = append(reasons, vrs...) 347 | return node, c1 && c2, reasons 348 | } 349 | 350 | // * 351 | if star, ok := expr.(*ast.StarExpr); ok { 352 | node := structure.CreatePointerNode(parent) 353 | key, check, rs := g.createNodeRecursive(star.X, node, importMap, dotStructs, sameHierarchyStructs, importPath, packageName) 354 | node.SetKeyNode(key) 355 | reasons = append(reasons, rs...) 356 | return node, check, reasons 357 | } 358 | 359 | // not supported 360 | if _, ok := expr.(*ast.InterfaceType); ok { 361 | return nil, false, []string{"interface type is not supported"} 362 | } 363 | if _, ok := expr.(*ast.StructType); ok { 364 | return nil, false, []string{"inner struct is not supported"} 365 | } 366 | if _, ok := expr.(*ast.ChanType); ok { 367 | return nil, false, []string{"chan type is not supported"} 368 | } 369 | if _, ok := expr.(*ast.FuncType); ok { 370 | return nil, false, []string{"func type is not supported"} 371 | } 372 | 373 | // unreachable 374 | return nil, false, []string{"this field is unknown field"} 375 | } 376 | 377 | func (g *generator) createAnalyzedFields(packageName, structName string, analyzedFieldMap map[string]*structure.Node, fset *token.FileSet, file *ast.File) ([]structure.Field, error) { 378 | 379 | // todo : should solve import check, but can not solve now 380 | // see below - https://github.com/golang/go/issues/13847 381 | // see also - https://github.com/golang/lint/blob/master/lint.go#L267 382 | conf := types.Config{ 383 | Importer: importer.Default(), // gcexportdata.NewImporter(fset, make(map[string]*types.Package)), 384 | Error: func(err error) {}, 385 | } 386 | 387 | info := &types.Info{ 388 | Types: make(map[ast.Expr]types.TypeAndValue), 389 | Defs: make(map[*ast.Ident]types.Object), 390 | Uses: make(map[*ast.Ident]types.Object), 391 | Scopes: make(map[ast.Node]*types.Scope), 392 | } 393 | 394 | pkg, _ /*err*/ := conf.Check(packageName, fset, []*ast.File{file}, info) 395 | //if err != nil { 396 | // // Consider reporting these errors when golint operates on entire packages 397 | // // https://github.com/golang/lint/blob/master/lint.go#L153 398 | //} 399 | 400 | obj := pkg.Scope().Lookup(structName) 401 | internal := obj.Type().Underlying().(*types.Struct) 402 | 403 | analyzedFields := make([]structure.Field, 0) 404 | tagNameCheck := map[string]bool{} 405 | for i := 0; i < internal.NumFields(); i++ { 406 | field := internal.Field(i) 407 | 408 | // fmt.Println(field.Id(), field.Type(), field.IsField()) 409 | 410 | if field.IsField() && field.Exported() { 411 | origin, _ := reflect.StructTag(internal.Tag(i)).Lookup("msgpack") 412 | tags := strings.Split(origin, ",") 413 | 414 | name := field.Id() 415 | tagName := name 416 | ignore := false 417 | for _, tag := range tags { 418 | if tag == "ignore" || tag == "-" { 419 | ignore = true 420 | } else if len(tag) > 0 { 421 | tagName = tag 422 | } 423 | } 424 | 425 | if ignore { 426 | continue 427 | } 428 | 429 | if _, found := tagNameCheck[tagName]; found { 430 | return nil, fmt.Errorf("duplicate tags %s.%s %s", packageName, structName, tagName) 431 | } 432 | tagNameCheck[tagName] = true 433 | 434 | analyzedFields = append(analyzedFields, structure.Field{ 435 | Name: name, 436 | Tag: tagName, 437 | Node: analyzedFieldMap[fmt.Sprint(i)+"@"+structName], 438 | }) 439 | } 440 | } 441 | 442 | return analyzedFields, nil 443 | } 444 | -------------------------------------------------------------------------------- /internal/generator/gen.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "go/ast" 7 | "go/token" 8 | "io" 9 | "os" 10 | "path/filepath" 11 | "runtime" 12 | "strings" 13 | 14 | . "github.com/dave/jennifer/jen" 15 | "github.com/shamaton/msgpackgen/internal/generator/ptn" 16 | "github.com/shamaton/msgpackgen/internal/generator/structure" 17 | ) 18 | 19 | var ( 20 | analyzedStructs []*structure.Structure 21 | structsInBrace []string 22 | ) 23 | 24 | type generator struct { 25 | fileSet *token.FileSet 26 | targetPackages map[string]bool 27 | parseFiles []*ast.File 28 | importPath2ParseFiles map[string][]*ast.File 29 | parseFile2ImportPath map[*ast.File]string 30 | importPath2package map[string]string 31 | noUserQualMap map[string]bool 32 | 33 | parseFile2ImportMap map[*ast.File]map[string]string 34 | parseFile2DotImportMap map[*ast.File]map[string]*structure.Structure 35 | 36 | outputDir string 37 | outputImportPath string 38 | outputJenFilePath string 39 | 40 | goModFilePath string 41 | goModModuleName string 42 | 43 | pointer int 44 | useGopath bool 45 | verbose bool 46 | strict bool 47 | } 48 | 49 | // Run runs code analyzing and generation. 50 | func Run(inputDir, inputFile, outDir, fileName string, pointer int, useGopath, dryRun, strict, verbose bool, w io.Writer) error { 51 | 52 | // can not input at same time 53 | if len(inputFile) > 0 && inputDir != "." { 54 | return fmt.Errorf("can not input directory and file at same time") 55 | } 56 | 57 | input := inputDir 58 | isInputDir := true 59 | if len(inputFile) < 1 { 60 | fi, err := os.Stat(inputDir) 61 | if err != nil { 62 | return fmt.Errorf("input directory error. os.Stat says %v", err) 63 | } 64 | if !fi.IsDir() { 65 | return fmt.Errorf("this(%s) path is not directory", inputDir) 66 | } 67 | } else { 68 | fi, err := os.Stat(inputFile) 69 | if err != nil { 70 | return fmt.Errorf("input file error. os.Stat says %v", err) 71 | } 72 | if fi.IsDir() { 73 | return fmt.Errorf("this(%s) is a directory", inputFile) 74 | } 75 | if !strings.HasSuffix(inputFile, ".go") { 76 | return fmt.Errorf("this(%s) is not .go file", inputFile) 77 | } 78 | input = inputFile 79 | isInputDir = false 80 | } 81 | 82 | if outDir == "" { 83 | outDir = inputDir 84 | } 85 | 86 | if pointer < 0 { 87 | pointer = 1 88 | } 89 | 90 | analyzedStructs = make([]*structure.Structure, 0) 91 | structsInBrace = make([]string, 0) 92 | g := generator{ 93 | useGopath: useGopath, 94 | pointer: pointer, 95 | strict: strict, 96 | verbose: verbose, 97 | targetPackages: map[string]bool{}, 98 | parseFiles: []*ast.File{}, 99 | importPath2package: map[string]string{}, 100 | importPath2ParseFiles: map[string][]*ast.File{}, 101 | parseFile2ImportPath: map[*ast.File]string{}, 102 | noUserQualMap: map[string]bool{}, 103 | 104 | parseFile2ImportMap: map[*ast.File]map[string]string{}, 105 | parseFile2DotImportMap: map[*ast.File]map[string]*structure.Structure{}, 106 | } 107 | return g.run(input, outDir, fileName, isInputDir, dryRun, w) 108 | } 109 | 110 | func (g *generator) run(input, out, fileName string, isInputDir, dryRun bool, w io.Writer) error { 111 | g.fileSet = token.NewFileSet() 112 | 113 | if !g.useGopath { 114 | modFilePath, err := g.searchGoModFile(input, isInputDir) 115 | if err != nil { 116 | return err 117 | } 118 | g.goModFilePath = modFilePath 119 | 120 | err = g.setModuleName() 121 | if err != nil { 122 | return err 123 | } 124 | } 125 | 126 | err := g.setOutputInfo(out) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | var filePaths []string 132 | if isInputDir { 133 | filePaths, err = g.getTargetFiles(input, true) 134 | if err != nil { 135 | return err 136 | } 137 | if len(filePaths) < 1 { 138 | return fmt.Errorf("not found go File") 139 | } 140 | } else { 141 | filePaths, err = g.getAbsolutePaths([]string{input}) 142 | if err != nil { 143 | return err 144 | } 145 | } 146 | 147 | err = g.getPackages(filePaths) 148 | if err != nil { 149 | return err 150 | } 151 | 152 | err = g.analyze() 153 | if err != nil { 154 | return err 155 | } 156 | 157 | var reasons []string 158 | analyzedStructs, reasons = g.filter(analyzedStructs, reasons) 159 | 160 | g.printAnalyzedResult(reasons) 161 | 162 | g.setOthers() 163 | f := g.generateCode() 164 | 165 | if dryRun { 166 | _, err = fmt.Fprintf(w, "%#v", f) 167 | return err 168 | } 169 | err = g.output(f, fileName) 170 | if err != nil { 171 | return err 172 | } 173 | return nil 174 | } 175 | 176 | func (g *generator) searchGoModFile(input string, isInputDir bool) (string, error) { 177 | goModFilePath := "" 178 | 179 | dir := input 180 | if !isInputDir { 181 | dir = filepath.Dir(input) 182 | } 183 | 184 | path, err := filepath.Abs(dir) 185 | if err != nil { 186 | return goModFilePath, err 187 | } 188 | 189 | files, err := os.ReadDir(path) 190 | if err != nil { 191 | return goModFilePath, err 192 | } 193 | 194 | for _, file := range files { 195 | if file.IsDir() { 196 | continue 197 | } 198 | 199 | if file.Name() == "go.mod" { 200 | goModFilePath = filepath.Join(path, file.Name()) 201 | } 202 | } 203 | 204 | // recursive upper 205 | if goModFilePath == "" { 206 | // upper path 207 | sep := string(filepath.Separator) 208 | upper := filepath.Join(path, fmt.Sprintf("%s..%s", sep, sep)) 209 | 210 | // reached root 211 | if path == upper { 212 | return goModFilePath, fmt.Errorf("not found go.mod") 213 | } 214 | return g.searchGoModFile(upper, true) 215 | } 216 | return goModFilePath, nil 217 | } 218 | 219 | func (g *generator) setModuleName() error { 220 | 221 | file, err := os.Open(g.goModFilePath) 222 | if err != nil { 223 | return err 224 | } 225 | defer func() { 226 | err := file.Close() 227 | if err != nil { 228 | fmt.Println("File close error", err) 229 | } 230 | }() 231 | 232 | scanner := bufio.NewScanner(file) 233 | 234 | if scanner.Scan() { 235 | text := scanner.Text() 236 | if !strings.HasPrefix(text, "module") { 237 | return fmt.Errorf("not found module name in go.mod") 238 | } 239 | 240 | results := strings.Split(text, " ") 241 | if len(results) != 2 { 242 | return fmt.Errorf("something wrong in go.mod \n %s", text) 243 | } 244 | g.goModModuleName = results[1] 245 | } 246 | 247 | if err = scanner.Err(); err != nil { 248 | return err 249 | } 250 | return nil 251 | } 252 | 253 | func (g *generator) setOutputInfo(out string) error { 254 | 255 | outAbs, err := filepath.Abs(out) 256 | if err != nil { 257 | return err 258 | } 259 | g.outputDir = outAbs 260 | 261 | importPath, err := g.getImportPath(g.outputDir) 262 | if err != nil { 263 | return err 264 | } 265 | g.outputImportPath = importPath 266 | g.outputJenFilePath = fmt.Sprintf("%s/%s", filepath.Dir(importPath), filepath.Base(importPath)) 267 | 268 | // if exist go file 269 | fi, err := os.Stat(outAbs) 270 | if err != nil { 271 | // end proc. 272 | return nil 273 | } 274 | if !fi.IsDir() { 275 | return fmt.Errorf("this(%s) path is not directory", out) 276 | } 277 | 278 | files, err := g.getTargetFiles(outAbs, false) 279 | if err != nil { 280 | return err 281 | } 282 | if len(files) < 1 { 283 | return nil 284 | } 285 | _, packageName, _, err := g.getImportPathAndParseFile(files[0]) 286 | if err != nil { 287 | return err 288 | } 289 | g.outputJenFilePath = fmt.Sprintf("%s/%s", filepath.Dir(importPath), packageName) 290 | return nil 291 | } 292 | 293 | func (g *generator) getImportPath(path string) (string, error) { 294 | if !g.useGopath { 295 | rep := strings.Replace(path, filepath.Dir(g.goModFilePath), g.goModModuleName, 1) 296 | return filepath.ToSlash(rep), nil 297 | } 298 | 299 | // use GOPATH option 300 | goPathAll := os.Getenv("GOPATH") 301 | sep := ":" 302 | if runtime.GOOS == "windows" { 303 | sep = ";" 304 | } 305 | goPaths := strings.Split(goPathAll, sep) 306 | 307 | p := filepath.ToSlash(path) 308 | for _, goPath := range goPaths { 309 | gp := filepath.ToSlash(goPath) + "/src/" 310 | if !strings.HasPrefix(p, gp) { 311 | continue 312 | } 313 | paths := strings.SplitN(p, gp, 2) 314 | return paths[1], nil 315 | } 316 | return "", fmt.Errorf("path %s is outside gopath", path) 317 | } 318 | 319 | func (g *generator) getTargetFiles(dir string, recursive bool) ([]string, error) { 320 | files, err := os.ReadDir(dir) 321 | if err != nil { 322 | return nil, err 323 | } 324 | 325 | var paths []string 326 | for _, file := range files { 327 | if file.IsDir() && recursive { 328 | if n := file.Name(); strings.HasPrefix(n, ".") || 329 | strings.HasPrefix(n, "_") || 330 | n == "testdata" || 331 | n == "vendor" { 332 | if g.verbose { 333 | fmt.Printf("%s is not covered directory. skipping. \n", n) 334 | } 335 | continue 336 | } 337 | 338 | path, err := g.getTargetFiles(filepath.Join(dir, file.Name()), recursive) 339 | if err != nil { 340 | return nil, err 341 | } 342 | paths = append(paths, path...) 343 | continue 344 | } 345 | if filepath.Ext(file.Name()) == ".go" && !strings.HasSuffix(file.Name(), "_test.go") { 346 | paths = append(paths, filepath.Join(dir, file.Name())) 347 | } 348 | } 349 | 350 | absPaths, err := g.getAbsolutePaths(paths) 351 | if err != nil { 352 | return nil, err 353 | } 354 | return absPaths, nil 355 | } 356 | 357 | func (g *generator) getAbsolutePaths(paths []string) ([]string, error) { 358 | absPaths := make([]string, len(paths)) 359 | for i, path := range paths { 360 | abs, err := filepath.Abs(path) 361 | if err != nil { 362 | return nil, err 363 | } 364 | absPaths[i] = abs 365 | } 366 | return absPaths, nil 367 | } 368 | 369 | func (g *generator) filter(structures []*structure.Structure, reasons []string) ([]*structure.Structure, []string) { 370 | newStructs := make([]*structure.Structure, 0) 371 | allOk := true 372 | for _, v := range structures { 373 | ok := true 374 | 375 | var rs []string 376 | if v.CanGen { 377 | for _, field := range v.Fields { 378 | if canGen, msgs := field.Node.CanGenerate(structures); !canGen { 379 | ok = false 380 | rs = msgs 381 | } 382 | } 383 | if ok { 384 | newStructs = append(newStructs, v) 385 | } 386 | } else { 387 | ok = false 388 | rs = v.Reasons 389 | } 390 | 391 | if !ok { 392 | reasons = append(reasons, fmt.Sprintf("notgen:%s.%s", v.ImportPath, v.Name)) 393 | reasons = append(reasons, "reason") 394 | for i, s := range rs { 395 | mark := " |- " 396 | if i == len(rs)-1 { 397 | mark = " `- " 398 | } 399 | reasons = append(reasons, mark+s) 400 | } 401 | } 402 | 403 | allOk = allOk && ok 404 | } 405 | if !allOk { 406 | return g.filter(newStructs, reasons) 407 | } 408 | return newStructs, reasons 409 | } 410 | 411 | func (g *generator) setOthers() { 412 | for i := range analyzedStructs { 413 | analyzedStructs[i].Others = analyzedStructs 414 | } 415 | } 416 | 417 | func (g *generator) generateCode() *File { 418 | 419 | f := NewFilePath(g.outputJenFilePath) 420 | 421 | registerName := "RegisterGeneratedResolver" 422 | f.HeaderComment("// Code generated by msgpackgen. DO NOT EDIT.") 423 | f.Comment(fmt.Sprintf("// %s registers generated resolver.\n", registerName)). 424 | Func().Id(registerName).Params().Block( 425 | Qual(ptn.PkTop, "SetResolver").Call( 426 | Id(ptn.PrivateFuncName("encodeAsMap")), 427 | Id(ptn.PrivateFuncName("encodeAsArray")), 428 | Id(ptn.PrivateFuncName("decodeAsMap")), 429 | Id(ptn.PrivateFuncName("decodeAsArray")), 430 | ), 431 | ) 432 | 433 | encReturn := Return(Nil(), Nil()) 434 | decReturn := Return(False(), Nil()) 435 | if g.strict { 436 | encReturn = Return(Nil(), Qual("fmt", "Errorf").Call(Lit("use strict option : undefined type"))) 437 | decReturn = Return(False(), Qual("fmt", "Errorf").Call(Lit("use strict option : undefined type"))) 438 | } 439 | 440 | encodeAsArrayCode := []Code{encReturn} 441 | encodeAsMapCode := []Code{encReturn} 442 | decodeAsArrayCode := []Code{decReturn} 443 | decodeAsMapCode := []Code{decReturn} 444 | if len(analyzedStructs) > 0 { 445 | encodeAsArrayCode = append([]Code{ 446 | Switch(Id("v").Op(":=").Id("i").Assert(Type())).Block( 447 | g.encodeAsArrayCases()..., 448 | )}, 449 | encodeAsArrayCode..., 450 | ) 451 | encodeAsMapCode = append([]Code{ 452 | Switch(Id("v").Op(":=").Id("i").Assert(Type())).Block( 453 | g.encodeAsMapCases()..., 454 | )}, 455 | encodeAsMapCode..., 456 | ) 457 | decodeAsArrayCode = append([]Code{ 458 | Switch(Id("v").Op(":=").Id("i").Assert(Type())).Block( 459 | g.decodeAsArrayCases()..., 460 | )}, 461 | decodeAsArrayCode..., 462 | ) 463 | decodeAsMapCode = append([]Code{ 464 | Switch(Id("v").Op(":=").Id("i").Assert(Type())).Block( 465 | g.decodeAsMapCases()..., 466 | )}, 467 | decodeAsMapCode..., 468 | ) 469 | } 470 | 471 | g.encodeTopTemplate("encode", f).Block( 472 | If(Qual(ptn.PkTop, "StructAsArray").Call()).Block( 473 | Return(Id(ptn.PrivateFuncName("encodeAsArray")).Call(Id("i"))), 474 | ).Else().Block( 475 | Return(Id(ptn.PrivateFuncName("encodeAsMap")).Call(Id("i"))), 476 | ), 477 | ) 478 | 479 | g.encodeTopTemplate("encodeAsArray", f).Block(encodeAsArrayCode...) 480 | g.encodeTopTemplate("encodeAsMap", f).Block(encodeAsMapCode...) 481 | 482 | g.decodeTopTemplate("decode", f).Block( 483 | If(Qual(ptn.PkTop, "StructAsArray").Call()).Block( 484 | Return(Id(ptn.PrivateFuncName("decodeAsArray")).Call(Id("data"), Id("i"))), 485 | ).Else().Block( 486 | Return(Id(ptn.PrivateFuncName("decodeAsMap")).Call(Id("data"), Id("i"))), 487 | ), 488 | ) 489 | 490 | g.decodeTopTemplate("decodeAsArray", f).Block(decodeAsArrayCode...) 491 | g.decodeTopTemplate("decodeAsMap", f).Block(decodeAsMapCode...) 492 | 493 | for _, st := range analyzedStructs { 494 | st.CreateCode(f) 495 | } 496 | 497 | return f 498 | } 499 | 500 | func (g *generator) output(f *File, genFileName string) error { 501 | 502 | if err := os.MkdirAll(g.outputDir, 0777); err != nil { 503 | return err 504 | } 505 | 506 | path := g.outputDir + string(filepath.Separator) + genFileName 507 | file, err := os.Create(filepath.FromSlash(path)) 508 | if err != nil { 509 | return err 510 | } 511 | defer func() { 512 | err := file.Close() 513 | if err != nil { 514 | fmt.Println("File close error", err) 515 | } 516 | }() 517 | 518 | _, err = fmt.Fprintf(file, "%#v", f) 519 | if err != nil { 520 | return err 521 | } 522 | 523 | p := genFileName 524 | if g.verbose { 525 | p = path 526 | } 527 | fmt.Println(p, "generated.") 528 | return err 529 | } 530 | 531 | func (g *generator) printAnalyzedResult(reasons []string) { 532 | if !g.verbose { 533 | return 534 | } 535 | 536 | fmt.Printf("=========== %d generated ==========\n", len(analyzedStructs)) 537 | for _, v := range analyzedStructs { 538 | fmt.Println(v.ImportPath, v.Name) 539 | } 540 | 541 | notGen := 0 542 | for _, s := range reasons { 543 | if strings.Contains(s, "notgen:") { 544 | notGen++ 545 | } 546 | } 547 | fmt.Printf("=========== %d not generated ==========\n", notGen+len(structsInBrace)) 548 | for _, s := range structsInBrace { 549 | fmt.Println(s) 550 | fmt.Println("reason") 551 | fmt.Println(" └ defined in function") 552 | } 553 | for _, s := range reasons { 554 | if strings.Contains(s, "notgen:") { 555 | fmt.Println(strings.ReplaceAll(s, "notgen:", "")) 556 | } else { 557 | fmt.Println(s) 558 | } 559 | } 560 | fmt.Println("=========================================") 561 | } 562 | 563 | func (g *generator) decodeTopTemplate(name string, f *File) *Statement { 564 | return f.Comment(fmt.Sprintf("// %s\n", name)). 565 | Func().Id(ptn.PrivateFuncName(name)).Params(Id("data").Index().Byte(), Id("i").Interface()).Params(Bool(), Error()) 566 | } 567 | 568 | func (g *generator) encodeTopTemplate(name string, f *File) *Statement { 569 | return f.Comment(fmt.Sprintf("// %s\n", name)). 570 | Func().Id(ptn.PrivateFuncName(name)).Params(Id("i").Interface()).Params(Index().Byte(), Error()) 571 | } 572 | 573 | func (g *generator) encodeAsArrayCases() []Code { 574 | var states, pointers []Code 575 | for _, v := range analyzedStructs { 576 | s, p := g.encodeCaseCode(v, true) 577 | states = append(states, s...) 578 | pointers = append(pointers, p...) 579 | } 580 | return append(states, pointers...) 581 | } 582 | 583 | func (g *generator) encodeAsMapCases() []Code { 584 | var states, pointers []Code 585 | for _, v := range analyzedStructs { 586 | s, p := g.encodeCaseCode(v, false) 587 | states = append(states, s...) 588 | pointers = append(pointers, p...) 589 | } 590 | return append(states, pointers...) 591 | } 592 | 593 | func (g *generator) encodeCaseCode(v *structure.Structure, asArray bool) (states []Code, pointers []Code) { 594 | 595 | var caseStatement func(string) *Statement 596 | var errID *Statement 597 | if v.NoUseQual { 598 | caseStatement = func(op string) *Statement { return Op(op).Id(v.Name) } 599 | errID = Lit(v.Name) 600 | } else { 601 | caseStatement = func(op string) *Statement { return Op(op).Qual(v.ImportPath, v.Name) } 602 | errID = Lit(v.ImportPath + "." + v.Name) 603 | } 604 | 605 | var calcFuncName, encodeFuncName, pointerFuncName string 606 | if asArray { 607 | calcFuncName = v.CalcArraySizeFuncName() 608 | encodeFuncName = v.EncodeArrayFuncName() 609 | pointerFuncName = "encodeAsArray" 610 | } else { 611 | calcFuncName = v.CalcMapSizeFuncName() 612 | encodeFuncName = v.EncodeMapFuncName() 613 | pointerFuncName = "encodeAsMap" 614 | } 615 | 616 | f := func(ptr string) *Statement { 617 | return Case(caseStatement(ptr)).Block( 618 | Id(ptn.IdEncoder).Op(":=").Qual(ptn.PkEnc, "NewEncoder").Call(), 619 | List(Id("size"), Err()).Op(":=").Id(calcFuncName).Call(Id(ptr+"v"), Id(ptn.IdEncoder)), 620 | If(Err().Op("!=").Nil()).Block( 621 | Return(Nil(), Err()), 622 | ), 623 | Id(ptn.IdEncoder).Dot("MakeBytes").Call(Id("size")), 624 | List(Id("b"), Id("offset"), Err()).Op(":=").Id(encodeFuncName).Call(Id(ptr+"v"), Id(ptn.IdEncoder), Lit(0)), 625 | If(Err().Op("!=").Nil()).Block( 626 | Return(Nil(), Err()), 627 | ), 628 | If(Id("size").Op("!=").Id("offset")).Block( 629 | Return(Nil(), Qual("fmt", "Errorf").Call(Lit("%s size / offset different %d : %d"), errID, Id("size"), Id("offset"))), 630 | ), 631 | Return(Id("b"), Err()), 632 | ) 633 | } 634 | 635 | states = append(states, f("")) 636 | 637 | if g.pointer > 0 { 638 | states = append(states, f("*")) 639 | } 640 | 641 | for i := 0; i < g.pointer-1; i++ { 642 | ptr := strings.Repeat("*", i+2) 643 | pointers = append(pointers, Case(caseStatement(ptr)).Block( 644 | Return(Id(ptn.PrivateFuncName(pointerFuncName)).Call(Id("*v"))), 645 | )) 646 | } 647 | return 648 | } 649 | 650 | func (g *generator) decodeAsArrayCases() []Code { 651 | var states, pointers []Code 652 | for _, v := range analyzedStructs { 653 | s, p := g.decodeCaseCode(v, true) 654 | states = append(states, s...) 655 | pointers = append(pointers, p...) 656 | } 657 | return append(states, pointers...) 658 | } 659 | 660 | func (g *generator) decodeAsMapCases() []Code { 661 | var states, pointers []Code 662 | for _, v := range analyzedStructs { 663 | s, p := g.decodeCaseCode(v, false) 664 | states = append(states, s...) 665 | pointers = append(pointers, p...) 666 | } 667 | return append(states, pointers...) 668 | } 669 | 670 | func (g *generator) decodeCaseCode(v *structure.Structure, asArray bool) (states []Code, pointers []Code) { 671 | 672 | var caseStatement func(string) *Statement 673 | if v.NoUseQual { 674 | caseStatement = func(op string) *Statement { return Op(op).Id(v.Name) } 675 | } else { 676 | caseStatement = func(op string) *Statement { return Op(op).Qual(v.ImportPath, v.Name) } 677 | } 678 | 679 | var decodeFuncName, pointerFuncName string 680 | if asArray { 681 | decodeFuncName = v.DecodeArrayFuncName() 682 | pointerFuncName = "decodeAsArray" 683 | } else { 684 | 685 | decodeFuncName = v.DecodeMapFuncName() 686 | pointerFuncName = "decodeAsMap" 687 | } 688 | 689 | states = append(states, Case(caseStatement("*")).Block( 690 | Id(ptn.IdDecoder).Op(":=").Qual(ptn.PkDec, "NewDecoder").Call(Id("data")), 691 | List(Id("offset"), Err()).Op(":=").Id(decodeFuncName).Call(Id("v"), Id(ptn.IdDecoder), Id("0")), 692 | If(Err().Op("==").Nil().Op("&&").Id("offset").Op("!=").Id(ptn.IdDecoder).Dot("Len").Call()).Block( 693 | Return(True(), Qual("fmt", "Errorf").Call(Lit("read length is different [%d] [%d] "), Id("offset"), Id(ptn.IdDecoder).Dot("Len").Call())), 694 | ), 695 | Return(True(), Err()))) 696 | 697 | if g.pointer > 0 { 698 | states = append(states, Case(caseStatement("**")).Block( 699 | Id(ptn.IdDecoder).Op(":=").Qual(ptn.PkDec, "NewDecoder").Call(Id("data")), 700 | List(Id("offset"), Err()).Op(":=").Id(decodeFuncName).Call(Id("*v"), Id(ptn.IdDecoder), Id("0")), 701 | If(Err().Op("==").Nil().Op("&&").Id("offset").Op("!=").Id(ptn.IdDecoder).Dot("Len").Call()).Block( 702 | Return(True(), Qual("fmt", "Errorf").Call(Lit("read length is different [%d] [%d] "), Id("offset"), Id(ptn.IdDecoder).Dot("Len").Call())), 703 | ), 704 | Return(True(), Err()))) 705 | } 706 | 707 | for i := 0; i < g.pointer-1; i++ { 708 | ptr := strings.Repeat("*", i+3) 709 | pointers = append(pointers, Case(caseStatement(ptr)).Block( 710 | Return(Id(ptn.PrivateFuncName(pointerFuncName)).Call(Id("data"), Id("*v"))), 711 | )) 712 | } 713 | return 714 | } 715 | -------------------------------------------------------------------------------- /msgpack_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "math" 7 | "math/rand" 8 | "reflect" 9 | "runtime" 10 | "strings" 11 | "testing" 12 | "time" 13 | 14 | "github.com/shamaton/msgpack/v2/def" 15 | define2 "github.com/shamaton/msgpackgen/internal/fortest/define" 16 | "github.com/shamaton/msgpackgen/internal/fortest/define/define" 17 | "github.com/shamaton/msgpackgen/msgpack" 18 | ) 19 | 20 | func TestSwitchDefaultBehaviour(t *testing.T) { 21 | msgpack.SetStructAsArray(false) 22 | 23 | v := inside{Int: 1} 24 | b1, err := msgpack.Marshal(v) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | msgpack.SetStructAsArray(true) 30 | 31 | b2, err := msgpack.Marshal(v) 32 | if err != nil { 33 | t.Error(err) 34 | } 35 | msgpack.SetStructAsArray(false) 36 | 37 | if b1[0] != 0x81 || b2[0] != 0x91 { 38 | t.Fatalf("format may be different 0x%x, 0x%x", b1[0], b2[0]) 39 | } 40 | 41 | } 42 | 43 | func TestInt(t *testing.T) { 44 | v := testingValue{Int: -8, Int8: math.MinInt8, Int16: math.MinInt16} 45 | if err := checkValue(v); err != nil { 46 | t.Error(err) 47 | } 48 | v = testingValue{Int: -108, Int8: math.MaxInt8, Int16: math.MaxInt16} 49 | if err := checkValue(v); err != nil { 50 | t.Error(err) 51 | } 52 | v = testingValue{Int: -30108, Int32: math.MinInt32, Int64: math.MinInt64} 53 | if err := checkValue(v); err != nil { 54 | t.Error(err) 55 | } 56 | v = testingValue{Int: -1030108, Int32: math.MaxInt32, Int64: math.MaxInt64} 57 | if err := checkValue(v); err != nil { 58 | t.Error(err) 59 | } 60 | v = testingValue{ 61 | Int: math.MinInt64 + 12345, 62 | Abcdefghijabcdefghijabcdefghijabcdefghij: rand.Int(), 63 | AbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghijAbcdefghij: rand.Int(), 64 | } 65 | if err := checkValue(v); err != nil { 66 | t.Error(err) 67 | } 68 | if v.Function() != v.Int*2 { 69 | t.Errorf("value different %d, %d", v.Function(), v.Int*2) 70 | } 71 | 72 | { 73 | var r testingInt 74 | r.I = 1234 75 | err := msgpack.UnmarshalAsArray([]byte{0x91, def.Nil}, &r) 76 | if err != nil { 77 | t.Error(err) 78 | } 79 | if r.I != 0 { 80 | t.Errorf("not equal %v", r) 81 | } 82 | 83 | err = msgpack.UnmarshalAsArray([]byte{0x91, def.True}, &r) 84 | if err == nil || !strings.Contains(err.Error(), "AsInt") { 85 | t.Error("something wrong", err) 86 | } 87 | } 88 | { 89 | var r testingInt 90 | f32 := testingFloat32{F: 2.345} 91 | b, err := msgpack.MarshalAsArray(f32) 92 | if err != nil { 93 | t.Error(err) 94 | } 95 | 96 | err = msgpack.UnmarshalAsArray(b, &r) 97 | if err != nil { 98 | t.Error(err) 99 | } 100 | 101 | if r.I != 2 { 102 | t.Error("different value", r.I) 103 | } 104 | } 105 | 106 | { 107 | var r testingInt 108 | f64 := testingFloat64{F: 6.789} 109 | b, err := msgpack.MarshalAsArray(f64) 110 | if err != nil { 111 | t.Error(err) 112 | } 113 | 114 | err = msgpack.UnmarshalAsArray(b, &r) 115 | if err != nil { 116 | t.Error(err) 117 | } 118 | 119 | if r.I != 6 { 120 | t.Error("different value", r.I) 121 | } 122 | } 123 | } 124 | 125 | func TestUint(t *testing.T) { 126 | v := testingValue{Uint: 8, Uint8: math.MaxUint8} 127 | if err := checkValue(v); err != nil { 128 | t.Error(err) 129 | } 130 | v = testingValue{Uint: 130, Uint16: math.MaxUint16} 131 | if err := checkValue(v); err != nil { 132 | t.Error(err) 133 | } 134 | v = testingValue{Uint: 30130, Uint32: math.MaxUint32} 135 | if err := checkValue(v); err != nil { 136 | t.Error(err) 137 | } 138 | v = testingValue{Uint: 1030130, Uint64: math.MaxUint64} 139 | if err := checkValue(v); err != nil { 140 | t.Error(err) 141 | } 142 | v = testingValue{Uint: math.MaxUint64 - 12345} 143 | if err := checkValue(v); err != nil { 144 | t.Error(err) 145 | } 146 | 147 | { 148 | vv := inside{Int: -1} 149 | var r testingUint 150 | b, err := msgpack.MarshalAsArray(vv) 151 | if err != nil { 152 | t.Error(err) 153 | } 154 | err = msgpack.UnmarshalAsArray(b, &r) 155 | if err != nil { 156 | t.Error(err) 157 | } 158 | if r.U != math.MaxUint64 { 159 | t.Errorf("not equal %v", r) 160 | } 161 | } 162 | 163 | { 164 | vv := inside{Int: math.MinInt8} 165 | var r testingUint 166 | b, err := msgpack.MarshalAsArray(vv) 167 | if err != nil { 168 | t.Error(err) 169 | } 170 | err = msgpack.UnmarshalAsArray(b, &r) 171 | if err != nil { 172 | t.Error(err) 173 | } 174 | if r.U != math.MaxUint64+math.MinInt8+1 { 175 | t.Errorf("not equal %v", r) 176 | } 177 | } 178 | 179 | { 180 | vv := inside{Int: math.MinInt16} 181 | var r testingUint 182 | b, err := msgpack.MarshalAsArray(vv) 183 | if err != nil { 184 | t.Error(err) 185 | } 186 | err = msgpack.UnmarshalAsArray(b, &r) 187 | if err != nil { 188 | t.Error(err) 189 | } 190 | if r.U != math.MaxUint64+math.MinInt16+1 { 191 | t.Errorf("not equal %v", r) 192 | } 193 | } 194 | 195 | { 196 | vv := inside{Int: math.MinInt32} 197 | var r testingUint 198 | b, err := msgpack.MarshalAsArray(vv) 199 | if err != nil { 200 | t.Error(err) 201 | } 202 | err = msgpack.UnmarshalAsArray(b, &r) 203 | if err != nil { 204 | t.Error(err) 205 | } 206 | if r.U != math.MaxUint64+math.MinInt32+1 { 207 | t.Errorf("not equal %v", r) 208 | } 209 | } 210 | { 211 | vv := inside{Int: math.MinInt32 - 1} 212 | var r testingUint 213 | b, err := msgpack.MarshalAsArray(vv) 214 | if err != nil { 215 | t.Error(err) 216 | } 217 | err = msgpack.UnmarshalAsArray(b, &r) 218 | if err != nil { 219 | t.Error(err) 220 | } 221 | if r.U != math.MaxUint64+math.MinInt32 { 222 | t.Errorf("not equal %v", r) 223 | } 224 | } 225 | { 226 | var r testingUint 227 | r.U = 1234 228 | err := msgpack.UnmarshalAsArray([]byte{0x91, def.Nil}, &r) 229 | if err != nil { 230 | t.Error(err) 231 | } 232 | if r.U != 0 { 233 | t.Errorf("not equal %v", r) 234 | } 235 | 236 | err = msgpack.UnmarshalAsArray([]byte{0x91, def.True}, &r) 237 | if err == nil { 238 | t.Errorf("error must occur") 239 | } 240 | if !strings.Contains(err.Error(), "AsUint") { 241 | t.Error(err) 242 | } 243 | } 244 | } 245 | 246 | func TestFloat(t *testing.T) { 247 | v := testingValue{Float32: 0, Float64: 0} 248 | if err := checkValue(v); err != nil { 249 | t.Error(err) 250 | } 251 | v = testingValue{Float32: -1, Float64: -1} 252 | if err := checkValue(v); err != nil { 253 | t.Error(err) 254 | } 255 | v = testingValue{Float32: math.SmallestNonzeroFloat32, Float64: math.SmallestNonzeroFloat64} 256 | if err := checkValue(v); err != nil { 257 | t.Error(err) 258 | } 259 | v = testingValue{Float32: math.MaxFloat32, Float64: math.MaxFloat64} 260 | if err := checkValue(v); err != nil { 261 | t.Error(err) 262 | } 263 | 264 | { 265 | vv := inside{Int: 1} 266 | var r testingFloat32 267 | b, err := msgpack.MarshalAsArray(vv) 268 | if err != nil { 269 | t.Error(err) 270 | } 271 | err = msgpack.UnmarshalAsArray(b, &r) 272 | if err != nil { 273 | t.Error(err) 274 | } 275 | if r.F != 1 { 276 | t.Errorf("not equal %v", r) 277 | } 278 | } 279 | { 280 | vv := inside{Int: -1} 281 | var r testingFloat32 282 | b, err := msgpack.MarshalAsArray(vv) 283 | if err != nil { 284 | t.Error(err) 285 | } 286 | err = msgpack.UnmarshalAsArray(b, &r) 287 | if err != nil { 288 | t.Error(err) 289 | } 290 | if r.F != -1 { 291 | t.Errorf("not equal %v", r) 292 | } 293 | } 294 | 295 | { 296 | vv := inside{Int: 1} 297 | var r testingFloat64 298 | b, err := msgpack.MarshalAsArray(vv) 299 | if err != nil { 300 | t.Error(err) 301 | } 302 | err = msgpack.UnmarshalAsArray(b, &r) 303 | if err != nil { 304 | t.Error(err) 305 | } 306 | if r.F != 1 { 307 | t.Errorf("not equal %v", r) 308 | } 309 | } 310 | { 311 | vv := inside{Int: -1} 312 | var r testingFloat64 313 | b, err := msgpack.MarshalAsArray(vv) 314 | if err != nil { 315 | t.Error(err) 316 | } 317 | err = msgpack.UnmarshalAsArray(b, &r) 318 | if err != nil { 319 | t.Error(err) 320 | } 321 | if r.F != -1 { 322 | t.Errorf("not equal %v", r) 323 | } 324 | } 325 | { 326 | vv := testingFloat32{F: 1.23} 327 | var r testingFloat64 328 | b, err := msgpack.MarshalAsArray(vv) 329 | if err != nil { 330 | t.Error(err) 331 | } 332 | err = msgpack.UnmarshalAsArray(b, &r) 333 | if err != nil { 334 | t.Error(err) 335 | } 336 | if float32(r.F) != 1.23 { 337 | t.Errorf("not equal %v", r) 338 | } 339 | } 340 | { 341 | var r testingFloat32 342 | err := msgpack.UnmarshalAsArray([]byte{0x91, def.Nil}, &r) 343 | if err != nil { 344 | t.Error(err) 345 | } 346 | if r.F != 0 { 347 | t.Errorf("not equal %v", r) 348 | } 349 | } 350 | { 351 | var r testingFloat64 352 | err := msgpack.UnmarshalAsArray([]byte{0x91, def.Nil}, &r) 353 | if err != nil { 354 | t.Error(err) 355 | } 356 | if r.F != 0 { 357 | t.Errorf("not equal %v", r) 358 | } 359 | } 360 | { 361 | var r testingFloat32 362 | err := msgpack.UnmarshalAsArray([]byte{0x91, def.True}, &r) 363 | if err == nil { 364 | t.Errorf("error must occur") 365 | } 366 | if !strings.Contains(err.Error(), "AsFloat32") { 367 | t.Error(err) 368 | } 369 | } 370 | { 371 | var r testingFloat64 372 | err := msgpack.UnmarshalAsArray([]byte{0x91, def.True}, &r) 373 | if err == nil { 374 | t.Errorf("error must occur") 375 | } 376 | if !strings.Contains(err.Error(), "AsFloat64") { 377 | t.Error(err) 378 | } 379 | } 380 | } 381 | 382 | func TestString(t *testing.T) { 383 | base := "abcdefghijklmnopqrstuvwxyz12345" 384 | v := testingValue{String: ""} 385 | if err := checkValue(v); err != nil { 386 | t.Error(err) 387 | } 388 | v = testingValue{String: strings.Repeat(base, 1)} 389 | if err := checkValue(v); err != nil { 390 | t.Error(err) 391 | } 392 | v = testingValue{String: strings.Repeat(base, 8)} 393 | if err := checkValue(v); err != nil { 394 | t.Error(err) 395 | } 396 | v = testingValue{String: strings.Repeat(base, (math.MaxUint16/len(base))-1)} 397 | if err := checkValue(v); err != nil { 398 | t.Error(err) 399 | } 400 | v = testingValue{String: strings.Repeat(base, (math.MaxUint16/len(base))+1)} 401 | if err := checkValue(v); err != nil { 402 | t.Error(err) 403 | } 404 | 405 | { 406 | var r testingString 407 | r.S = "setset" 408 | err := msgpack.UnmarshalAsArray([]byte{0x91, def.Nil}, &r) 409 | if err != nil { 410 | t.Error(err) 411 | } 412 | if r.S != "" { 413 | t.Errorf("not equal %v", r) 414 | } 415 | 416 | err = msgpack.UnmarshalAsArray([]byte{0x91, def.True}, &r) 417 | if err == nil || !strings.Contains(err.Error(), "StringByteLength") { 418 | t.Error("something wrong", err) 419 | } 420 | } 421 | } 422 | 423 | func TestBool(t *testing.T) { 424 | v := testingValue{Bool: true} 425 | if err := checkValue(v); err != nil { 426 | t.Error(err) 427 | } 428 | v = testingValue{Bool: false} 429 | if err := checkValue(v); err != nil { 430 | t.Error(err) 431 | } 432 | 433 | } 434 | 435 | func TestByteRune(t *testing.T) { 436 | v := testingValue{Byte: 127, Rune: 'a'} 437 | if err := checkValue(v); err != nil { 438 | t.Error(err) 439 | } 440 | 441 | { 442 | var r testingBool 443 | r.B = true 444 | err := msgpack.UnmarshalAsArray([]byte{0x91, def.Nil}, &r) 445 | if err != nil { 446 | t.Error(err) 447 | } 448 | if r.B != false { 449 | t.Errorf("not equal %v", r) 450 | } 451 | err = msgpack.UnmarshalAsArray([]byte{0x91, def.Uint8, 0x01}, &r) 452 | if err == nil { 453 | t.Errorf("error must occur") 454 | } 455 | if !strings.Contains(err.Error(), "AsBool") { 456 | t.Error(err) 457 | } 458 | } 459 | } 460 | 461 | func TestComplex(t *testing.T) { 462 | v := testingValue{Complex64: complex(1, 2), Complex128: complex(3, 4)} 463 | if err := checkValue(v); err != nil { 464 | t.Error(err) 465 | } 466 | v = testingValue{ 467 | Complex64: complex(math.MaxFloat32, math.SmallestNonzeroFloat32), 468 | Complex128: complex(math.MaxFloat64, math.SmallestNonzeroFloat64), 469 | } 470 | if err := checkValue(v); err != nil { 471 | t.Error(err) 472 | } 473 | msgpack.SetComplexTypeCode(-123) 474 | if err := checkValue(v); err != nil { 475 | t.Error(err) 476 | } 477 | 478 | b64, err := msgpack.MarshalAsArray(testingComplex64{}) 479 | if err != nil { 480 | t.Error(err) 481 | } 482 | b128, err := msgpack.MarshalAsArray(testingComplex128{}) 483 | if err != nil { 484 | t.Error(err) 485 | } 486 | 487 | msgpack.SetComplexTypeCode(-122) 488 | 489 | { 490 | var r testingComplex64 491 | err = msgpack.UnmarshalAsArray(b64, &r) 492 | if err == nil || !strings.Contains(err.Error(), "fixext8") { 493 | t.Error(err) 494 | } 495 | err := msgpack.UnmarshalAsArray([]byte{0x91, def.True}, &r) 496 | if err == nil { 497 | t.Errorf("error must occur") 498 | } 499 | if !strings.Contains(err.Error(), "AsComplex64") { 500 | t.Error(err) 501 | } 502 | } 503 | { 504 | var r testingComplex128 505 | err = msgpack.UnmarshalAsArray(b128, &r) 506 | if err == nil || !strings.Contains(err.Error(), "fixext16") { 507 | t.Error(err) 508 | } 509 | err := msgpack.UnmarshalAsArray([]byte{0x91, def.True}, &r) 510 | if err == nil { 511 | t.Errorf("error must occur") 512 | } 513 | if !strings.Contains(err.Error(), "AsComplex128") { 514 | t.Error(err) 515 | } 516 | } 517 | } 518 | 519 | func TestMap(t *testing.T) { 520 | v := testingValue{ 521 | MapIntInt: map[string]int{"1": 2, "3": 4, "5": 6, "7": 8, "9": 10}, 522 | } 523 | if err := checkValue(v); err != nil { 524 | t.Error(err) 525 | } 526 | v = testingValue{ 527 | MapIntInt: map[string]int{}, 528 | } 529 | for i := 0; i < 1000; i++ { 530 | v.MapIntInt[fmt.Sprint(i)] = i + 1 531 | } 532 | if err := checkValue(v); err != nil { 533 | t.Error(err) 534 | } 535 | 536 | v = testingValue{ 537 | MapIntInt: map[string]int{}, 538 | } 539 | for i := 0; i < math.MaxUint16+1; i++ { 540 | v.MapIntInt[fmt.Sprint(i)] = i + 1 541 | } 542 | if err := checkValue(v); err != nil { 543 | t.Error(err) 544 | } 545 | v = testingValue{ 546 | MapIntInt: nil, 547 | } 548 | if err := checkValue(v); err != nil { 549 | t.Error(err) 550 | } 551 | 552 | { 553 | var r testingMap 554 | err := msgpack.UnmarshalAsArray([]byte{0x91, def.True}, &r) 555 | if err == nil { 556 | t.Errorf("error must occur") 557 | } 558 | if !strings.Contains(err.Error(), "MapLength") { 559 | t.Error(err) 560 | } 561 | } 562 | } 563 | 564 | func TestPointerValue(t *testing.T) { 565 | { 566 | vv := 123 567 | v := testingValue{Pint: &vv} 568 | if err := checkValue(v); err != nil { 569 | t.Error(err) 570 | } 571 | } 572 | { 573 | vv := "this is pointer" 574 | vvv := &vv 575 | v := testingValue{P2string: &vvv} 576 | if err := checkValue(v); err != nil { 577 | t.Error(err) 578 | } 579 | } 580 | { 581 | vv := float32(1.234) 582 | vvv := &vv 583 | vvvv := &vvv 584 | vvvvv := &vvvv 585 | v := testingValue{P3float32: vvvvv} 586 | if err := checkValue(v); err != nil { 587 | t.Error(err) 588 | } 589 | } 590 | 591 | check := func(v testingValue) error { 592 | var v1, v2 testingValue 593 | f1 := func() (bool, interface{}, interface{}) { 594 | return v1.P3float32 == nil, v1.P3float32, nil 595 | } 596 | f2 := func() (bool, interface{}, interface{}) { 597 | return v2.P3float32 == nil, v1.P3float32, nil 598 | } 599 | 600 | return _checkValue(v, &v1, &v2, f1, f2) 601 | } 602 | { 603 | 604 | vv := float32(1.234) 605 | vvv := &vv 606 | vvvv := &vvv 607 | vvvvv := &vvvv 608 | 609 | vvv = nil 610 | v := testingValue{P3float32: vvvvv} 611 | if err := check(v); err != nil { 612 | t.Error(err) 613 | } 614 | vvvv = nil 615 | v = testingValue{P3float32: vvvvv} 616 | if err := check(v); err != nil { 617 | t.Error(err) 618 | } 619 | vvvvv = nil 620 | v = testingValue{P3float32: vvvvv} 621 | if err := check(v); err != nil { 622 | t.Error(err) 623 | } 624 | } 625 | { 626 | vv := make([]*int, 100) 627 | for i := range vv { 628 | if i%2 == 0 { 629 | vvv := i 630 | vv[i] = &vvv 631 | } else { 632 | vv[i] = nil 633 | } 634 | } 635 | 636 | v := testingValue{IntPointers: vv} 637 | if err := checkValue(v); err != nil { 638 | t.Error(err) 639 | } 640 | } 641 | 642 | check2 := func(v testingValue) error { 643 | var v1, v2 testingValue 644 | f1 := func() (bool, interface{}, interface{}) { 645 | mp := map[uint]string{} 646 | for kk, vv := range v1.MapPointers { 647 | vvv := *vv 648 | mp[*kk] = *vvv 649 | } 650 | for kk, vv := range v.MapPointers { 651 | vvv := *vv 652 | if str, ok := mp[*kk]; !ok || str != *vvv { 653 | return false, str, *vvv 654 | } 655 | } 656 | return true, nil, nil 657 | } 658 | 659 | f2 := func() (bool, interface{}, interface{}) { 660 | mp := map[uint]string{} 661 | for kk, vv := range v2.MapPointers { 662 | vvv := *vv 663 | mp[*kk] = *vvv 664 | } 665 | for kk, vv := range v.MapPointers { 666 | vvv := *vv 667 | if str, ok := mp[*kk]; !ok || str != *vvv { 668 | return false, str, *vvv 669 | } 670 | } 671 | return true, nil, nil 672 | } 673 | 674 | return _checkValue(v, &v1, &v2, f1, f2) 675 | } 676 | { 677 | vvv := make(map[*uint]**string, 10) 678 | for i := 0; i < 10; i++ { 679 | k := uint(i) 680 | v := fmt.Sprint(i, i, i) 681 | vv := &v 682 | vvv[&k] = &vv 683 | } 684 | 685 | v := testingValue{MapPointers: vvv} 686 | if err := check2(v); err != nil { 687 | t.Error(err) 688 | } 689 | } 690 | } 691 | 692 | func TestTime(t *testing.T) { 693 | { 694 | v := testingTime{Time: time.Now()} 695 | b, err := msgpack.Marshal(v) 696 | if err != nil { 697 | t.Error(err) 698 | } 699 | var vv testingTime 700 | err = msgpack.Unmarshal(b, &vv) 701 | if err != nil { 702 | t.Error(err) 703 | } 704 | if v.Time.UnixNano() != vv.Time.UnixNano() { 705 | t.Errorf("time different %v, %v", v.Time, vv.Time) 706 | } 707 | } 708 | { 709 | vv := testingTime{} 710 | b, err := msgpack.MarshalAsArray(vv) 711 | if err != nil { 712 | t.Error(err) 713 | } 714 | var vvv testingTime 715 | err = msgpack.UnmarshalAsArray(b, &vvv) 716 | if err != nil { 717 | t.Error(err) 718 | } 719 | if vv.Time.UnixNano() != vvv.Time.UnixNano() { 720 | t.Errorf("time different %v, %v", vv.Time, vvv.Time) 721 | } 722 | } 723 | { 724 | v := define2.AA{Time: time.Now()} 725 | b, err := msgpack.Marshal(v) 726 | if err != nil { 727 | t.Error(err) 728 | } 729 | var vv define2.AA 730 | err = msgpack.Unmarshal(b, &vv) 731 | if err != nil { 732 | t.Error(err) 733 | } 734 | if v.UnixNano() != vv.UnixNano() { 735 | t.Errorf("time different %v, %v", v.Time, vv.Time) 736 | } 737 | } 738 | { 739 | now := time.Now().Unix() 740 | nowByte := make([]byte, 4) 741 | binary.BigEndian.PutUint32(nowByte, uint32(now)) 742 | 743 | var r testingTime 744 | c := def.TimeStamp 745 | err := msgpack.UnmarshalAsArray(append([]byte{def.FixArray + 1, def.Fixext4, byte(c)}, nowByte...), &r) 746 | if err != nil { 747 | t.Error(err) 748 | } 749 | if r.Time.Unix() != now { 750 | t.Error("different time", r.Time.Unix(), now, nowByte) 751 | } 752 | 753 | _, err = msgpack.MarshalAsArray(r) 754 | if err != nil { 755 | t.Error(err) 756 | } 757 | 758 | err = msgpack.UnmarshalAsArray(append([]byte{def.FixArray + 1, def.Fixext4, 3}, nowByte...), &r) 759 | if err == nil || !strings.Contains(err.Error(), "fixext4. time type is different") { 760 | t.Error("something wrong", err) 761 | } 762 | 763 | err = msgpack.UnmarshalAsArray([]byte{def.FixArray + 1, def.Fixext8, 3}, &r) 764 | if err == nil || !strings.Contains(err.Error(), "fixext8. time type is different") { 765 | t.Error("something wrong", err) 766 | } 767 | 768 | err = msgpack.UnmarshalAsArray([]byte{def.FixArray + 1, def.Ext8, 11}, &r) 769 | if err == nil || !strings.Contains(err.Error(), "ext8. time ext length is different") { 770 | t.Error("something wrong", err) 771 | } 772 | 773 | err = msgpack.UnmarshalAsArray([]byte{def.FixArray + 1, def.Ext8, 12, 3}, &r) 774 | if err == nil || !strings.Contains(err.Error(), "ext8. time type is different") { 775 | t.Error("something wrong", err) 776 | } 777 | 778 | nanoByte := make([]byte, 64) 779 | for i := range nanoByte[:30] { 780 | nanoByte[i] = 0xff 781 | } 782 | b := append([]byte{def.FixArray + 1, def.Fixext8, byte(c)}, nanoByte...) 783 | err = msgpack.UnmarshalAsArray(b, &r) 784 | if err == nil || !strings.Contains(err.Error(), "in timestamp 64 formats") { 785 | t.Error(err) 786 | } 787 | 788 | nanoByte = make([]byte, 96) 789 | for i := range nanoByte[:32] { 790 | nanoByte[i] = 0xff 791 | } 792 | b = append([]byte{def.FixArray + 1, def.Ext8, byte(12), byte(c)}, nanoByte...) 793 | err = msgpack.UnmarshalAsArray(b, &r) 794 | if err == nil || !strings.Contains(err.Error(), "in timestamp 96 formats") { 795 | t.Error(err) 796 | } 797 | 798 | err = msgpack.UnmarshalAsArray([]byte{def.FixArray + 1, def.Fixext1}, &r) 799 | if err == nil || !strings.Contains(err.Error(), "AsDateTime") { 800 | t.Error("something wrong", err) 801 | } 802 | } 803 | } 804 | 805 | func TestTimePointer(t *testing.T) { 806 | now := time.Now() 807 | add := now.Add(1 * time.Minute) 808 | v := testingTimePointer{Time: &add} 809 | b, err := msgpack.Marshal(v) 810 | if err != nil { 811 | t.Error(err) 812 | } 813 | var r testingTimePointer 814 | err = msgpack.Unmarshal(b, &r) 815 | if err != nil { 816 | t.Error(err) 817 | } 818 | if v.Time.UnixNano() != r.Time.UnixNano() { 819 | t.Errorf("time different %v, %v", v.Time, r.Time) 820 | } 821 | 822 | vv := testingTimePointer{} 823 | b, err = msgpack.MarshalAsArray(vv) 824 | if err != nil { 825 | t.Error(err) 826 | } 827 | var rr testingTimePointer 828 | err = msgpack.UnmarshalAsArray(b, &rr) 829 | if err != nil { 830 | t.Error(err) 831 | } 832 | if vv.Time != nil || rr.Time != nil { 833 | t.Errorf("time different %v, %v", vv.Time, rr.Time) 834 | } 835 | } 836 | 837 | func checkValue(v testingValue, eqs ...func() (bool, interface{}, interface{})) error { 838 | var v1, v2 testingValue 839 | return _checkValue(v, &v1, &v2, eqs...) 840 | } 841 | 842 | func TestSlice(t *testing.T) { 843 | 844 | f := func(l int) []int8 { 845 | slice := make([]int8, l) 846 | for i := range slice { 847 | slice[i] = int8(rand.Intn(math.MaxUint8) + math.MinInt8) 848 | } 849 | return slice 850 | } 851 | 852 | check := func(v testingValue) error { 853 | var v1, v2 testingValue 854 | return _checkValue(v, &v1, &v2) 855 | } 856 | 857 | v := testingValue{Slice: f(15)} 858 | if err := check(v); err != nil { 859 | t.Error(err) 860 | } 861 | v = testingValue{Slice: f(150)} 862 | if err := check(v); err != nil { 863 | t.Error(err) 864 | } 865 | v = testingValue{Slice: f(math.MaxUint16 + 1)} 866 | if err := check(v); err != nil { 867 | t.Error(err) 868 | } 869 | v = testingValue{Slice: nil} 870 | if err := check(v); err != nil { 871 | t.Error(err) 872 | } 873 | 874 | v = testingValue{} 875 | v.Bytes = make([]byte, 100) 876 | for i := range v.Bytes { 877 | v.Bytes[i] = byte(rand.Intn(255)) 878 | } 879 | if err := check(v); err != nil { 880 | t.Error(err) 881 | } 882 | 883 | v = testingValue{} 884 | v.Bytes = make([]byte, math.MaxUint8+1) 885 | for i := range v.Bytes { 886 | v.Bytes[i] = byte(rand.Intn(255)) 887 | } 888 | if err := check(v); err != nil { 889 | t.Error(err) 890 | } 891 | 892 | v = testingValue{} 893 | v.Bytes = make([]byte, math.MaxUint16+1) 894 | for i := range v.Bytes { 895 | v.Bytes[i] = byte(rand.Intn(255)) 896 | } 897 | if err := check(v); err != nil { 898 | t.Error(err) 899 | } 900 | 901 | v = testingValue{} 902 | v.DoubleSlice = make([][]int16, 3) 903 | for i := range v.DoubleSlice { 904 | v.DoubleSlice[i] = make([]int16, i+1) 905 | for j := range v.DoubleSlice[i] { 906 | v.DoubleSlice[i][j] = int16(i*2 + j) 907 | } 908 | } 909 | if err := check(v); err != nil { 910 | t.Error(err) 911 | } 912 | 913 | v = testingValue{} 914 | for i := range v.DoubleArray { 915 | for j := range v.DoubleArray[i] { 916 | v.DoubleArray[i][j] = int16(i*4 + j) 917 | } 918 | } 919 | if err := check(v); err != nil { 920 | t.Error(err) 921 | } 922 | 923 | v = testingValue{} 924 | v.TripleBytes = make([][][]byte, 5) 925 | for i := range v.TripleBytes { 926 | v.TripleBytes[i] = make([][]byte, i+1) 927 | for j := range v.TripleBytes[i] { 928 | v.TripleBytes[i][j] = make([]byte, j+1) 929 | for k := range v.TripleBytes[i][j] { 930 | v.TripleBytes[i][j][k] = byte(i*2 + j*1 + k) 931 | } 932 | } 933 | } 934 | if err := check(v); err != nil { 935 | t.Error(err) 936 | } 937 | 938 | v = testingValue{} 939 | v.DoubleSlicePointerMap = make([][]**map[string]int, 2) 940 | for i := range v.DoubleSlicePointerMap { 941 | v.DoubleSlicePointerMap[i] = make([]**map[string]int, 4) 942 | for j := range v.DoubleSlicePointerMap[i] { 943 | m := map[string]int{fmt.Sprint(i*100 + j): i*50 + j} 944 | mp := &m 945 | mpp := &mp 946 | v.DoubleSlicePointerMap[i][j] = mpp 947 | } 948 | } 949 | if err := check(v); err != nil { 950 | t.Error(err) 951 | } 952 | 953 | v = testingValue{} 954 | v.MapDoubleSlicePointerInt = make(map[string][][]**int) 955 | for i := 0; i < 3; i++ { 956 | key := fmt.Sprint(i) 957 | v.MapDoubleSlicePointerInt[key] = make([][]**int, i+3) 958 | for j := range v.MapDoubleSlicePointerInt[key] { 959 | v.MapDoubleSlicePointerInt[key][j] = make([]**int, j+2) 960 | for k := range v.MapDoubleSlicePointerInt[key][j] { 961 | a := rand.Int() 962 | ap := &a 963 | app := &ap 964 | v.MapDoubleSlicePointerInt[key][j][k] = app 965 | } 966 | } 967 | } 968 | if err := check(v); err != nil { 969 | t.Error(err) 970 | } 971 | 972 | v = testingValue{} 973 | v.Bytes = make([]byte, math.MaxUint32+1) 974 | _, err := msgpack.MarshalAsArray(v) 975 | if err == nil || !strings.Contains(err.Error(), "not support this array length") { 976 | t.Error("something wrong", err) 977 | } 978 | runtime.GC() 979 | 980 | { 981 | var r testingSlice 982 | err = msgpack.UnmarshalAsArray([]byte{0x91, def.True}, &r) 983 | if err == nil { 984 | t.Errorf("error must occur") 985 | } 986 | if !strings.Contains(err.Error(), "SliceLength") { 987 | t.Error(err) 988 | } 989 | } 990 | //{ windows panic... 991 | // vv := testingSlice{} 992 | // vv.Slice = make([]int8, math.MaxUint32+1) 993 | // _, err := msgpack.MarshalAsArray(v) 994 | // if err == nil || !strings.Contains(err.Error(), "not support this array length") { 995 | // t.Error("something wrong", err) 996 | // } 997 | //} 998 | 999 | runtime.GC() 1000 | } 1001 | 1002 | func TestArray(t *testing.T) { 1003 | check := func(v testingArrays) error { 1004 | var v1, v2 testingArrays 1005 | return _checkValue(v, &v1, &v2) 1006 | } 1007 | 1008 | var v testingArrays 1009 | 1010 | v = testingArrays{} 1011 | for i := range v.Array1 { 1012 | v.Array1[i] = float32(rand.Intn(0xff)) 1013 | } 1014 | if err := check(v); err != nil { 1015 | t.Error(err) 1016 | } 1017 | v = testingArrays{} 1018 | for i := range v.Array2 { 1019 | v.Array2[i] = "a" 1020 | } 1021 | if err := check(v); err != nil { 1022 | t.Error(err) 1023 | } 1024 | v = testingArrays{} 1025 | for i := range v.Array3 { 1026 | v.Array3[i] = rand.Intn(0xff) > 0x7f 1027 | } 1028 | if err := check(v); err != nil { 1029 | t.Error(err) 1030 | } 1031 | v = testingArrays{} 1032 | for i := range v.Array4 { 1033 | v.Array4[i] = rand.Intn(math.MaxInt32) 1034 | } 1035 | if err := check(v); err != nil { 1036 | t.Error(err) 1037 | } 1038 | v = testingArrays{} 1039 | for i := range v.Array5 { 1040 | v.Array5[i] = rand.Intn(math.MaxInt32) 1041 | } 1042 | if err := check(v); err != nil { 1043 | t.Error(err) 1044 | } 1045 | v = testingArrays{} 1046 | for i := range v.Array6 { 1047 | v.Array6[i] = rand.Intn(math.MaxInt32) 1048 | } 1049 | if err := check(v); err != nil { 1050 | t.Error(err) 1051 | } 1052 | } 1053 | 1054 | func _checkValue(v interface{}, u1, u2 interface{}, eqs ...func() (bool, interface{}, interface{})) error { 1055 | b1, b2, err1, err2 := marshal(v, v) 1056 | if err1 != nil { 1057 | return fmt.Errorf("marshal to b1 failed %v", err1) 1058 | } 1059 | if err2 != nil { 1060 | return fmt.Errorf("marshal to b2 failed %v", err2) 1061 | } 1062 | 1063 | err1, err2 = unmarshal(b1, b2, u1, u2) 1064 | if err1 != nil { 1065 | return fmt.Errorf("unmarshal to u1 failed %v", err1) 1066 | } 1067 | if err2 != nil { 1068 | return fmt.Errorf("unmarshal to u2 failed %v", err2) 1069 | } 1070 | 1071 | if len(eqs) < 2 { 1072 | if !reflect.DeepEqual(v, reflect.ValueOf(u1).Elem().Interface()) { 1073 | return fmt.Errorf("not equal u1 %v, %v", v, u1) 1074 | } 1075 | if !reflect.DeepEqual(v, reflect.ValueOf(u2).Elem().Interface()) { 1076 | return fmt.Errorf("not equal u2 %v, %v", v, u2) 1077 | } 1078 | } else { 1079 | if b, v1, v2 := eqs[0](); !b { 1080 | return fmt.Errorf("not equal u1 %v, %v", v1, v2) 1081 | } 1082 | if b, v1, v2 := eqs[1](); !b { 1083 | return fmt.Errorf("not equal u2 %v, %v", v1, v2) 1084 | } 1085 | } 1086 | return nil 1087 | } 1088 | 1089 | func TestStruct(t *testing.T) { 1090 | check := func(v testingStruct) (testingStruct, testingStruct, error) { 1091 | f := func() (bool, interface{}, interface{}) { 1092 | return true, nil, nil 1093 | } 1094 | var r1, r2 testingStruct 1095 | err := _checkValue(v, &r1, &r2, f, f) 1096 | return r1, r2, err 1097 | } 1098 | 1099 | v := testingStruct{} 1100 | v.Int = rand.Int() 1101 | v1, v2, err := check(v) 1102 | if err != nil { 1103 | t.Error(err) 1104 | } 1105 | if v.Int != v1.Int || v.Int != v2.Int { 1106 | t.Error("value different", v.Int, v1.Int, v2.Int) 1107 | } 1108 | if v.Int == v1.Inside.Int || v.Int == v2.Outside.Int { 1109 | t.Error("value something wrong", v.Int, v1.Inside.Int, v2.Outside.Int) 1110 | } 1111 | 1112 | v = testingStruct{} 1113 | v.Inside = inside{Int: rand.Int()} 1114 | v1, v2, err = check(v) 1115 | if err != nil { 1116 | t.Error(err) 1117 | } 1118 | if !reflect.DeepEqual(v.Inside, v1.Inside) || !reflect.DeepEqual(v.Inside, v2.Inside) { 1119 | t.Error("value different", v.Inside, v1.Inside, v2.Inside) 1120 | } 1121 | if v.Inside.Int == v1.Int || v.Inside.Int == v2.Outside.Int { 1122 | t.Error("value something wrong", v.Inside.Int, v1.Int, v2.Outside.Int) 1123 | } 1124 | 1125 | v = testingStruct{} 1126 | v.Outside = outside{Int: rand.Int()} 1127 | v1, v2, err = check(v) 1128 | if err != nil { 1129 | t.Error(err) 1130 | } 1131 | if !reflect.DeepEqual(v.Outside, v1.Outside) || !reflect.DeepEqual(v.Outside, v2.Outside) { 1132 | t.Error("value different", v.Outside, v1.Outside, v2.Outside) 1133 | } 1134 | if v.Outside.Int == v1.Int || v.Outside.Int == v2.Inside.Int { 1135 | t.Error("value something wrong", v.Outside.Int, v1.Int, v2.Inside.Int) 1136 | } 1137 | 1138 | v = testingStruct{} 1139 | v.A = define2.A{Int: rand.Int(), B: define.B{Int: rand.Int()}} 1140 | v1, v2, err = check(v) 1141 | if err != nil { 1142 | t.Error(err) 1143 | } 1144 | if !reflect.DeepEqual(v.A, v1.A) || !reflect.DeepEqual(v.A, v2.A) { 1145 | t.Error("value different", v.A, v1.A, v2.A) 1146 | } 1147 | if v.A.B.Int != v1.B.Int || v.B.Int != v2.A.B.Int { //nolint:staticcheck // keeping "A" explicit for clarity 1148 | t.Error("value something wrong", v.A.Int, v1.Int, v2.Int) 1149 | } 1150 | if v.A.Int == v1.Int || v.A.Int == v2.Int { 1151 | t.Error("value something wrong", v.A.Int, v1.Int, v2.Int) 1152 | } 1153 | if v.B.Int == v1.Int || v.B.Int == v2.Int { 1154 | t.Error("value something wrong", v.A.Int, v1.Int, v2.Int) 1155 | } 1156 | 1157 | v = testingStruct{} 1158 | v.BB = define.DotImport{Int: rand.Int()} 1159 | v1, v2, err = check(v) 1160 | if err != nil { 1161 | t.Error(err) 1162 | } 1163 | if !reflect.DeepEqual(v.BB, v1.BB) || !reflect.DeepEqual(v.BB, v2.BB) { 1164 | t.Error("value different", v.BB, v1.BB, v2.BB) 1165 | } 1166 | if v.BB.Int == v1.Int || v.BB.Int == v2.Int { 1167 | t.Error("value something wrong", v.BB.Int, v1.Int, v2.Int) 1168 | } 1169 | 1170 | v = testingStruct{} 1171 | v.Time = define.Time{Int: rand.Int()} 1172 | v1, v2, err = check(v) 1173 | if err != nil { 1174 | t.Error(err) 1175 | } 1176 | if !reflect.DeepEqual(v.Time, v1.Time) || !reflect.DeepEqual(v.Time, v2.Time) { 1177 | t.Error("value different", v.Time, v1.Time, v2.Time) 1178 | } 1179 | if v.Time.Int == v1.Int || v.Time.Int == v2.Int { 1180 | t.Error("value something wrong", v.Time.Int, v1.Int, v2.Int) 1181 | } 1182 | 1183 | v = testingStruct{} 1184 | v.R = &recursive{Int: rand.Int()} 1185 | v.R.R = &recursive{Int: rand.Int()} 1186 | v1, v2, err = check(v) 1187 | if err != nil { 1188 | t.Error(err) 1189 | } 1190 | if !reflect.DeepEqual(v.R, v1.R) || !reflect.DeepEqual(v.R, v2.R) { 1191 | t.Error("value different", v.R, v1.R, v2.R) 1192 | } 1193 | if v.R.Int != v1.R.Int || v.R.Int != v2.R.Int { 1194 | t.Error("value different", v.R.Int, v1.R.Int, v2.R.Int) 1195 | } 1196 | if v.R.R.Int != v1.R.R.Int || v.R.R.Int != v2.R.R.Int { 1197 | t.Error("value different", v.R.R.Int, v1.R.R.Int, v2.R.R.Int) 1198 | } 1199 | if v.R.R.R != nil || v1.R.R.R != nil || v2.R.R.R != nil { 1200 | t.Error("value different", v.R.R.R, v1.R.R.R, v2.R.R.R) 1201 | } 1202 | 1203 | { 1204 | b1 := []byte{def.Array32, 0x00, 0x00, 0x00, 0x02} 1205 | var r inside 1206 | err := msgpack.UnmarshalAsArray(b1, &r) 1207 | if err == nil || !strings.Contains(err.Error(), "data length wrong") { 1208 | t.Error("something wrong", err) 1209 | } 1210 | b2 := []byte{def.Map32, 0x00, 0x00, 0x00, 0x02} 1211 | err = msgpack.UnmarshalAsArray(b2, &r) 1212 | if err == nil || !strings.Contains(err.Error(), "data length wrong") { 1213 | t.Error("something wrong", err) 1214 | } 1215 | } 1216 | } 1217 | 1218 | func TestTag(t *testing.T) { 1219 | v := testingTag{Tag: 1, Ignore: rand.Int(), Omit: rand.Int()} 1220 | b1, b2, e1, e2 := marshal(v, v) 1221 | if e1 != nil || e2 != nil { 1222 | t.Error(e1, e2) 1223 | } 1224 | if len(b1) <= 6 { 1225 | t.Errorf("tag does not recognize %v % x", v, b1) 1226 | } 1227 | if len(b2) != 2 { 1228 | t.Errorf("something wrong %v % x", v, b2) 1229 | } 1230 | 1231 | var v1, v2 testingTag 1232 | e1, e2 = unmarshal(b1, b2, &v1, &v2) 1233 | if e1 != nil || e2 != nil { 1234 | t.Error(e1, e2) 1235 | } 1236 | if v.Tag != v1.Tag || v.Tag != v2.Tag { 1237 | t.Errorf("not equal value %d, %d, %d", v.Tag, v1.Tag, v2.Tag) 1238 | } 1239 | if v.Ignore == v1.Ignore || v.Ignore == v2.Ignore { 1240 | t.Errorf("equal value %d, %d, %d", v.Ignore, v1.Ignore, v2.Ignore) 1241 | } 1242 | if v.Omit == v1.Omit || v.Omit == v2.Omit { 1243 | t.Errorf("equal value %d, %d, %d", v.Omit, v1.Omit, v2.Omit) 1244 | } 1245 | } 1246 | 1247 | func TestPointer(t *testing.T) { 1248 | 1249 | v := testingValue{Int: -1, Uint: 1} 1250 | 1251 | //// OK 1252 | // encode single pointer 1253 | b1, b2, err1, err2 := marshal(&v, &v) 1254 | if err1 != nil { 1255 | t.Error(err1) 1256 | } 1257 | if err2 != nil { 1258 | t.Error(err2) 1259 | } 1260 | 1261 | // decode double pointer 1262 | v1, v2 := new(testingValue), new(testingValue) 1263 | err1, err2 = unmarshal(b1, b2, &v1, &v2) 1264 | if err1 != nil { 1265 | t.Error(err1) 1266 | } 1267 | if err2 != nil { 1268 | t.Error(err2) 1269 | } 1270 | 1271 | // encode double pointer 1272 | b3, b4, err1, err2 := marshal(&v1, &v2) 1273 | if err1 != nil { 1274 | t.Error(err1) 1275 | } 1276 | if err2 != nil { 1277 | t.Error(err2) 1278 | } 1279 | 1280 | // decode triple pointer 1281 | v3p, v4p := new(testingValue), new(testingValue) 1282 | v3, v4 := &v3p, &v4p 1283 | err1, err2 = unmarshal(b3, b4, &v3, &v4) 1284 | if err1 != nil { 1285 | t.Error(err1) 1286 | } 1287 | if err2 != nil { 1288 | t.Error(err2) 1289 | } 1290 | 1291 | if !reflect.DeepEqual(v, *v1) { 1292 | t.Error("not equal v1", v, *v1) 1293 | } 1294 | if !reflect.DeepEqual(v, *v2) { 1295 | t.Error("not equal v2", v, *v2) 1296 | } 1297 | if v3v := *v3; !reflect.DeepEqual(v, *v3v) { 1298 | t.Error("not equal v3", v, v3v) 1299 | } 1300 | if v4v := *v4; !reflect.DeepEqual(v, *v4v) { 1301 | t.Error("not equal v4", v, v4v) 1302 | } 1303 | 1304 | //// NG 1305 | // encode triple pointer 1306 | b5, b6, err1, err2 := marshal(&v3, &v4) 1307 | if err1 != nil && !strings.Contains(err1.Error(), "strict") { 1308 | t.Error(err1) 1309 | } 1310 | if err1 == nil { 1311 | t.Error("error should occur at marshalling v3 pointer") 1312 | } 1313 | if err2 != nil && !strings.Contains(err2.Error(), "strict") { 1314 | t.Error(err2) 1315 | } 1316 | if err2 == nil { 1317 | t.Error("error should occur at marshalling v4 pointer") 1318 | } 1319 | 1320 | // decode quad pointer 1321 | v5pp, v6pp := new(testingValue), new(testingValue) 1322 | v5p, v6p := &v5pp, &v6pp 1323 | v5, v6 := &v5p, &v6p 1324 | err1, err2 = unmarshal(b5, b6, &v5, &v6) 1325 | if err1 != nil && !strings.Contains(err1.Error(), "strict") { 1326 | t.Error(err1) 1327 | } 1328 | if err1 == nil { 1329 | t.Error("error should occur at unmarshalling b5 pointer") 1330 | } 1331 | if err2 != nil && !strings.Contains(err2.Error(), "strict") { 1332 | t.Error(err2) 1333 | } 1334 | if err2 == nil { 1335 | t.Error("error should occur at unmarshalling b6 pointer") 1336 | } 1337 | } 1338 | 1339 | func TestNotGenerated(t *testing.T) { 1340 | err := checkUndefined(notGenerated1{}, notGenerated2{}, ¬Generated1{}, ¬Generated2{}) 1341 | if err != nil { 1342 | t.Error(err) 1343 | } 1344 | err = checkUndefined(notGenerated3{}, notGenerated4{}, ¬Generated3{}, ¬Generated4{}) 1345 | if err != nil { 1346 | t.Error(err) 1347 | } 1348 | err = checkUndefined(notGenerated5{}, notGenerated6{}, ¬Generated5{}, ¬Generated6{}) 1349 | if err != nil { 1350 | t.Error(err) 1351 | } 1352 | err = checkUndefined(notGenerated7{}, notGenerated8{}, ¬Generated7{}, ¬Generated8{}) 1353 | if err != nil { 1354 | t.Error(err) 1355 | } 1356 | err = checkUndefined(notGenerated10{}, notGenerated10{}, ¬Generated10{}, ¬Generated10{}) 1357 | if err != nil { 1358 | t.Error(err) 1359 | } 1360 | } 1361 | 1362 | func TestPrivate(t *testing.T) { 1363 | v := private{} 1364 | v.SetInt() 1365 | b1, b2, err1, err2 := marshal(v, v) 1366 | if err1 != nil || err2 != nil { 1367 | t.Errorf("somthing wrong %v, %v", err1, err2) 1368 | } 1369 | if len(b1) != 1 || b1[0] != 0x80 { 1370 | t.Errorf("data is wrong % x", b1) 1371 | } 1372 | if len(b2) != 1 || b2[0] != 0x90 { 1373 | t.Errorf("data is wrong % x", b2) 1374 | } 1375 | 1376 | vc1 := &v 1377 | vc2 := &vc1 1378 | vc3 := &vc2 1379 | vc4 := &vc3 1380 | err := forCoverage(vc1, vc2, vc3, vc4) 1381 | if err != nil { 1382 | t.Error(err) 1383 | } 1384 | } 1385 | 1386 | func forCoverage(v1, v2, v3, v4 interface{}) error { 1387 | 1388 | // encode single pointer 1389 | b1, b2, err1, err2 := marshal(v1, v1) 1390 | if err1 != nil { 1391 | return fmt.Errorf("marshal to b1 error %v", err1) 1392 | } 1393 | if err2 != nil { 1394 | return fmt.Errorf("marshal to b2 error %v", err2) 1395 | } 1396 | 1397 | // decode double pointer 1398 | err1, err2 = unmarshal(b1, b2, v2, v2) 1399 | if err1 != nil { 1400 | return fmt.Errorf("unmarshal from b1 error %v", err1) 1401 | } 1402 | if err2 != nil { 1403 | return fmt.Errorf("unmarshal from b2 error %v", err2) 1404 | } 1405 | 1406 | // encode double pointer 1407 | b3, b4, err1, err2 := marshal(v2, v2) 1408 | if err1 != nil { 1409 | return fmt.Errorf("marshal to b3 error %v", err1) 1410 | } 1411 | if err2 != nil { 1412 | return fmt.Errorf("marshal to b4 error %v", err2) 1413 | } 1414 | 1415 | // decode triple pointer 1416 | err1, err2 = unmarshal(b3, b4, v3, v3) 1417 | if err1 != nil { 1418 | return fmt.Errorf("unmarshal from b3 error %v", err1) 1419 | } 1420 | if err2 != nil { 1421 | return fmt.Errorf("unmarshal from b4 error %v", err2) 1422 | } 1423 | 1424 | //// NG 1425 | // encode triple pointer / decode quad pointer 1426 | err := checkUndefined(v3, v3, v4, v4) 1427 | if err != nil { 1428 | return fmt.Errorf("for coverage; %v", err) 1429 | } 1430 | return nil 1431 | } 1432 | 1433 | func checkUndefined(m1, m2, u1, u2 interface{}) error { 1434 | b1, b2, err1, err2 := marshal(m1, m2) 1435 | if err1 != nil && !strings.Contains(err1.Error(), "use strict option") { 1436 | return fmt.Errorf("check undefined: marshal to b1 error %v", err1) 1437 | } 1438 | if err1 == nil { 1439 | return fmt.Errorf("check undefined: error should occur at marshalling m1") 1440 | } 1441 | if err2 != nil && !strings.Contains(err2.Error(), "use strict option") { 1442 | return fmt.Errorf("check undefined: marshal to b2 error %v", err2) 1443 | } 1444 | if err2 == nil { 1445 | return fmt.Errorf("check undefined: error should occur at marshalling m2") 1446 | } 1447 | 1448 | err1, err2 = unmarshal(b1, b2, u1, u2) 1449 | if err1 != nil && !strings.Contains(err1.Error(), "use strict option") { 1450 | return fmt.Errorf("check undefined: unmarshal to u1 error %v", err1) 1451 | } 1452 | if err1 == nil { 1453 | return fmt.Errorf("check undefined: error should occur at unmarshalling u1") 1454 | } 1455 | if err2 != nil && !strings.Contains(err2.Error(), "use strict option") { 1456 | return fmt.Errorf("ucheck undefined: unmarshal to u2 error %v", err2) 1457 | } 1458 | if err2 == nil { 1459 | return fmt.Errorf("check undefined: error should occur at unmarshalling u2") 1460 | } 1461 | return nil 1462 | } 1463 | 1464 | func marshal(v1, v2 interface{}) ([]byte, []byte, error, error) { 1465 | b1, e1 := msgpack.Marshal(v1) 1466 | b2, e2 := msgpack.MarshalAsArray(v2) 1467 | return b1, b2, e1, e2 1468 | } 1469 | 1470 | func unmarshal(b1, b2 []byte, v1, v2 interface{}) (error, error) { 1471 | return msgpack.Unmarshal(b1, v1), msgpack.UnmarshalAsArray(b2, v2) 1472 | } 1473 | --------------------------------------------------------------------------------