MM-27456: Use reflect-free serialization for hot structs (#15171)
Automatic Merge
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
32b7d2b5f1
Коммит
91a76b2df9
9
Makefile
9
Makefile
@@ -554,7 +554,7 @@ vet: ## Run mattermost go vet specific checks
|
||||
echo "mattermost-govet is not installed. Please install it executing \"GO111MODULE=off GOBIN=$(PWD)/bin go get -u github.com/mattermost/mattermost-govet\""; \
|
||||
exit 1; \
|
||||
fi;
|
||||
@VET_CMD="-license -structuredLogging -inconsistentReceiverName -tFatal"; \
|
||||
@VET_CMD="-license -structuredLogging -inconsistentReceiverName -inconsistentReceiverName.ignore=serialized_gen.go -tFatal"; \
|
||||
if ! [ -z "${MM_VET_OPENSPEC_PATH}" ] && [ -f "${MM_VET_OPENSPEC_PATH}" ]; then \
|
||||
VET_CMD="$$VET_CMD -openApiSync -openApiSync.spec=$$MM_VET_OPENSPEC_PATH"; \
|
||||
else \
|
||||
@@ -567,6 +567,13 @@ ifneq ($(MM_NO_ENTERPRISE_LINT),true)
|
||||
endif
|
||||
endif
|
||||
|
||||
gen-serialized: ## Generates serialization methods for hot structs
|
||||
# This tool only works at a file level, not at a package level.
|
||||
# So you would need to move the structs that need to be serialized temporarily
|
||||
# to session.go and run the tool.
|
||||
$(GO) get -modfile=go.tools.mod github.com/tinylib/msgp
|
||||
$(GOBIN)/msgp -file=./model/session.go -tests=false -o=./model/serialized_gen.go
|
||||
|
||||
todo: ## Display TODO and FIXME items in the source code.
|
||||
@! ag --ignore Makefile --ignore-dir vendor --ignore-dir runtime TODO
|
||||
@! ag --ignore Makefile --ignore-dir vendor --ignore-dir runtime XXX
|
||||
|
||||
3
go.mod
3
go.mod
@@ -103,7 +103,8 @@ require (
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
|
||||
github.com/throttled/throttled v2.2.4+incompatible
|
||||
github.com/tinylib/msgp v1.1.2 // indirect
|
||||
github.com/tinylib/msgp v1.1.2
|
||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 // indirect
|
||||
github.com/tylerb/graceful v1.2.15
|
||||
github.com/uber/jaeger-client-go v2.24.0+incompatible
|
||||
github.com/uber/jaeger-lib v2.2.0+incompatible
|
||||
|
||||
2
go.sum
2
go.sum
@@ -719,6 +719,8 @@ github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDW
|
||||
github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ=
|
||||
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 h1:OXcKh35JaYsGMRzpvFkLv/MEyPuL49CThT1pZ8aSml4=
|
||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/tylerb/graceful v1.2.15 h1:B0x01Y8fsJpogzZTkDg6BDi6eMf03s01lEKGdrv83oA=
|
||||
github.com/tylerb/graceful v1.2.15/go.mod h1:LPYTbOYmUTdabwRt0TGhLllQ0MUNbs0Y5q1WXJOI9II=
|
||||
|
||||
@@ -5,6 +5,9 @@ go 1.14
|
||||
require (
|
||||
github.com/jstemmer/go-junit-report v0.9.1 // indirect
|
||||
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200730135456-e2334fe87160 // indirect
|
||||
github.com/philhofer/fwd v1.0.0 // indirect
|
||||
github.com/reflog/struct2interface v0.6.1 // indirect
|
||||
github.com/tinylib/msgp v1.1.2 // indirect
|
||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 // indirect
|
||||
github.com/vektra/mockery v1.1.2 // indirect
|
||||
)
|
||||
|
||||
@@ -64,6 +64,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -100,7 +102,11 @@ github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ=
|
||||
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 h1:OXcKh35JaYsGMRzpvFkLv/MEyPuL49CThT1pZ8aSml4=
|
||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/vektra/mockery v1.1.2 h1:uc0Yn67rJpjt8U/mAZimdCKn9AeA97BOkjpmtBSlfP4=
|
||||
|
||||
1622
model/serialized_gen.go
Обычный файл
1622
model/serialized_gen.go
Обычный файл
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
@@ -30,6 +30,11 @@ const (
|
||||
SESSION_USER_ACCESS_TOKEN_EXPIRY = 100 * 365 // 100 years
|
||||
)
|
||||
|
||||
//msgp:tuple Session
|
||||
|
||||
// Session contains the user session details.
|
||||
// This struct's serializer methods are auto-generated. If a new field is added/removed,
|
||||
// please run make gen-serialized.
|
||||
type Session struct {
|
||||
Id string `json:"id"`
|
||||
Token string `json:"token"`
|
||||
|
||||
@@ -59,6 +59,11 @@ const (
|
||||
USER_LOCALE_MAX_LENGTH = 5
|
||||
)
|
||||
|
||||
//msgp:tuple User
|
||||
|
||||
// User contains the details about the user.
|
||||
// This struct's serializer methods are auto-generated. If a new field is added/removed,
|
||||
// please run make gen-serialized.
|
||||
type User struct {
|
||||
Id string `json:"id"`
|
||||
CreateAt int64 `json:"create_at,omitempty"`
|
||||
|
||||
48
services/cache/lru.go
поставляемый
48
services/cache/lru.go
поставляемый
@@ -8,6 +8,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
)
|
||||
|
||||
@@ -141,9 +143,21 @@ func (l *LRU) set(key string, value interface{}, ttl time.Duration) error {
|
||||
expires = time.Now().Add(ttl)
|
||||
}
|
||||
|
||||
buf, err := msgpack.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
var buf []byte
|
||||
var err error
|
||||
|
||||
// We use a fast path for hot structs.
|
||||
if msgpVal, ok := value.(msgp.Marshaler); ok {
|
||||
buf, err = msgpVal.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Slow path for other structs.
|
||||
buf, err = msgpack.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Check for existing item, ignoring expiry since we'd update anyway.
|
||||
@@ -182,6 +196,34 @@ func (l *LRU) get(key string, value interface{}) error {
|
||||
|
||||
l.evictList.MoveToFront(ent)
|
||||
|
||||
// We use a fast path for hot structs.
|
||||
if msgpVal, ok := value.(msgp.Unmarshaler); ok {
|
||||
_, err := msgpVal.UnmarshalMsg(e.value)
|
||||
return err
|
||||
}
|
||||
|
||||
// This is ugly and makes the cache package aware of the model package.
|
||||
// But this is due to 2 things.
|
||||
// 1. The msgp package works on methods on structs rather than functions.
|
||||
// 2. Our cache interface passes pointers to empty pointers, and not pointers
|
||||
// to values. This is mainly how all our model structs are passed around.
|
||||
// It might be technically possible to use values _just_ for hot structs
|
||||
// like these and then return a pointer while returning from the cache function,
|
||||
// but it will make the codebase inconsistent, and has some edge-cases to take care of.
|
||||
switch v := value.(type) {
|
||||
case **model.User:
|
||||
var u model.User
|
||||
_, err := u.UnmarshalMsg(e.value)
|
||||
*v = &u
|
||||
return err
|
||||
case **model.Session:
|
||||
var s model.Session
|
||||
_, err := s.UnmarshalMsg(e.value)
|
||||
*v = &s
|
||||
return err
|
||||
}
|
||||
|
||||
// Slow path for other structs.
|
||||
return msgpack.Unmarshal(e.value, value)
|
||||
}
|
||||
return ErrKeyNotFound
|
||||
|
||||
112
services/cache/lru_test.go
поставляемый
112
services/cache/lru_test.go
поставляемый
@@ -205,6 +205,80 @@ func TestLRUMarshalUnMarshal(t *testing.T) {
|
||||
err = l.Get("post", &p)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, post.Clone(), p.Clone())
|
||||
|
||||
session := &model.Session{
|
||||
Id: "ty7ia14yuty5bmpt8wmz6da1fw",
|
||||
Token: "79c3iq6nzpycmkkawudanqhg5c",
|
||||
CreateAt: 1595445296960,
|
||||
ExpiresAt: 1598296496960,
|
||||
LastActivityAt: 1595445296960,
|
||||
UserId: "rpgh1q5ra38y9xjn9z8fjctezr",
|
||||
Roles: "system_admin system_user",
|
||||
IsOAuth: false,
|
||||
ExpiredNotify: false,
|
||||
Props: map[string]string{
|
||||
"csrf": "33zb7h7rk3rfffztojn5pxbkxe",
|
||||
"isMobile": "false",
|
||||
"isSaml": "false",
|
||||
"is_guest": "false",
|
||||
"os": "",
|
||||
"platform": "Windows",
|
||||
},
|
||||
}
|
||||
|
||||
err = l.Set("session", session)
|
||||
require.Nil(t, err)
|
||||
|
||||
var s *model.Session
|
||||
err = l.Get("session", &s)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, session, s)
|
||||
|
||||
user := &model.User{
|
||||
Id: "id",
|
||||
CreateAt: 11111,
|
||||
UpdateAt: 11111,
|
||||
DeleteAt: 11111,
|
||||
Username: "username",
|
||||
Password: "password",
|
||||
AuthService: "AuthService",
|
||||
AuthData: nil,
|
||||
Email: "Email",
|
||||
EmailVerified: true,
|
||||
Nickname: "Nickname",
|
||||
FirstName: "FirstName",
|
||||
LastName: "LastName",
|
||||
Position: "Position",
|
||||
Roles: "Roles",
|
||||
AllowMarketing: true,
|
||||
Props: map[string]string{
|
||||
"key0": "value0",
|
||||
},
|
||||
NotifyProps: map[string]string{
|
||||
"key0": "value0",
|
||||
},
|
||||
LastPasswordUpdate: 111111,
|
||||
LastPictureUpdate: 111111,
|
||||
FailedAttempts: 111111,
|
||||
Locale: "Locale",
|
||||
MfaActive: true,
|
||||
MfaSecret: "MfaSecret",
|
||||
LastActivityAt: 111111,
|
||||
IsBot: true,
|
||||
TermsOfServiceId: "TermsOfServiceId",
|
||||
TermsOfServiceCreateAt: 111111,
|
||||
}
|
||||
|
||||
err = l.Set("user", user)
|
||||
require.Nil(t, err)
|
||||
|
||||
var u *model.User
|
||||
err = l.Get("user", &u)
|
||||
require.Nil(t, err)
|
||||
// msgp returns an empty map instead of a nil map.
|
||||
// This does not make an actual difference in terms of functionality.
|
||||
u.Timezone = nil
|
||||
require.Equal(t, user, u)
|
||||
}
|
||||
|
||||
func BenchmarkLRU(b *testing.B) {
|
||||
@@ -458,7 +532,43 @@ func BenchmarkLRU(b *testing.B) {
|
||||
err := l2.Set("test", status)
|
||||
require.Nil(b, err)
|
||||
|
||||
var val model.Status
|
||||
var val *model.Status
|
||||
err = l2.Get("test", &val)
|
||||
require.Nil(b, err)
|
||||
}
|
||||
})
|
||||
|
||||
session := model.Session{
|
||||
Id: "ty7ia14yuty5bmpt8wmz6da1fw",
|
||||
Token: "79c3iq6nzpycmkkawudanqhg5c",
|
||||
CreateAt: 1595445296960,
|
||||
ExpiresAt: 1598296496960,
|
||||
LastActivityAt: 1595445296960,
|
||||
UserId: "rpgh1q5ra38y9xjn9z8fjctezr",
|
||||
Roles: "system_admin system_user",
|
||||
IsOAuth: false,
|
||||
ExpiredNotify: false,
|
||||
Props: map[string]string{
|
||||
"csrf": "33zb7h7rk3rfffztojn5pxbkxe",
|
||||
"isMobile": "false",
|
||||
"isSaml": "false",
|
||||
"is_guest": "false",
|
||||
"os": "",
|
||||
"platform": "Windows",
|
||||
},
|
||||
}
|
||||
|
||||
b.Run("Session=new", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
l2 := NewLRU(&LRUOptions{
|
||||
Size: 1,
|
||||
DefaultExpiry: 0,
|
||||
InvalidateClusterEvent: "",
|
||||
})
|
||||
err := l2.Set("test", &session)
|
||||
require.Nil(b, err)
|
||||
|
||||
var val *model.Session
|
||||
err = l2.Get("test", &val)
|
||||
require.Nil(b, err)
|
||||
}
|
||||
|
||||
@@ -101,6 +101,8 @@ func TestUserStoreCache(t *testing.T) {
|
||||
cachedUsers, err = cachedStore.User().GetProfileByIds(fakeUserIds, &store.UserGetByIdsOpts{}, true)
|
||||
require.Nil(t, err)
|
||||
for i := 0; i < len(storedUsers); i++ {
|
||||
storedUsers[i].Props = model.StringMap{}
|
||||
storedUsers[i].Timezone = model.StringMap{}
|
||||
assert.Equal(t, storedUsers[i], cachedUsers[i])
|
||||
if storedUsers[i] == cachedUsers[i] {
|
||||
assert.Fail(t, "should be different pointers")
|
||||
@@ -241,6 +243,8 @@ func TestUserStoreGetCache(t *testing.T) {
|
||||
cachedUser.NotifyProps["key"] = "othervalue"
|
||||
assert.NotEqual(t, storedUser, cachedUser)
|
||||
|
||||
storedUser.Props = model.StringMap{}
|
||||
storedUser.Timezone = model.StringMap{}
|
||||
cachedUser, err = cachedStore.User().Get(fakeUserId)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, storedUser, cachedUser)
|
||||
|
||||
10
vendor/github.com/tinylib/msgp/.gitignore
сгенерированный
поставляемый
Обычный файл
10
vendor/github.com/tinylib/msgp/.gitignore
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,10 @@
|
||||
_generated/generated.go
|
||||
_generated/generated_test.go
|
||||
_generated/*_gen.go
|
||||
_generated/*_gen_test.go
|
||||
_generated/embeddedStruct/*_gen.go
|
||||
_generated/embeddedStruct/*_gen_test.go
|
||||
msgp/defgen_test.go
|
||||
msgp/cover.out
|
||||
*~
|
||||
*.coverprofile
|
||||
11
vendor/github.com/tinylib/msgp/.travis.yml
сгенерированный
поставляемый
Обычный файл
11
vendor/github.com/tinylib/msgp/.travis.yml
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,11 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.12.x
|
||||
- tip
|
||||
|
||||
env:
|
||||
- GIMME_ARCH=amd64
|
||||
- GIMME_ARCH=386
|
||||
|
||||
script: "make travis"
|
||||
52
vendor/github.com/tinylib/msgp/Makefile
сгенерированный
поставляемый
Обычный файл
52
vendor/github.com/tinylib/msgp/Makefile
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,52 @@
|
||||
|
||||
# NOTE: This Makefile is only necessary if you
|
||||
# plan on developing the msgp tool and library.
|
||||
# Installation can still be performed with a
|
||||
# normal `go install`.
|
||||
|
||||
# generated integration test files
|
||||
GGEN = ./_generated/generated.go ./_generated/generated_test.go
|
||||
# generated unit test files
|
||||
MGEN = ./msgp/defgen_test.go
|
||||
|
||||
SHELL := /bin/bash
|
||||
|
||||
BIN = $(GOBIN)/msgp
|
||||
|
||||
.PHONY: clean wipe install get-deps bench all
|
||||
|
||||
$(BIN): */*.go
|
||||
@go install ./...
|
||||
|
||||
install: $(BIN)
|
||||
|
||||
$(GGEN): ./_generated/def.go
|
||||
go generate ./_generated
|
||||
|
||||
$(MGEN): ./msgp/defs_test.go
|
||||
go generate ./msgp
|
||||
|
||||
test: all
|
||||
go test ./... ./_generated
|
||||
|
||||
bench: all
|
||||
go test -bench ./...
|
||||
|
||||
clean:
|
||||
$(RM) $(GGEN) $(MGEN)
|
||||
|
||||
wipe: clean
|
||||
$(RM) $(BIN)
|
||||
|
||||
get-deps:
|
||||
go get -d -t ./...
|
||||
|
||||
all: install $(GGEN) $(MGEN)
|
||||
|
||||
# travis CI enters here
|
||||
travis:
|
||||
go get -d -t ./...
|
||||
go build -o "$${GOPATH%%:*}/bin/msgp" .
|
||||
go generate ./msgp
|
||||
go generate ./_generated
|
||||
go test -v ./... ./_generated
|
||||
102
vendor/github.com/tinylib/msgp/README.md
сгенерированный
поставляемый
Обычный файл
102
vendor/github.com/tinylib/msgp/README.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,102 @@
|
||||
MessagePack Code Generator [](https://travis-ci.org/tinylib/msgp)
|
||||
=======
|
||||
|
||||
This is a code generation tool and serialization library for [MessagePack](http://msgpack.org). You can read more about MessagePack [in the wiki](http://github.com/tinylib/msgp/wiki), or at [msgpack.org](http://msgpack.org).
|
||||
|
||||
### Why?
|
||||
|
||||
- Use Go as your schema language
|
||||
- Performance
|
||||
- [JSON interop](http://godoc.org/github.com/tinylib/msgp/msgp#CopyToJSON)
|
||||
- [User-defined extensions](http://github.com/tinylib/msgp/wiki/Using-Extensions)
|
||||
- Type safety
|
||||
- Encoding flexibility
|
||||
|
||||
### Quickstart
|
||||
|
||||
In a source file, include the following directive:
|
||||
|
||||
```go
|
||||
//go:generate msgp
|
||||
```
|
||||
|
||||
The `msgp` command will generate serialization methods for all exported type declarations in the file.
|
||||
|
||||
You can [read more about the code generation options here](http://github.com/tinylib/msgp/wiki/Using-the-Code-Generator).
|
||||
|
||||
### Use
|
||||
|
||||
Field names can be set in much the same way as the `encoding/json` package. For example:
|
||||
|
||||
```go
|
||||
type Person struct {
|
||||
Name string `msg:"name"`
|
||||
Address string `msg:"address"`
|
||||
Age int `msg:"age"`
|
||||
Hidden string `msg:"-"` // this field is ignored
|
||||
unexported bool // this field is also ignored
|
||||
}
|
||||
```
|
||||
|
||||
By default, the code generator will satisfy `msgp.Sizer`, `msgp.Encodable`, `msgp.Decodable`,
|
||||
`msgp.Marshaler`, and `msgp.Unmarshaler`. Carefully-designed applications can use these methods to do
|
||||
marshalling/unmarshalling with zero heap allocations.
|
||||
|
||||
While `msgp.Marshaler` and `msgp.Unmarshaler` are quite similar to the standard library's
|
||||
`json.Marshaler` and `json.Unmarshaler`, `msgp.Encodable` and `msgp.Decodable` are useful for
|
||||
stream serialization. (`*msgp.Writer` and `*msgp.Reader` are essentially protocol-aware versions
|
||||
of `*bufio.Writer` and `*bufio.Reader`, respectively.)
|
||||
|
||||
### Features
|
||||
|
||||
- Extremely fast generated code
|
||||
- Test and benchmark generation
|
||||
- JSON interoperability (see `msgp.CopyToJSON() and msgp.UnmarshalAsJSON()`)
|
||||
- Support for complex type declarations
|
||||
- Native support for Go's `time.Time`, `complex64`, and `complex128` types
|
||||
- Generation of both `[]byte`-oriented and `io.Reader/io.Writer`-oriented methods
|
||||
- Support for arbitrary type system extensions
|
||||
- [Preprocessor directives](http://github.com/tinylib/msgp/wiki/Preprocessor-Directives)
|
||||
- File-based dependency model means fast codegen regardless of source tree size.
|
||||
|
||||
Consider the following:
|
||||
```go
|
||||
const Eight = 8
|
||||
type MyInt int
|
||||
type Data []byte
|
||||
|
||||
type Struct struct {
|
||||
Which map[string]*MyInt `msg:"which"`
|
||||
Other Data `msg:"other"`
|
||||
Nums [Eight]float64 `msg:"nums"`
|
||||
}
|
||||
```
|
||||
As long as the declarations of `MyInt` and `Data` are in the same file as `Struct`, the parser will determine that the type information for `MyInt` and `Data` can be passed into the definition of `Struct` before its methods are generated.
|
||||
|
||||
#### Extensions
|
||||
|
||||
MessagePack supports defining your own types through "extensions," which are just a tuple of
|
||||
the data "type" (`int8`) and the raw binary. You [can see a worked example in the wiki.](http://github.com/tinylib/msgp/wiki/Using-Extensions)
|
||||
|
||||
### Status
|
||||
|
||||
Mostly stable, in that no breaking changes have been made to the `/msgp` library in more than a year. Newer versions
|
||||
of the code may generate different code than older versions for performance reasons. I (@philhofer) am aware of a
|
||||
number of stability-critical commercial applications that use this code with good results. But, caveat emptor.
|
||||
|
||||
You can read more about how `msgp` maps MessagePack types onto Go types [in the wiki](http://github.com/tinylib/msgp/wiki).
|
||||
|
||||
Here some of the known limitations/restrictions:
|
||||
|
||||
- Identifiers from outside the processed source file are assumed (optimistically) to satisfy the generator's interfaces. If this isn't the case, your code will fail to compile.
|
||||
- Like most serializers, `chan` and `func` fields are ignored, as well as non-exported fields.
|
||||
- Encoding of `interface{}` is limited to built-ins or types that have explicit encoding methods.
|
||||
- _Maps must have `string` keys._ This is intentional (as it preserves JSON interop.) Although non-string map keys are not forbidden by the MessagePack standard, many serializers impose this restriction. (It also means *any* well-formed `struct` can be de-serialized into a `map[string]interface{}`.) The only exception to this rule is that the deserializers will allow you to read map keys encoded as `bin` types, due to the fact that some legacy encodings permitted this. (However, those values will still be cast to Go `string`s, and they will be converted to `str` types when re-encoded. It is the responsibility of the user to ensure that map keys are UTF-8 safe in this case.) The same rules hold true for JSON translation.
|
||||
|
||||
If the output compiles, then there's a pretty good chance things are fine. (Plus, we generate tests for you.) *Please, please, please* file an issue if you think the generator is writing broken code.
|
||||
|
||||
### Performance
|
||||
|
||||
If you like benchmarks, see [here](http://bravenewgeek.com/so-you-wanna-go-fast/) and [here](https://github.com/alecthomas/go_serialization_benchmarks).
|
||||
|
||||
As one might expect, the generated methods that deal with `[]byte` are faster for small objects, but the `io.Reader/Writer` methods are generally more memory-efficient (and, at some point, faster) for large (> 2KB) objects.
|
||||
231
vendor/github.com/tinylib/msgp/gen/decode.go
сгенерированный
поставляемый
Обычный файл
231
vendor/github.com/tinylib/msgp/gen/decode.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,231 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func decode(w io.Writer) *decodeGen {
|
||||
return &decodeGen{
|
||||
p: printer{w: w},
|
||||
hasfield: false,
|
||||
}
|
||||
}
|
||||
|
||||
type decodeGen struct {
|
||||
passes
|
||||
p printer
|
||||
hasfield bool
|
||||
ctx *Context
|
||||
}
|
||||
|
||||
func (d *decodeGen) Method() Method { return Decode }
|
||||
|
||||
func (d *decodeGen) needsField() {
|
||||
if d.hasfield {
|
||||
return
|
||||
}
|
||||
d.p.print("\nvar field []byte; _ = field")
|
||||
d.hasfield = true
|
||||
}
|
||||
|
||||
func (d *decodeGen) Execute(p Elem) error {
|
||||
p = d.applyall(p)
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
d.hasfield = false
|
||||
if !d.p.ok() {
|
||||
return d.p.err
|
||||
}
|
||||
|
||||
if !IsPrintable(p) {
|
||||
return nil
|
||||
}
|
||||
|
||||
d.ctx = &Context{}
|
||||
|
||||
d.p.comment("DecodeMsg implements msgp.Decodable")
|
||||
|
||||
d.p.printf("\nfunc (%s %s) DecodeMsg(dc *msgp.Reader) (err error) {", p.Varname(), methodReceiver(p))
|
||||
next(d, p)
|
||||
d.p.nakedReturn()
|
||||
unsetReceiver(p)
|
||||
return d.p.err
|
||||
}
|
||||
|
||||
func (d *decodeGen) gStruct(s *Struct) {
|
||||
if !d.p.ok() {
|
||||
return
|
||||
}
|
||||
if s.AsTuple {
|
||||
d.structAsTuple(s)
|
||||
} else {
|
||||
d.structAsMap(s)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *decodeGen) assignAndCheck(name string, typ string) {
|
||||
if !d.p.ok() {
|
||||
return
|
||||
}
|
||||
d.p.printf("\n%s, err = dc.Read%s()", name, typ)
|
||||
d.p.wrapErrCheck(d.ctx.ArgsStr())
|
||||
}
|
||||
|
||||
func (d *decodeGen) structAsTuple(s *Struct) {
|
||||
nfields := len(s.Fields)
|
||||
|
||||
sz := randIdent()
|
||||
d.p.declare(sz, u32)
|
||||
d.assignAndCheck(sz, arrayHeader)
|
||||
d.p.arrayCheck(strconv.Itoa(nfields), sz)
|
||||
for i := range s.Fields {
|
||||
if !d.p.ok() {
|
||||
return
|
||||
}
|
||||
d.ctx.PushString(s.Fields[i].FieldName)
|
||||
next(d, s.Fields[i].FieldElem)
|
||||
d.ctx.Pop()
|
||||
}
|
||||
}
|
||||
|
||||
func (d *decodeGen) structAsMap(s *Struct) {
|
||||
d.needsField()
|
||||
sz := randIdent()
|
||||
d.p.declare(sz, u32)
|
||||
d.assignAndCheck(sz, mapHeader)
|
||||
|
||||
d.p.printf("\nfor %s > 0 {\n%s--", sz, sz)
|
||||
d.assignAndCheck("field", mapKey)
|
||||
d.p.print("\nswitch msgp.UnsafeString(field) {")
|
||||
for i := range s.Fields {
|
||||
d.ctx.PushString(s.Fields[i].FieldName)
|
||||
d.p.printf("\ncase \"%s\":", s.Fields[i].FieldTag)
|
||||
next(d, s.Fields[i].FieldElem)
|
||||
d.ctx.Pop()
|
||||
if !d.p.ok() {
|
||||
return
|
||||
}
|
||||
}
|
||||
d.p.print("\ndefault:\nerr = dc.Skip()")
|
||||
d.p.wrapErrCheck(d.ctx.ArgsStr())
|
||||
|
||||
d.p.closeblock() // close switch
|
||||
d.p.closeblock() // close for loop
|
||||
}
|
||||
|
||||
func (d *decodeGen) gBase(b *BaseElem) {
|
||||
if !d.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
// open block for 'tmp'
|
||||
var tmp string
|
||||
if b.Convert {
|
||||
tmp = randIdent()
|
||||
d.p.printf("\n{ var %s %s", tmp, b.BaseType())
|
||||
}
|
||||
|
||||
vname := b.Varname() // e.g. "z.FieldOne"
|
||||
bname := b.BaseName() // e.g. "Float64"
|
||||
|
||||
// handle special cases
|
||||
// for object type.
|
||||
switch b.Value {
|
||||
case Bytes:
|
||||
if b.Convert {
|
||||
d.p.printf("\n%s, err = dc.ReadBytes([]byte(%s))", tmp, vname)
|
||||
} else {
|
||||
d.p.printf("\n%s, err = dc.ReadBytes(%s)", vname, vname)
|
||||
}
|
||||
case IDENT:
|
||||
d.p.printf("\nerr = %s.DecodeMsg(dc)", vname)
|
||||
case Ext:
|
||||
d.p.printf("\nerr = dc.ReadExtension(%s)", vname)
|
||||
default:
|
||||
if b.Convert {
|
||||
d.p.printf("\n%s, err = dc.Read%s()", tmp, bname)
|
||||
} else {
|
||||
d.p.printf("\n%s, err = dc.Read%s()", vname, bname)
|
||||
}
|
||||
}
|
||||
d.p.wrapErrCheck(d.ctx.ArgsStr())
|
||||
|
||||
// close block for 'tmp'
|
||||
if b.Convert {
|
||||
if b.ShimMode == Cast {
|
||||
d.p.printf("\n%s = %s(%s)\n}", vname, b.FromBase(), tmp)
|
||||
} else {
|
||||
d.p.printf("\n%s, err = %s(%s)\n}", vname, b.FromBase(), tmp)
|
||||
d.p.wrapErrCheck(d.ctx.ArgsStr())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *decodeGen) gMap(m *Map) {
|
||||
if !d.p.ok() {
|
||||
return
|
||||
}
|
||||
sz := randIdent()
|
||||
|
||||
// resize or allocate map
|
||||
d.p.declare(sz, u32)
|
||||
d.assignAndCheck(sz, mapHeader)
|
||||
d.p.resizeMap(sz, m)
|
||||
|
||||
// for element in map, read string/value
|
||||
// pair and assign
|
||||
d.p.printf("\nfor %s > 0 {\n%s--", sz, sz)
|
||||
d.p.declare(m.Keyidx, "string")
|
||||
d.p.declare(m.Validx, m.Value.TypeName())
|
||||
d.assignAndCheck(m.Keyidx, stringTyp)
|
||||
d.ctx.PushVar(m.Keyidx)
|
||||
next(d, m.Value)
|
||||
d.p.mapAssign(m)
|
||||
d.ctx.Pop()
|
||||
d.p.closeblock()
|
||||
}
|
||||
|
||||
func (d *decodeGen) gSlice(s *Slice) {
|
||||
if !d.p.ok() {
|
||||
return
|
||||
}
|
||||
sz := randIdent()
|
||||
d.p.declare(sz, u32)
|
||||
d.assignAndCheck(sz, arrayHeader)
|
||||
d.p.resizeSlice(sz, s)
|
||||
d.p.rangeBlock(d.ctx, s.Index, s.Varname(), d, s.Els)
|
||||
}
|
||||
|
||||
func (d *decodeGen) gArray(a *Array) {
|
||||
if !d.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
// special case if we have [const]byte
|
||||
if be, ok := a.Els.(*BaseElem); ok && (be.Value == Byte || be.Value == Uint8) {
|
||||
d.p.printf("\nerr = dc.ReadExactBytes((%s)[:])", a.Varname())
|
||||
d.p.wrapErrCheck(d.ctx.ArgsStr())
|
||||
return
|
||||
}
|
||||
sz := randIdent()
|
||||
d.p.declare(sz, u32)
|
||||
d.assignAndCheck(sz, arrayHeader)
|
||||
d.p.arrayCheck(coerceArraySize(a.Size), sz)
|
||||
d.p.rangeBlock(d.ctx, a.Index, a.Varname(), d, a.Els)
|
||||
}
|
||||
|
||||
func (d *decodeGen) gPtr(p *Ptr) {
|
||||
if !d.p.ok() {
|
||||
return
|
||||
}
|
||||
d.p.print("\nif dc.IsNil() {")
|
||||
d.p.print("\nerr = dc.ReadNil()")
|
||||
d.p.wrapErrCheck(d.ctx.ArgsStr())
|
||||
d.p.printf("\n%s = nil\n} else {", p.Varname())
|
||||
d.p.initPtr(p)
|
||||
next(d, p.Value)
|
||||
d.p.closeblock()
|
||||
}
|
||||
739
vendor/github.com/tinylib/msgp/gen/elem.go
сгенерированный
поставляемый
Обычный файл
739
vendor/github.com/tinylib/msgp/gen/elem.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,739 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
identNext = 0
|
||||
identPrefix = "za"
|
||||
)
|
||||
|
||||
func resetIdent(prefix string) {
|
||||
identPrefix = prefix
|
||||
identNext = 0
|
||||
}
|
||||
|
||||
// generate a random identifier name
|
||||
func randIdent() string {
|
||||
identNext++
|
||||
return fmt.Sprintf("%s%04d", identPrefix, identNext)
|
||||
}
|
||||
|
||||
// This code defines the type declaration tree.
|
||||
//
|
||||
// Consider the following:
|
||||
//
|
||||
// type Marshaler struct {
|
||||
// Thing1 *float64 `msg:"thing1"`
|
||||
// Body []byte `msg:"body"`
|
||||
// }
|
||||
//
|
||||
// A parser using this generator as a backend
|
||||
// should parse the above into:
|
||||
//
|
||||
// var val Elem = &Ptr{
|
||||
// name: "z",
|
||||
// Value: &Struct{
|
||||
// Name: "Marshaler",
|
||||
// Fields: []StructField{
|
||||
// {
|
||||
// FieldTag: "thing1",
|
||||
// FieldElem: &Ptr{
|
||||
// name: "z.Thing1",
|
||||
// Value: &BaseElem{
|
||||
// name: "*z.Thing1",
|
||||
// Value: Float64,
|
||||
// Convert: false,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// FieldTag: "body",
|
||||
// FieldElem: &BaseElem{
|
||||
// name: "z.Body",
|
||||
// Value: Bytes,
|
||||
// Convert: false,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
|
||||
// Base is one of the
|
||||
// base types
|
||||
type Primitive uint8
|
||||
|
||||
// this is effectively the
|
||||
// list of currently available
|
||||
// ReadXxxx / WriteXxxx methods.
|
||||
const (
|
||||
Invalid Primitive = iota
|
||||
Bytes
|
||||
String
|
||||
Float32
|
||||
Float64
|
||||
Complex64
|
||||
Complex128
|
||||
Uint
|
||||
Uint8
|
||||
Uint16
|
||||
Uint32
|
||||
Uint64
|
||||
Byte
|
||||
Int
|
||||
Int8
|
||||
Int16
|
||||
Int32
|
||||
Int64
|
||||
Bool
|
||||
Intf // interface{}
|
||||
Time // time.Time
|
||||
Ext // extension
|
||||
|
||||
IDENT // IDENT means an unrecognized identifier
|
||||
)
|
||||
|
||||
// all of the recognized identities
|
||||
// that map to primitive types
|
||||
var primitives = map[string]Primitive{
|
||||
"[]byte": Bytes,
|
||||
"string": String,
|
||||
"float32": Float32,
|
||||
"float64": Float64,
|
||||
"complex64": Complex64,
|
||||
"complex128": Complex128,
|
||||
"uint": Uint,
|
||||
"uint8": Uint8,
|
||||
"uint16": Uint16,
|
||||
"uint32": Uint32,
|
||||
"uint64": Uint64,
|
||||
"byte": Byte,
|
||||
"rune": Int32,
|
||||
"int": Int,
|
||||
"int8": Int8,
|
||||
"int16": Int16,
|
||||
"int32": Int32,
|
||||
"int64": Int64,
|
||||
"bool": Bool,
|
||||
"interface{}": Intf,
|
||||
"time.Time": Time,
|
||||
"msgp.Extension": Ext,
|
||||
}
|
||||
|
||||
// types built into the library
|
||||
// that satisfy all of the
|
||||
// interfaces.
|
||||
var builtins = map[string]struct{}{
|
||||
"msgp.Raw": struct{}{},
|
||||
"msgp.Number": struct{}{},
|
||||
}
|
||||
|
||||
// common data/methods for every Elem
|
||||
type common struct{ vname, alias string }
|
||||
|
||||
func (c *common) SetVarname(s string) { c.vname = s }
|
||||
func (c *common) Varname() string { return c.vname }
|
||||
func (c *common) Alias(typ string) { c.alias = typ }
|
||||
func (c *common) hidden() {}
|
||||
|
||||
func IsPrintable(e Elem) bool {
|
||||
if be, ok := e.(*BaseElem); ok && !be.Printable() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Elem is a go type capable of being
|
||||
// serialized into MessagePack. It is
|
||||
// implemented by *Ptr, *Struct, *Array,
|
||||
// *Slice, *Map, and *BaseElem.
|
||||
type Elem interface {
|
||||
// SetVarname sets this nodes
|
||||
// variable name and recursively
|
||||
// sets the names of all its children.
|
||||
// In general, this should only be
|
||||
// called on the parent of the tree.
|
||||
SetVarname(s string)
|
||||
|
||||
// Varname returns the variable
|
||||
// name of the element.
|
||||
Varname() string
|
||||
|
||||
// TypeName is the canonical
|
||||
// go type name of the node
|
||||
// e.g. "string", "int", "map[string]float64"
|
||||
// OR the alias name, if it has been set.
|
||||
TypeName() string
|
||||
|
||||
// Alias sets a type (alias) name
|
||||
Alias(typ string)
|
||||
|
||||
// Copy should perform a deep copy of the object
|
||||
Copy() Elem
|
||||
|
||||
// Complexity returns a measure of the
|
||||
// complexity of element (greater than
|
||||
// or equal to 1.)
|
||||
Complexity() int
|
||||
|
||||
// ZeroExpr returns the expression for the correct zero/empty
|
||||
// value. Can be used for assignment.
|
||||
// Returns "" if zero/empty not supported for this Elem.
|
||||
ZeroExpr() string
|
||||
|
||||
// IfZeroExpr returns the expression to compare to zero/empty
|
||||
// for this type. It is meant to be used in an if statement
|
||||
// and may include the simple statement form followed by
|
||||
// semicolon and then the expression.
|
||||
// Returns "" if zero/empty not supported for this Elem.
|
||||
IfZeroExpr() string
|
||||
|
||||
hidden()
|
||||
}
|
||||
|
||||
// Ident returns the *BaseElem that corresponds
|
||||
// to the provided identity.
|
||||
func Ident(id string) *BaseElem {
|
||||
p, ok := primitives[id]
|
||||
if ok {
|
||||
return &BaseElem{Value: p}
|
||||
}
|
||||
be := &BaseElem{Value: IDENT}
|
||||
be.Alias(id)
|
||||
return be
|
||||
}
|
||||
|
||||
type Array struct {
|
||||
common
|
||||
Index string // index variable name
|
||||
Size string // array size
|
||||
Els Elem // child
|
||||
}
|
||||
|
||||
func (a *Array) SetVarname(s string) {
|
||||
a.common.SetVarname(s)
|
||||
ridx:
|
||||
a.Index = randIdent()
|
||||
|
||||
// try to avoid using the same
|
||||
// index as a parent slice
|
||||
if strings.Contains(a.Varname(), a.Index) {
|
||||
goto ridx
|
||||
}
|
||||
|
||||
a.Els.SetVarname(fmt.Sprintf("%s[%s]", a.Varname(), a.Index))
|
||||
}
|
||||
|
||||
func (a *Array) TypeName() string {
|
||||
if a.common.alias != "" {
|
||||
return a.common.alias
|
||||
}
|
||||
a.common.Alias(fmt.Sprintf("[%s]%s", a.Size, a.Els.TypeName()))
|
||||
return a.common.alias
|
||||
}
|
||||
|
||||
func (a *Array) Copy() Elem {
|
||||
b := *a
|
||||
b.Els = a.Els.Copy()
|
||||
return &b
|
||||
}
|
||||
|
||||
func (a *Array) Complexity() int { return 1 + a.Els.Complexity() }
|
||||
|
||||
// ZeroExpr returns the zero/empty expression or empty string if not supported. Unsupported for this case.
|
||||
func (a *Array) ZeroExpr() string { return "" }
|
||||
|
||||
// IfZeroExpr unsupported
|
||||
func (a *Array) IfZeroExpr() string { return "" }
|
||||
|
||||
// Map is a map[string]Elem
|
||||
type Map struct {
|
||||
common
|
||||
Keyidx string // key variable name
|
||||
Validx string // value variable name
|
||||
Value Elem // value element
|
||||
}
|
||||
|
||||
func (m *Map) SetVarname(s string) {
|
||||
m.common.SetVarname(s)
|
||||
ridx:
|
||||
m.Keyidx = randIdent()
|
||||
m.Validx = randIdent()
|
||||
|
||||
// just in case
|
||||
if m.Keyidx == m.Validx {
|
||||
goto ridx
|
||||
}
|
||||
|
||||
m.Value.SetVarname(m.Validx)
|
||||
}
|
||||
|
||||
func (m *Map) TypeName() string {
|
||||
if m.common.alias != "" {
|
||||
return m.common.alias
|
||||
}
|
||||
m.common.Alias("map[string]" + m.Value.TypeName())
|
||||
return m.common.alias
|
||||
}
|
||||
|
||||
func (m *Map) Copy() Elem {
|
||||
g := *m
|
||||
g.Value = m.Value.Copy()
|
||||
return &g
|
||||
}
|
||||
|
||||
func (m *Map) Complexity() int { return 2 + m.Value.Complexity() }
|
||||
|
||||
// ZeroExpr returns the zero/empty expression or empty string if not supported. Always "nil" for this case.
|
||||
func (m *Map) ZeroExpr() string { return "nil" }
|
||||
|
||||
// IfZeroExpr returns the expression to compare to zero/empty.
|
||||
func (m *Map) IfZeroExpr() string { return m.Varname() + " == nil" }
|
||||
|
||||
type Slice struct {
|
||||
common
|
||||
Index string
|
||||
Els Elem // The type of each element
|
||||
}
|
||||
|
||||
func (s *Slice) SetVarname(a string) {
|
||||
s.common.SetVarname(a)
|
||||
s.Index = randIdent()
|
||||
varName := s.Varname()
|
||||
if varName[0] == '*' {
|
||||
// Pointer-to-slice requires parenthesis for slicing.
|
||||
varName = "(" + varName + ")"
|
||||
}
|
||||
s.Els.SetVarname(fmt.Sprintf("%s[%s]", varName, s.Index))
|
||||
}
|
||||
|
||||
func (s *Slice) TypeName() string {
|
||||
if s.common.alias != "" {
|
||||
return s.common.alias
|
||||
}
|
||||
s.common.Alias("[]" + s.Els.TypeName())
|
||||
return s.common.alias
|
||||
}
|
||||
|
||||
func (s *Slice) Copy() Elem {
|
||||
z := *s
|
||||
z.Els = s.Els.Copy()
|
||||
return &z
|
||||
}
|
||||
|
||||
func (s *Slice) Complexity() int {
|
||||
return 1 + s.Els.Complexity()
|
||||
}
|
||||
|
||||
// ZeroExpr returns the zero/empty expression or empty string if not supported. Always "nil" for this case.
|
||||
func (s *Slice) ZeroExpr() string { return "nil" }
|
||||
|
||||
// IfZeroExpr returns the expression to compare to zero/empty.
|
||||
func (s *Slice) IfZeroExpr() string { return s.Varname() + " == nil" }
|
||||
|
||||
type Ptr struct {
|
||||
common
|
||||
Value Elem
|
||||
}
|
||||
|
||||
func (s *Ptr) SetVarname(a string) {
|
||||
s.common.SetVarname(a)
|
||||
|
||||
// struct fields are dereferenced
|
||||
// automatically...
|
||||
switch x := s.Value.(type) {
|
||||
case *Struct:
|
||||
// struct fields are automatically dereferenced
|
||||
x.SetVarname(a)
|
||||
return
|
||||
|
||||
case *BaseElem:
|
||||
// identities have pointer receivers
|
||||
if x.Value == IDENT {
|
||||
x.SetVarname(a)
|
||||
} else {
|
||||
x.SetVarname("*" + a)
|
||||
}
|
||||
return
|
||||
|
||||
default:
|
||||
s.Value.SetVarname("*" + a)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Ptr) TypeName() string {
|
||||
if s.common.alias != "" {
|
||||
return s.common.alias
|
||||
}
|
||||
s.common.Alias("*" + s.Value.TypeName())
|
||||
return s.common.alias
|
||||
}
|
||||
|
||||
func (s *Ptr) Copy() Elem {
|
||||
v := *s
|
||||
v.Value = s.Value.Copy()
|
||||
return &v
|
||||
}
|
||||
|
||||
func (s *Ptr) Complexity() int { return 1 + s.Value.Complexity() }
|
||||
|
||||
func (s *Ptr) Needsinit() bool {
|
||||
if be, ok := s.Value.(*BaseElem); ok && be.needsref {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ZeroExpr returns the zero/empty expression or empty string if not supported. Always "nil" for this case.
|
||||
func (s *Ptr) ZeroExpr() string { return "nil" }
|
||||
|
||||
// IfZeroExpr returns the expression to compare to zero/empty.
|
||||
func (s *Ptr) IfZeroExpr() string { return s.Varname() + " == nil" }
|
||||
|
||||
type Struct struct {
|
||||
common
|
||||
Fields []StructField // field list
|
||||
AsTuple bool // write as an array instead of a map
|
||||
}
|
||||
|
||||
func (s *Struct) TypeName() string {
|
||||
if s.common.alias != "" {
|
||||
return s.common.alias
|
||||
}
|
||||
str := "struct{\n"
|
||||
for i := range s.Fields {
|
||||
str += s.Fields[i].FieldName +
|
||||
" " + s.Fields[i].FieldElem.TypeName() +
|
||||
" " + s.Fields[i].RawTag + ";\n"
|
||||
}
|
||||
str += "}"
|
||||
s.common.Alias(str)
|
||||
return s.common.alias
|
||||
}
|
||||
|
||||
func (s *Struct) SetVarname(a string) {
|
||||
s.common.SetVarname(a)
|
||||
writeStructFields(s.Fields, a)
|
||||
}
|
||||
|
||||
func (s *Struct) Copy() Elem {
|
||||
g := *s
|
||||
g.Fields = make([]StructField, len(s.Fields))
|
||||
copy(g.Fields, s.Fields)
|
||||
for i := range s.Fields {
|
||||
g.Fields[i].FieldElem = s.Fields[i].FieldElem.Copy()
|
||||
}
|
||||
return &g
|
||||
}
|
||||
|
||||
func (s *Struct) Complexity() int {
|
||||
c := 1
|
||||
for i := range s.Fields {
|
||||
c += s.Fields[i].FieldElem.Complexity()
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// ZeroExpr returns the zero/empty expression or empty string if not supported.
|
||||
func (s *Struct) ZeroExpr() string {
|
||||
if s.alias == "" {
|
||||
return "" // structs with no names not supported (for now)
|
||||
}
|
||||
return "(" + s.TypeName() + "{})"
|
||||
}
|
||||
|
||||
// IfZeroExpr returns the expression to compare to zero/empty.
|
||||
func (s *Struct) IfZeroExpr() string {
|
||||
if s.alias == "" {
|
||||
return "" // structs with no names not supported (for now)
|
||||
}
|
||||
return s.Varname() + " == " + s.ZeroExpr()
|
||||
}
|
||||
|
||||
// AnyHasTagPart returns true if HasTagPart(p) is true for any field.
|
||||
func (s *Struct) AnyHasTagPart(pname string) bool {
|
||||
for _, sf := range s.Fields {
|
||||
if sf.HasTagPart(pname) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type StructField struct {
|
||||
FieldTag string // the string inside the `msg:""` tag up to the first comma
|
||||
FieldTagParts []string // the string inside the `msg:""` tag split by commas
|
||||
RawTag string // the full struct tag
|
||||
FieldName string // the name of the struct field
|
||||
FieldElem Elem // the field type
|
||||
}
|
||||
|
||||
// HasTagPart returns true if the specified tag part (option) is present.
|
||||
func (sf *StructField) HasTagPart(pname string) bool {
|
||||
if len(sf.FieldTagParts) < 2 {
|
||||
return false
|
||||
}
|
||||
for _, p := range sf.FieldTagParts[1:] {
|
||||
if p == pname {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ShimMode int
|
||||
|
||||
const (
|
||||
Cast ShimMode = iota
|
||||
Convert
|
||||
)
|
||||
|
||||
// BaseElem is an element that
|
||||
// can be represented by a primitive
|
||||
// MessagePack type.
|
||||
type BaseElem struct {
|
||||
common
|
||||
ShimMode ShimMode // Method used to shim
|
||||
ShimToBase string // shim to base type, or empty
|
||||
ShimFromBase string // shim from base type, or empty
|
||||
Value Primitive // Type of element
|
||||
Convert bool // should we do an explicit conversion?
|
||||
mustinline bool // must inline; not printable
|
||||
needsref bool // needs reference for shim
|
||||
}
|
||||
|
||||
func (s *BaseElem) Printable() bool { return !s.mustinline }
|
||||
|
||||
func (s *BaseElem) Alias(typ string) {
|
||||
s.common.Alias(typ)
|
||||
if s.Value != IDENT {
|
||||
s.Convert = true
|
||||
}
|
||||
if strings.Contains(typ, ".") {
|
||||
s.mustinline = true
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BaseElem) SetVarname(a string) {
|
||||
// extensions whose parents
|
||||
// are not pointers need to
|
||||
// be explicitly referenced
|
||||
if s.Value == Ext || s.needsref {
|
||||
if strings.HasPrefix(a, "*") {
|
||||
s.common.SetVarname(a[1:])
|
||||
return
|
||||
}
|
||||
s.common.SetVarname("&" + a)
|
||||
return
|
||||
}
|
||||
|
||||
s.common.SetVarname(a)
|
||||
}
|
||||
|
||||
// TypeName returns the syntactically correct Go
|
||||
// type name for the base element.
|
||||
func (s *BaseElem) TypeName() string {
|
||||
if s.common.alias != "" {
|
||||
return s.common.alias
|
||||
}
|
||||
s.common.Alias(s.BaseType())
|
||||
return s.common.alias
|
||||
}
|
||||
|
||||
// ToBase, used if Convert==true, is used as tmp = {{ToBase}}({{Varname}})
|
||||
func (s *BaseElem) ToBase() string {
|
||||
if s.ShimToBase != "" {
|
||||
return s.ShimToBase
|
||||
}
|
||||
return s.BaseType()
|
||||
}
|
||||
|
||||
// FromBase, used if Convert==true, is used as {{Varname}} = {{FromBase}}(tmp)
|
||||
func (s *BaseElem) FromBase() string {
|
||||
if s.ShimFromBase != "" {
|
||||
return s.ShimFromBase
|
||||
}
|
||||
return s.TypeName()
|
||||
}
|
||||
|
||||
// BaseName returns the string form of the
|
||||
// base type (e.g. Float64, Ident, etc)
|
||||
func (s *BaseElem) BaseName() string {
|
||||
// time is a special case;
|
||||
// we strip the package prefix
|
||||
if s.Value == Time {
|
||||
return "Time"
|
||||
}
|
||||
return s.Value.String()
|
||||
}
|
||||
|
||||
func (s *BaseElem) BaseType() string {
|
||||
switch s.Value {
|
||||
case IDENT:
|
||||
return s.TypeName()
|
||||
|
||||
// exceptions to the naming/capitalization
|
||||
// rule:
|
||||
case Intf:
|
||||
return "interface{}"
|
||||
case Bytes:
|
||||
return "[]byte"
|
||||
case Time:
|
||||
return "time.Time"
|
||||
case Ext:
|
||||
return "msgp.Extension"
|
||||
|
||||
// everything else is base.String() with
|
||||
// the first letter as lowercase
|
||||
default:
|
||||
return strings.ToLower(s.BaseName())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BaseElem) Needsref(b bool) {
|
||||
s.needsref = b
|
||||
}
|
||||
|
||||
func (s *BaseElem) Copy() Elem {
|
||||
g := *s
|
||||
return &g
|
||||
}
|
||||
|
||||
func (s *BaseElem) Complexity() int {
|
||||
if s.Convert && !s.mustinline {
|
||||
return 2
|
||||
}
|
||||
// we need to return 1 if !printable(),
|
||||
// in order to make sure that stuff gets
|
||||
// inlined appropriately
|
||||
return 1
|
||||
}
|
||||
|
||||
// Resolved returns whether or not
|
||||
// the type of the element is
|
||||
// a primitive or a builtin provided
|
||||
// by the package.
|
||||
func (s *BaseElem) Resolved() bool {
|
||||
if s.Value == IDENT {
|
||||
_, ok := builtins[s.TypeName()]
|
||||
return ok
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ZeroExpr returns the zero/empty expression or empty string if not supported.
|
||||
func (s *BaseElem) ZeroExpr() string {
|
||||
|
||||
switch s.Value {
|
||||
case Bytes:
|
||||
return "nil"
|
||||
case String:
|
||||
return "\"\""
|
||||
case Complex64, Complex128:
|
||||
return "complex(0,0)"
|
||||
case Float32,
|
||||
Float64,
|
||||
Uint,
|
||||
Uint8,
|
||||
Uint16,
|
||||
Uint32,
|
||||
Uint64,
|
||||
Byte,
|
||||
Int,
|
||||
Int8,
|
||||
Int16,
|
||||
Int32,
|
||||
Int64:
|
||||
return "0"
|
||||
case Bool:
|
||||
return "false"
|
||||
|
||||
case Time:
|
||||
return "(time.Time{})"
|
||||
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// IfZeroExpr returns the expression to compare to zero/empty.
|
||||
func (s *BaseElem) IfZeroExpr() string {
|
||||
z := s.ZeroExpr()
|
||||
if z == "" {
|
||||
return ""
|
||||
}
|
||||
return s.Varname() + " == " + z
|
||||
}
|
||||
|
||||
func (k Primitive) String() string {
|
||||
switch k {
|
||||
case String:
|
||||
return "String"
|
||||
case Bytes:
|
||||
return "Bytes"
|
||||
case Float32:
|
||||
return "Float32"
|
||||
case Float64:
|
||||
return "Float64"
|
||||
case Complex64:
|
||||
return "Complex64"
|
||||
case Complex128:
|
||||
return "Complex128"
|
||||
case Uint:
|
||||
return "Uint"
|
||||
case Uint8:
|
||||
return "Uint8"
|
||||
case Uint16:
|
||||
return "Uint16"
|
||||
case Uint32:
|
||||
return "Uint32"
|
||||
case Uint64:
|
||||
return "Uint64"
|
||||
case Byte:
|
||||
return "Byte"
|
||||
case Int:
|
||||
return "Int"
|
||||
case Int8:
|
||||
return "Int8"
|
||||
case Int16:
|
||||
return "Int16"
|
||||
case Int32:
|
||||
return "Int32"
|
||||
case Int64:
|
||||
return "Int64"
|
||||
case Bool:
|
||||
return "Bool"
|
||||
case Intf:
|
||||
return "Intf"
|
||||
case Time:
|
||||
return "time.Time"
|
||||
case Ext:
|
||||
return "Extension"
|
||||
case IDENT:
|
||||
return "Ident"
|
||||
default:
|
||||
return "INVALID"
|
||||
}
|
||||
}
|
||||
|
||||
// writeStructFields is a trampoline for writeBase for
|
||||
// all of the fields in a struct
|
||||
func writeStructFields(s []StructField, name string) {
|
||||
for i := range s {
|
||||
s[i].FieldElem.SetVarname(fmt.Sprintf("%s.%s", name, s[i].FieldName))
|
||||
}
|
||||
}
|
||||
|
||||
// coerceArraySize ensures we can compare constant array lengths.
|
||||
//
|
||||
// msgpack array headers are 32 bit unsigned, which is reflected in the
|
||||
// ArrayHeader implementation in this library using uint32. On the Go side, we
|
||||
// can declare array lengths as any constant integer width, which breaks when
|
||||
// attempting a direct comparison to an array header's uint32.
|
||||
//
|
||||
func coerceArraySize(asz string) string {
|
||||
return fmt.Sprintf("uint32(%s)", asz)
|
||||
}
|
||||
270
vendor/github.com/tinylib/msgp/gen/encode.go
сгенерированный
поставляемый
Обычный файл
270
vendor/github.com/tinylib/msgp/gen/encode.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,270 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
func encode(w io.Writer) *encodeGen {
|
||||
return &encodeGen{
|
||||
p: printer{w: w},
|
||||
}
|
||||
}
|
||||
|
||||
type encodeGen struct {
|
||||
passes
|
||||
p printer
|
||||
fuse []byte
|
||||
ctx *Context
|
||||
}
|
||||
|
||||
func (e *encodeGen) Method() Method { return Encode }
|
||||
|
||||
func (e *encodeGen) Apply(dirs []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *encodeGen) writeAndCheck(typ string, argfmt string, arg interface{}) {
|
||||
e.p.printf("\nerr = en.Write%s(%s)", typ, fmt.Sprintf(argfmt, arg))
|
||||
e.p.wrapErrCheck(e.ctx.ArgsStr())
|
||||
}
|
||||
|
||||
func (e *encodeGen) fuseHook() {
|
||||
if len(e.fuse) > 0 {
|
||||
e.appendraw(e.fuse)
|
||||
e.fuse = e.fuse[:0]
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encodeGen) Fuse(b []byte) {
|
||||
if len(e.fuse) > 0 {
|
||||
e.fuse = append(e.fuse, b...)
|
||||
} else {
|
||||
e.fuse = b
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encodeGen) Execute(p Elem) error {
|
||||
if !e.p.ok() {
|
||||
return e.p.err
|
||||
}
|
||||
p = e.applyall(p)
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
if !IsPrintable(p) {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.ctx = &Context{}
|
||||
|
||||
e.p.comment("EncodeMsg implements msgp.Encodable")
|
||||
|
||||
e.p.printf("\nfunc (%s %s) EncodeMsg(en *msgp.Writer) (err error) {", p.Varname(), imutMethodReceiver(p))
|
||||
next(e, p)
|
||||
e.p.nakedReturn()
|
||||
return e.p.err
|
||||
}
|
||||
|
||||
func (e *encodeGen) gStruct(s *Struct) {
|
||||
if !e.p.ok() {
|
||||
return
|
||||
}
|
||||
if s.AsTuple {
|
||||
e.tuple(s)
|
||||
} else {
|
||||
e.structmap(s)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (e *encodeGen) tuple(s *Struct) {
|
||||
nfields := len(s.Fields)
|
||||
data := msgp.AppendArrayHeader(nil, uint32(nfields))
|
||||
e.p.printf("\n// array header, size %d", nfields)
|
||||
e.Fuse(data)
|
||||
if len(s.Fields) == 0 {
|
||||
e.fuseHook()
|
||||
}
|
||||
for i := range s.Fields {
|
||||
if !e.p.ok() {
|
||||
return
|
||||
}
|
||||
e.ctx.PushString(s.Fields[i].FieldName)
|
||||
next(e, s.Fields[i].FieldElem)
|
||||
e.ctx.Pop()
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encodeGen) appendraw(bts []byte) {
|
||||
e.p.print("\nerr = en.Append(")
|
||||
for i, b := range bts {
|
||||
if i != 0 {
|
||||
e.p.print(", ")
|
||||
}
|
||||
e.p.printf("0x%x", b)
|
||||
}
|
||||
e.p.print(")\nif err != nil { return }")
|
||||
}
|
||||
|
||||
func (e *encodeGen) structmap(s *Struct) {
|
||||
|
||||
oeIdentPrefix := randIdent()
|
||||
|
||||
var data []byte
|
||||
nfields := len(s.Fields)
|
||||
bm := bmask{
|
||||
bitlen: nfields,
|
||||
varname: oeIdentPrefix + "Mask",
|
||||
}
|
||||
|
||||
omitempty := s.AnyHasTagPart("omitempty")
|
||||
var fieldNVar string
|
||||
if omitempty {
|
||||
|
||||
fieldNVar = oeIdentPrefix + "Len"
|
||||
|
||||
e.p.printf("\n// omitempty: check for empty values")
|
||||
e.p.printf("\n%s := uint32(%d)", fieldNVar, nfields)
|
||||
e.p.printf("\n%s", bm.typeDecl())
|
||||
for i, sf := range s.Fields {
|
||||
if !e.p.ok() {
|
||||
return
|
||||
}
|
||||
if ize := sf.FieldElem.IfZeroExpr(); ize != "" && sf.HasTagPart("omitempty") {
|
||||
e.p.printf("\nif %s {", ize)
|
||||
e.p.printf("\n%s--", fieldNVar)
|
||||
e.p.printf("\n%s", bm.setStmt(i))
|
||||
e.p.printf("\n}")
|
||||
}
|
||||
}
|
||||
|
||||
e.p.printf("\n// variable map header, size %s", fieldNVar)
|
||||
e.p.varWriteMapHeader("en", fieldNVar, nfields)
|
||||
e.p.print("\nif err != nil { return }")
|
||||
if !e.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
// quick return for the case where the entire thing is empty, but only at the top level
|
||||
if !strings.Contains(s.Varname(), ".") {
|
||||
e.p.printf("\nif %s == 0 { return }", fieldNVar)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// non-omitempty version
|
||||
data = msgp.AppendMapHeader(nil, uint32(nfields))
|
||||
e.p.printf("\n// map header, size %d", nfields)
|
||||
e.Fuse(data)
|
||||
if len(s.Fields) == 0 {
|
||||
e.fuseHook()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for i := range s.Fields {
|
||||
if !e.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
// if field is omitempty, wrap with if statement based on the emptymask
|
||||
oeField := s.Fields[i].HasTagPart("omitempty") && s.Fields[i].FieldElem.IfZeroExpr() != ""
|
||||
if oeField {
|
||||
e.p.printf("\nif %s == 0 { // if not empty", bm.readExpr(i))
|
||||
}
|
||||
|
||||
data = msgp.AppendString(nil, s.Fields[i].FieldTag)
|
||||
e.p.printf("\n// write %q", s.Fields[i].FieldTag)
|
||||
e.Fuse(data)
|
||||
e.fuseHook()
|
||||
|
||||
e.ctx.PushString(s.Fields[i].FieldName)
|
||||
next(e, s.Fields[i].FieldElem)
|
||||
e.ctx.Pop()
|
||||
|
||||
if oeField {
|
||||
e.p.print("\n}") // close if statement
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encodeGen) gMap(m *Map) {
|
||||
if !e.p.ok() {
|
||||
return
|
||||
}
|
||||
e.fuseHook()
|
||||
vname := m.Varname()
|
||||
e.writeAndCheck(mapHeader, lenAsUint32, vname)
|
||||
|
||||
e.p.printf("\nfor %s, %s := range %s {", m.Keyidx, m.Validx, vname)
|
||||
e.writeAndCheck(stringTyp, literalFmt, m.Keyidx)
|
||||
e.ctx.PushVar(m.Keyidx)
|
||||
next(e, m.Value)
|
||||
e.ctx.Pop()
|
||||
e.p.closeblock()
|
||||
}
|
||||
|
||||
func (e *encodeGen) gPtr(s *Ptr) {
|
||||
if !e.p.ok() {
|
||||
return
|
||||
}
|
||||
e.fuseHook()
|
||||
e.p.printf("\nif %s == nil { err = en.WriteNil(); if err != nil { return; } } else {", s.Varname())
|
||||
next(e, s.Value)
|
||||
e.p.closeblock()
|
||||
}
|
||||
|
||||
func (e *encodeGen) gSlice(s *Slice) {
|
||||
if !e.p.ok() {
|
||||
return
|
||||
}
|
||||
e.fuseHook()
|
||||
e.writeAndCheck(arrayHeader, lenAsUint32, s.Varname())
|
||||
e.p.rangeBlock(e.ctx, s.Index, s.Varname(), e, s.Els)
|
||||
}
|
||||
|
||||
func (e *encodeGen) gArray(a *Array) {
|
||||
if !e.p.ok() {
|
||||
return
|
||||
}
|
||||
e.fuseHook()
|
||||
// shortcut for [const]byte
|
||||
if be, ok := a.Els.(*BaseElem); ok && (be.Value == Byte || be.Value == Uint8) {
|
||||
e.p.printf("\nerr = en.WriteBytes((%s)[:])", a.Varname())
|
||||
e.p.wrapErrCheck(e.ctx.ArgsStr())
|
||||
return
|
||||
}
|
||||
|
||||
e.writeAndCheck(arrayHeader, literalFmt, coerceArraySize(a.Size))
|
||||
e.p.rangeBlock(e.ctx, a.Index, a.Varname(), e, a.Els)
|
||||
}
|
||||
|
||||
func (e *encodeGen) gBase(b *BaseElem) {
|
||||
if !e.p.ok() {
|
||||
return
|
||||
}
|
||||
e.fuseHook()
|
||||
vname := b.Varname()
|
||||
if b.Convert {
|
||||
if b.ShimMode == Cast {
|
||||
vname = tobaseConvert(b)
|
||||
} else {
|
||||
vname = randIdent()
|
||||
e.p.printf("\nvar %s %s", vname, b.BaseType())
|
||||
e.p.printf("\n%s, err = %s", vname, tobaseConvert(b))
|
||||
e.p.wrapErrCheck(e.ctx.ArgsStr())
|
||||
}
|
||||
}
|
||||
|
||||
if b.Value == IDENT { // unknown identity
|
||||
e.p.printf("\nerr = %s.EncodeMsg(en)", vname)
|
||||
e.p.wrapErrCheck(e.ctx.ArgsStr())
|
||||
} else { // typical case
|
||||
e.writeAndCheck(b.BaseName(), literalFmt, vname)
|
||||
}
|
||||
}
|
||||
283
vendor/github.com/tinylib/msgp/gen/marshal.go
сгенерированный
поставляемый
Обычный файл
283
vendor/github.com/tinylib/msgp/gen/marshal.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,283 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
func marshal(w io.Writer) *marshalGen {
|
||||
return &marshalGen{
|
||||
p: printer{w: w},
|
||||
}
|
||||
}
|
||||
|
||||
type marshalGen struct {
|
||||
passes
|
||||
p printer
|
||||
fuse []byte
|
||||
ctx *Context
|
||||
}
|
||||
|
||||
func (m *marshalGen) Method() Method { return Marshal }
|
||||
|
||||
func (m *marshalGen) Apply(dirs []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *marshalGen) Execute(p Elem) error {
|
||||
if !m.p.ok() {
|
||||
return m.p.err
|
||||
}
|
||||
p = m.applyall(p)
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
if !IsPrintable(p) {
|
||||
return nil
|
||||
}
|
||||
|
||||
m.ctx = &Context{}
|
||||
|
||||
m.p.comment("MarshalMsg implements msgp.Marshaler")
|
||||
|
||||
// save the vname before
|
||||
// calling methodReceiver so
|
||||
// that z.Msgsize() is printed correctly
|
||||
c := p.Varname()
|
||||
|
||||
m.p.printf("\nfunc (%s %s) MarshalMsg(b []byte) (o []byte, err error) {", p.Varname(), imutMethodReceiver(p))
|
||||
m.p.printf("\no = msgp.Require(b, %s.Msgsize())", c)
|
||||
next(m, p)
|
||||
m.p.nakedReturn()
|
||||
return m.p.err
|
||||
}
|
||||
|
||||
func (m *marshalGen) rawAppend(typ string, argfmt string, arg interface{}) {
|
||||
m.p.printf("\no = msgp.Append%s(o, %s)", typ, fmt.Sprintf(argfmt, arg))
|
||||
}
|
||||
|
||||
func (m *marshalGen) fuseHook() {
|
||||
if len(m.fuse) > 0 {
|
||||
m.rawbytes(m.fuse)
|
||||
m.fuse = m.fuse[:0]
|
||||
}
|
||||
}
|
||||
|
||||
func (m *marshalGen) Fuse(b []byte) {
|
||||
if len(m.fuse) == 0 {
|
||||
m.fuse = b
|
||||
} else {
|
||||
m.fuse = append(m.fuse, b...)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *marshalGen) gStruct(s *Struct) {
|
||||
if !m.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
if s.AsTuple {
|
||||
m.tuple(s)
|
||||
} else {
|
||||
m.mapstruct(s)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m *marshalGen) tuple(s *Struct) {
|
||||
data := make([]byte, 0, 5)
|
||||
data = msgp.AppendArrayHeader(data, uint32(len(s.Fields)))
|
||||
m.p.printf("\n// array header, size %d", len(s.Fields))
|
||||
m.Fuse(data)
|
||||
if len(s.Fields) == 0 {
|
||||
m.fuseHook()
|
||||
}
|
||||
for i := range s.Fields {
|
||||
if !m.p.ok() {
|
||||
return
|
||||
}
|
||||
m.ctx.PushString(s.Fields[i].FieldName)
|
||||
next(m, s.Fields[i].FieldElem)
|
||||
m.ctx.Pop()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *marshalGen) mapstruct(s *Struct) {
|
||||
|
||||
oeIdentPrefix := randIdent()
|
||||
|
||||
var data []byte
|
||||
nfields := len(s.Fields)
|
||||
bm := bmask{
|
||||
bitlen: nfields,
|
||||
varname: oeIdentPrefix + "Mask",
|
||||
}
|
||||
|
||||
omitempty := s.AnyHasTagPart("omitempty")
|
||||
var fieldNVar string
|
||||
if omitempty {
|
||||
|
||||
fieldNVar = oeIdentPrefix + "Len"
|
||||
|
||||
m.p.printf("\n// omitempty: check for empty values")
|
||||
m.p.printf("\n%s := uint32(%d)", fieldNVar, nfields)
|
||||
m.p.printf("\n%s", bm.typeDecl())
|
||||
for i, sf := range s.Fields {
|
||||
if !m.p.ok() {
|
||||
return
|
||||
}
|
||||
if ize := sf.FieldElem.IfZeroExpr(); ize != "" && sf.HasTagPart("omitempty") {
|
||||
m.p.printf("\nif %s {", ize)
|
||||
m.p.printf("\n%s--", fieldNVar)
|
||||
m.p.printf("\n%s", bm.setStmt(i))
|
||||
m.p.printf("\n}")
|
||||
}
|
||||
}
|
||||
|
||||
m.p.printf("\n// variable map header, size %s", fieldNVar)
|
||||
m.p.varAppendMapHeader("o", fieldNVar, nfields)
|
||||
if !m.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
// quick return for the case where the entire thing is empty, but only at the top level
|
||||
if !strings.Contains(s.Varname(), ".") {
|
||||
m.p.printf("\nif %s == 0 { return }", fieldNVar)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// non-omitempty version
|
||||
data = make([]byte, 0, 64)
|
||||
data = msgp.AppendMapHeader(data, uint32(len(s.Fields)))
|
||||
m.p.printf("\n// map header, size %d", len(s.Fields))
|
||||
m.Fuse(data)
|
||||
if len(s.Fields) == 0 {
|
||||
m.fuseHook()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for i := range s.Fields {
|
||||
if !m.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
// if field is omitempty, wrap with if statement based on the emptymask
|
||||
oeField := s.Fields[i].HasTagPart("omitempty") && s.Fields[i].FieldElem.IfZeroExpr() != ""
|
||||
if oeField {
|
||||
m.p.printf("\nif %s == 0 { // if not empty", bm.readExpr(i))
|
||||
}
|
||||
|
||||
data = msgp.AppendString(nil, s.Fields[i].FieldTag)
|
||||
|
||||
m.p.printf("\n// string %q", s.Fields[i].FieldTag)
|
||||
m.Fuse(data)
|
||||
m.fuseHook()
|
||||
|
||||
m.ctx.PushString(s.Fields[i].FieldName)
|
||||
next(m, s.Fields[i].FieldElem)
|
||||
m.ctx.Pop()
|
||||
|
||||
if oeField {
|
||||
m.p.printf("\n}") // close if statement
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// append raw data
|
||||
func (m *marshalGen) rawbytes(bts []byte) {
|
||||
m.p.print("\no = append(o, ")
|
||||
for _, b := range bts {
|
||||
m.p.printf("0x%x,", b)
|
||||
}
|
||||
m.p.print(")")
|
||||
}
|
||||
|
||||
func (m *marshalGen) gMap(s *Map) {
|
||||
if !m.p.ok() {
|
||||
return
|
||||
}
|
||||
m.fuseHook()
|
||||
vname := s.Varname()
|
||||
m.rawAppend(mapHeader, lenAsUint32, vname)
|
||||
m.p.printf("\nfor %s, %s := range %s {", s.Keyidx, s.Validx, vname)
|
||||
m.rawAppend(stringTyp, literalFmt, s.Keyidx)
|
||||
m.ctx.PushVar(s.Keyidx)
|
||||
next(m, s.Value)
|
||||
m.ctx.Pop()
|
||||
m.p.closeblock()
|
||||
}
|
||||
|
||||
func (m *marshalGen) gSlice(s *Slice) {
|
||||
if !m.p.ok() {
|
||||
return
|
||||
}
|
||||
m.fuseHook()
|
||||
vname := s.Varname()
|
||||
m.rawAppend(arrayHeader, lenAsUint32, vname)
|
||||
m.p.rangeBlock(m.ctx, s.Index, vname, m, s.Els)
|
||||
}
|
||||
|
||||
func (m *marshalGen) gArray(a *Array) {
|
||||
if !m.p.ok() {
|
||||
return
|
||||
}
|
||||
m.fuseHook()
|
||||
if be, ok := a.Els.(*BaseElem); ok && be.Value == Byte {
|
||||
m.rawAppend("Bytes", "(%s)[:]", a.Varname())
|
||||
return
|
||||
}
|
||||
|
||||
m.rawAppend(arrayHeader, literalFmt, coerceArraySize(a.Size))
|
||||
m.p.rangeBlock(m.ctx, a.Index, a.Varname(), m, a.Els)
|
||||
}
|
||||
|
||||
func (m *marshalGen) gPtr(p *Ptr) {
|
||||
if !m.p.ok() {
|
||||
return
|
||||
}
|
||||
m.fuseHook()
|
||||
m.p.printf("\nif %s == nil {\no = msgp.AppendNil(o)\n} else {", p.Varname())
|
||||
next(m, p.Value)
|
||||
m.p.closeblock()
|
||||
}
|
||||
|
||||
func (m *marshalGen) gBase(b *BaseElem) {
|
||||
if !m.p.ok() {
|
||||
return
|
||||
}
|
||||
m.fuseHook()
|
||||
vname := b.Varname()
|
||||
|
||||
if b.Convert {
|
||||
if b.ShimMode == Cast {
|
||||
vname = tobaseConvert(b)
|
||||
} else {
|
||||
vname = randIdent()
|
||||
m.p.printf("\nvar %s %s", vname, b.BaseType())
|
||||
m.p.printf("\n%s, err = %s", vname, tobaseConvert(b))
|
||||
m.p.wrapErrCheck(m.ctx.ArgsStr())
|
||||
}
|
||||
}
|
||||
|
||||
var echeck bool
|
||||
switch b.Value {
|
||||
case IDENT:
|
||||
echeck = true
|
||||
m.p.printf("\no, err = %s.MarshalMsg(o)", vname)
|
||||
case Intf, Ext:
|
||||
echeck = true
|
||||
m.p.printf("\no, err = msgp.Append%s(o, %s)", b.BaseName(), vname)
|
||||
default:
|
||||
m.rawAppend(b.BaseName(), literalFmt, vname)
|
||||
}
|
||||
|
||||
if echeck {
|
||||
m.p.wrapErrCheck(m.ctx.ArgsStr())
|
||||
}
|
||||
}
|
||||
292
vendor/github.com/tinylib/msgp/gen/size.go
сгенерированный
поставляемый
Обычный файл
292
vendor/github.com/tinylib/msgp/gen/size.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,292 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/tinylib/msgp/msgp"
|
||||
)
|
||||
|
||||
type sizeState uint8
|
||||
|
||||
const (
|
||||
// need to write "s = ..."
|
||||
assign sizeState = iota
|
||||
|
||||
// need to write "s += ..."
|
||||
add
|
||||
|
||||
// can just append "+ ..."
|
||||
expr
|
||||
)
|
||||
|
||||
func sizes(w io.Writer) *sizeGen {
|
||||
return &sizeGen{
|
||||
p: printer{w: w},
|
||||
state: assign,
|
||||
}
|
||||
}
|
||||
|
||||
type sizeGen struct {
|
||||
passes
|
||||
p printer
|
||||
state sizeState
|
||||
ctx *Context
|
||||
}
|
||||
|
||||
func (s *sizeGen) Method() Method { return Size }
|
||||
|
||||
func (s *sizeGen) Apply(dirs []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func builtinSize(typ string) string {
|
||||
return "msgp." + typ + "Size"
|
||||
}
|
||||
|
||||
// this lets us chain together addition
|
||||
// operations where possible
|
||||
func (s *sizeGen) addConstant(sz string) {
|
||||
if !s.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
switch s.state {
|
||||
case assign:
|
||||
s.p.print("\ns = " + sz)
|
||||
s.state = expr
|
||||
return
|
||||
case add:
|
||||
s.p.print("\ns += " + sz)
|
||||
s.state = expr
|
||||
return
|
||||
case expr:
|
||||
s.p.print(" + " + sz)
|
||||
return
|
||||
}
|
||||
|
||||
panic("unknown size state")
|
||||
}
|
||||
|
||||
func (s *sizeGen) Execute(p Elem) error {
|
||||
if !s.p.ok() {
|
||||
return s.p.err
|
||||
}
|
||||
p = s.applyall(p)
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
if !IsPrintable(p) {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.ctx = &Context{}
|
||||
s.ctx.PushString(p.TypeName())
|
||||
|
||||
s.p.comment("Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message")
|
||||
|
||||
s.p.printf("\nfunc (%s %s) Msgsize() (s int) {", p.Varname(), imutMethodReceiver(p))
|
||||
s.state = assign
|
||||
next(s, p)
|
||||
s.p.nakedReturn()
|
||||
return s.p.err
|
||||
}
|
||||
|
||||
func (s *sizeGen) gStruct(st *Struct) {
|
||||
if !s.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
nfields := uint32(len(st.Fields))
|
||||
|
||||
if st.AsTuple {
|
||||
data := msgp.AppendArrayHeader(nil, nfields)
|
||||
s.addConstant(strconv.Itoa(len(data)))
|
||||
for i := range st.Fields {
|
||||
if !s.p.ok() {
|
||||
return
|
||||
}
|
||||
next(s, st.Fields[i].FieldElem)
|
||||
}
|
||||
} else {
|
||||
data := msgp.AppendMapHeader(nil, nfields)
|
||||
s.addConstant(strconv.Itoa(len(data)))
|
||||
for i := range st.Fields {
|
||||
data = data[:0]
|
||||
data = msgp.AppendString(data, st.Fields[i].FieldTag)
|
||||
s.addConstant(strconv.Itoa(len(data)))
|
||||
next(s, st.Fields[i].FieldElem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sizeGen) gPtr(p *Ptr) {
|
||||
s.state = add // inner must use add
|
||||
s.p.printf("\nif %s == nil {\ns += msgp.NilSize\n} else {", p.Varname())
|
||||
next(s, p.Value)
|
||||
s.state = add // closing block; reset to add
|
||||
s.p.closeblock()
|
||||
}
|
||||
|
||||
func (s *sizeGen) gSlice(sl *Slice) {
|
||||
if !s.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
s.addConstant(builtinSize(arrayHeader))
|
||||
|
||||
// if the slice's element is a fixed size
|
||||
// (e.g. float64, [32]int, etc.), then
|
||||
// print the length times the element size directly
|
||||
if str, ok := fixedsizeExpr(sl.Els); ok {
|
||||
s.addConstant(fmt.Sprintf("(%s * (%s))", lenExpr(sl), str))
|
||||
return
|
||||
}
|
||||
|
||||
// add inside the range block, and immediately after
|
||||
s.state = add
|
||||
s.p.rangeBlock(s.ctx, sl.Index, sl.Varname(), s, sl.Els)
|
||||
s.state = add
|
||||
}
|
||||
|
||||
func (s *sizeGen) gArray(a *Array) {
|
||||
if !s.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
s.addConstant(builtinSize(arrayHeader))
|
||||
|
||||
// if the array's children are a fixed
|
||||
// size, we can compile an expression
|
||||
// that always represents the array's wire size
|
||||
if str, ok := fixedsizeExpr(a); ok {
|
||||
s.addConstant(str)
|
||||
return
|
||||
}
|
||||
|
||||
s.state = add
|
||||
s.p.rangeBlock(s.ctx, a.Index, a.Varname(), s, a.Els)
|
||||
s.state = add
|
||||
}
|
||||
|
||||
func (s *sizeGen) gMap(m *Map) {
|
||||
s.addConstant(builtinSize(mapHeader))
|
||||
vn := m.Varname()
|
||||
s.p.printf("\nif %s != nil {", vn)
|
||||
s.p.printf("\nfor %s, %s := range %s {", m.Keyidx, m.Validx, vn)
|
||||
s.p.printf("\n_ = %s", m.Validx) // we may not use the value
|
||||
s.p.printf("\ns += msgp.StringPrefixSize + len(%s)", m.Keyidx)
|
||||
s.state = expr
|
||||
s.ctx.PushVar(m.Keyidx)
|
||||
next(s, m.Value)
|
||||
s.ctx.Pop()
|
||||
s.p.closeblock()
|
||||
s.p.closeblock()
|
||||
s.state = add
|
||||
}
|
||||
|
||||
func (s *sizeGen) gBase(b *BaseElem) {
|
||||
if !s.p.ok() {
|
||||
return
|
||||
}
|
||||
if b.Convert && b.ShimMode == Convert {
|
||||
s.state = add
|
||||
vname := randIdent()
|
||||
s.p.printf("\nvar %s %s", vname, b.BaseType())
|
||||
|
||||
// ensure we don't get "unused variable" warnings from outer slice iterations
|
||||
s.p.printf("\n_ = %s", b.Varname())
|
||||
|
||||
s.p.printf("\ns += %s", basesizeExpr(b.Value, vname, b.BaseName()))
|
||||
s.state = expr
|
||||
|
||||
} else {
|
||||
vname := b.Varname()
|
||||
if b.Convert {
|
||||
vname = tobaseConvert(b)
|
||||
}
|
||||
s.addConstant(basesizeExpr(b.Value, vname, b.BaseName()))
|
||||
}
|
||||
}
|
||||
|
||||
// returns "len(slice)"
|
||||
func lenExpr(sl *Slice) string {
|
||||
return "len(" + sl.Varname() + ")"
|
||||
}
|
||||
|
||||
// is a given primitive always the same (max)
|
||||
// size on the wire?
|
||||
func fixedSize(p Primitive) bool {
|
||||
switch p {
|
||||
case Intf, Ext, IDENT, Bytes, String:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// strip reference from string
|
||||
func stripRef(s string) string {
|
||||
if s[0] == '&' {
|
||||
return s[1:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// return a fixed-size expression, if possible.
|
||||
// only possible for *BaseElem and *Array.
|
||||
// returns (expr, ok)
|
||||
func fixedsizeExpr(e Elem) (string, bool) {
|
||||
switch e := e.(type) {
|
||||
case *Array:
|
||||
if str, ok := fixedsizeExpr(e.Els); ok {
|
||||
return fmt.Sprintf("(%s * (%s))", e.Size, str), true
|
||||
}
|
||||
case *BaseElem:
|
||||
if fixedSize(e.Value) {
|
||||
return builtinSize(e.BaseName()), true
|
||||
}
|
||||
case *Struct:
|
||||
var str string
|
||||
for _, f := range e.Fields {
|
||||
if fs, ok := fixedsizeExpr(f.FieldElem); ok {
|
||||
if str == "" {
|
||||
str = fs
|
||||
} else {
|
||||
str += "+" + fs
|
||||
}
|
||||
} else {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
var hdrlen int
|
||||
mhdr := msgp.AppendMapHeader(nil, uint32(len(e.Fields)))
|
||||
hdrlen += len(mhdr)
|
||||
var strbody []byte
|
||||
for _, f := range e.Fields {
|
||||
strbody = msgp.AppendString(strbody[:0], f.FieldTag)
|
||||
hdrlen += len(strbody)
|
||||
}
|
||||
return fmt.Sprintf("%d + %s", hdrlen, str), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// print size expression of a variable name
|
||||
func basesizeExpr(value Primitive, vname, basename string) string {
|
||||
switch value {
|
||||
case Ext:
|
||||
return "msgp.ExtensionPrefixSize + " + stripRef(vname) + ".Len()"
|
||||
case Intf:
|
||||
return "msgp.GuessSize(" + vname + ")"
|
||||
case IDENT:
|
||||
return vname + ".Msgsize()"
|
||||
case Bytes:
|
||||
return "msgp.BytesPrefixSize + len(" + vname + ")"
|
||||
case String:
|
||||
return "msgp.StringPrefixSize + len(" + vname + ")"
|
||||
default:
|
||||
return builtinSize(basename)
|
||||
}
|
||||
}
|
||||
519
vendor/github.com/tinylib/msgp/gen/spec.go
сгенерированный
поставляемый
Обычный файл
519
vendor/github.com/tinylib/msgp/gen/spec.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,519 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
lenAsUint32 = "uint32(len(%s))"
|
||||
literalFmt = "%s"
|
||||
intFmt = "%d"
|
||||
quotedFmt = `"%s"`
|
||||
mapHeader = "MapHeader"
|
||||
arrayHeader = "ArrayHeader"
|
||||
mapKey = "MapKeyPtr"
|
||||
stringTyp = "String"
|
||||
u32 = "uint32"
|
||||
)
|
||||
|
||||
// Method is a bitfield representing something that the
|
||||
// generator knows how to print.
|
||||
type Method uint8
|
||||
|
||||
// are the bits in 'f' set in 'm'?
|
||||
func (m Method) isset(f Method) bool { return (m&f == f) }
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (m Method) String() string {
|
||||
switch m {
|
||||
case 0, invalidmeth:
|
||||
return "<invalid method>"
|
||||
case Decode:
|
||||
return "decode"
|
||||
case Encode:
|
||||
return "encode"
|
||||
case Marshal:
|
||||
return "marshal"
|
||||
case Unmarshal:
|
||||
return "unmarshal"
|
||||
case Size:
|
||||
return "size"
|
||||
case Test:
|
||||
return "test"
|
||||
default:
|
||||
// return e.g. "decode+encode+test"
|
||||
modes := [...]Method{Decode, Encode, Marshal, Unmarshal, Size, Test}
|
||||
any := false
|
||||
nm := ""
|
||||
for _, mm := range modes {
|
||||
if m.isset(mm) {
|
||||
if any {
|
||||
nm += "+" + mm.String()
|
||||
} else {
|
||||
nm += mm.String()
|
||||
any = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nm
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func strtoMeth(s string) Method {
|
||||
switch s {
|
||||
case "encode":
|
||||
return Encode
|
||||
case "decode":
|
||||
return Decode
|
||||
case "marshal":
|
||||
return Marshal
|
||||
case "unmarshal":
|
||||
return Unmarshal
|
||||
case "size":
|
||||
return Size
|
||||
case "test":
|
||||
return Test
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
Decode Method = 1 << iota // msgp.Decodable
|
||||
Encode // msgp.Encodable
|
||||
Marshal // msgp.Marshaler
|
||||
Unmarshal // msgp.Unmarshaler
|
||||
Size // msgp.Sizer
|
||||
Test // generate tests
|
||||
invalidmeth // this isn't a method
|
||||
encodetest = Encode | Decode | Test // tests for Encodable and Decodable
|
||||
marshaltest = Marshal | Unmarshal | Test // tests for Marshaler and Unmarshaler
|
||||
)
|
||||
|
||||
type Printer struct {
|
||||
gens []generator
|
||||
}
|
||||
|
||||
func NewPrinter(m Method, out io.Writer, tests io.Writer) *Printer {
|
||||
if m.isset(Test) && tests == nil {
|
||||
panic("cannot print tests with 'nil' tests argument!")
|
||||
}
|
||||
gens := make([]generator, 0, 7)
|
||||
if m.isset(Decode) {
|
||||
gens = append(gens, decode(out))
|
||||
}
|
||||
if m.isset(Encode) {
|
||||
gens = append(gens, encode(out))
|
||||
}
|
||||
if m.isset(Marshal) {
|
||||
gens = append(gens, marshal(out))
|
||||
}
|
||||
if m.isset(Unmarshal) {
|
||||
gens = append(gens, unmarshal(out))
|
||||
}
|
||||
if m.isset(Size) {
|
||||
gens = append(gens, sizes(out))
|
||||
}
|
||||
if m.isset(marshaltest) {
|
||||
gens = append(gens, mtest(tests))
|
||||
}
|
||||
if m.isset(encodetest) {
|
||||
gens = append(gens, etest(tests))
|
||||
}
|
||||
if len(gens) == 0 {
|
||||
panic("NewPrinter called with invalid method flags")
|
||||
}
|
||||
return &Printer{gens: gens}
|
||||
}
|
||||
|
||||
// TransformPass is a pass that transforms individual
|
||||
// elements. (Note that if the returned is different from
|
||||
// the argument, it should not point to the same objects.)
|
||||
type TransformPass func(Elem) Elem
|
||||
|
||||
// IgnoreTypename is a pass that just ignores
|
||||
// types of a given name.
|
||||
func IgnoreTypename(name string) TransformPass {
|
||||
return func(e Elem) Elem {
|
||||
if e.TypeName() == name {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyDirective applies a directive to a named pass
|
||||
// and all of its dependents.
|
||||
func (p *Printer) ApplyDirective(pass Method, t TransformPass) {
|
||||
for _, g := range p.gens {
|
||||
if g.Method().isset(pass) {
|
||||
g.Add(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print prints an Elem.
|
||||
func (p *Printer) Print(e Elem) error {
|
||||
for _, g := range p.gens {
|
||||
// Elem.SetVarname() is called before the Print() step in parse.FileSet.PrintTo().
|
||||
// Elem.SetVarname() generates identifiers as it walks the Elem. This can cause
|
||||
// collisions between idents created during SetVarname and idents created during Print,
|
||||
// hence the separate prefixes.
|
||||
resetIdent("zb")
|
||||
err := g.Execute(e)
|
||||
resetIdent("za")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type contextItem interface {
|
||||
Arg() string
|
||||
}
|
||||
|
||||
type contextString string
|
||||
|
||||
func (c contextString) Arg() string {
|
||||
return fmt.Sprintf("%q", c)
|
||||
}
|
||||
|
||||
type contextVar string
|
||||
|
||||
func (c contextVar) Arg() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
type Context struct {
|
||||
path []contextItem
|
||||
}
|
||||
|
||||
func (c *Context) PushString(s string) {
|
||||
c.path = append(c.path, contextString(s))
|
||||
}
|
||||
|
||||
func (c *Context) PushVar(s string) {
|
||||
c.path = append(c.path, contextVar(s))
|
||||
}
|
||||
|
||||
func (c *Context) Pop() {
|
||||
c.path = c.path[:len(c.path)-1]
|
||||
}
|
||||
|
||||
func (c *Context) ArgsStr() string {
|
||||
var out string
|
||||
for idx, p := range c.path {
|
||||
if idx > 0 {
|
||||
out += ", "
|
||||
}
|
||||
out += p.Arg()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// generator is the interface through
|
||||
// which code is generated.
|
||||
type generator interface {
|
||||
Method() Method
|
||||
Add(p TransformPass)
|
||||
Execute(Elem) error // execute writes the method for the provided object.
|
||||
}
|
||||
|
||||
type passes []TransformPass
|
||||
|
||||
func (p *passes) Add(t TransformPass) {
|
||||
*p = append(*p, t)
|
||||
}
|
||||
|
||||
func (p *passes) applyall(e Elem) Elem {
|
||||
for _, t := range *p {
|
||||
e = t(e)
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
type traversal interface {
|
||||
gMap(*Map)
|
||||
gSlice(*Slice)
|
||||
gArray(*Array)
|
||||
gPtr(*Ptr)
|
||||
gBase(*BaseElem)
|
||||
gStruct(*Struct)
|
||||
}
|
||||
|
||||
// type-switch dispatch to the correct
|
||||
// method given the type of 'e'
|
||||
func next(t traversal, e Elem) {
|
||||
switch e := e.(type) {
|
||||
case *Map:
|
||||
t.gMap(e)
|
||||
case *Struct:
|
||||
t.gStruct(e)
|
||||
case *Slice:
|
||||
t.gSlice(e)
|
||||
case *Array:
|
||||
t.gArray(e)
|
||||
case *Ptr:
|
||||
t.gPtr(e)
|
||||
case *BaseElem:
|
||||
t.gBase(e)
|
||||
default:
|
||||
panic("bad element type")
|
||||
}
|
||||
}
|
||||
|
||||
// possibly-immutable method receiver
|
||||
func imutMethodReceiver(p Elem) string {
|
||||
switch e := p.(type) {
|
||||
case *Struct:
|
||||
// TODO(HACK): actually do real math here.
|
||||
if len(e.Fields) <= 3 {
|
||||
for i := range e.Fields {
|
||||
if be, ok := e.Fields[i].FieldElem.(*BaseElem); !ok || (be.Value == IDENT || be.Value == Bytes) {
|
||||
goto nope
|
||||
}
|
||||
}
|
||||
return p.TypeName()
|
||||
}
|
||||
nope:
|
||||
return "*" + p.TypeName()
|
||||
|
||||
// gets dereferenced automatically
|
||||
case *Array:
|
||||
return "*" + p.TypeName()
|
||||
|
||||
// everything else can be
|
||||
// by-value.
|
||||
default:
|
||||
return p.TypeName()
|
||||
}
|
||||
}
|
||||
|
||||
// if necessary, wraps a type
|
||||
// so that its method receiver
|
||||
// is of the write type.
|
||||
func methodReceiver(p Elem) string {
|
||||
switch p.(type) {
|
||||
|
||||
// structs and arrays are
|
||||
// dereferenced automatically,
|
||||
// so no need to alter varname
|
||||
case *Struct, *Array:
|
||||
return "*" + p.TypeName()
|
||||
// set variable name to
|
||||
// *varname
|
||||
default:
|
||||
p.SetVarname("(*" + p.Varname() + ")")
|
||||
return "*" + p.TypeName()
|
||||
}
|
||||
}
|
||||
|
||||
func unsetReceiver(p Elem) {
|
||||
switch p.(type) {
|
||||
case *Struct, *Array:
|
||||
default:
|
||||
p.SetVarname("z")
|
||||
}
|
||||
}
|
||||
|
||||
// shared utility for generators
|
||||
type printer struct {
|
||||
w io.Writer
|
||||
err error
|
||||
}
|
||||
|
||||
// writes "var {{name}} {{typ}};"
|
||||
func (p *printer) declare(name string, typ string) {
|
||||
p.printf("\nvar %s %s", name, typ)
|
||||
}
|
||||
|
||||
// does:
|
||||
//
|
||||
// if m == nil {
|
||||
// m = make(type, size)
|
||||
// } else if len(m) > 0 {
|
||||
// for key := range m { delete(m, key) }
|
||||
// }
|
||||
//
|
||||
func (p *printer) resizeMap(size string, m *Map) {
|
||||
vn := m.Varname()
|
||||
if !p.ok() {
|
||||
return
|
||||
}
|
||||
p.printf("\nif %s == nil {", vn)
|
||||
p.printf("\n%s = make(%s, %s)", vn, m.TypeName(), size)
|
||||
p.printf("\n} else if len(%s) > 0 {", vn)
|
||||
p.clearMap(vn)
|
||||
p.closeblock()
|
||||
}
|
||||
|
||||
// assign key to value based on varnames
|
||||
func (p *printer) mapAssign(m *Map) {
|
||||
if !p.ok() {
|
||||
return
|
||||
}
|
||||
p.printf("\n%s[%s] = %s", m.Varname(), m.Keyidx, m.Validx)
|
||||
}
|
||||
|
||||
// clear map keys
|
||||
func (p *printer) clearMap(name string) {
|
||||
p.printf("\nfor key := range %[1]s { delete(%[1]s, key) }", name)
|
||||
}
|
||||
|
||||
func (p *printer) wrapErrCheck(ctx string) {
|
||||
p.print("\nif err != nil {")
|
||||
p.printf("\nerr = msgp.WrapError(err, %s)", ctx)
|
||||
p.printf("\nreturn")
|
||||
p.print("\n}")
|
||||
}
|
||||
|
||||
func (p *printer) resizeSlice(size string, s *Slice) {
|
||||
p.printf("\nif cap(%[1]s) >= int(%[2]s) { %[1]s = (%[1]s)[:%[2]s] } else { %[1]s = make(%[3]s, %[2]s) }", s.Varname(), size, s.TypeName())
|
||||
}
|
||||
|
||||
func (p *printer) arrayCheck(want string, got string) {
|
||||
p.printf("\nif %[1]s != %[2]s { err = msgp.ArrayError{Wanted: %[2]s, Got: %[1]s}; return }", got, want)
|
||||
}
|
||||
|
||||
func (p *printer) closeblock() { p.print("\n}") }
|
||||
|
||||
// does:
|
||||
//
|
||||
// for idx := range iter {
|
||||
// {{generate inner}}
|
||||
// }
|
||||
//
|
||||
func (p *printer) rangeBlock(ctx *Context, idx string, iter string, t traversal, inner Elem) {
|
||||
ctx.PushVar(idx)
|
||||
p.printf("\n for %s := range %s {", idx, iter)
|
||||
next(t, inner)
|
||||
p.closeblock()
|
||||
ctx.Pop()
|
||||
}
|
||||
|
||||
func (p *printer) nakedReturn() {
|
||||
if p.ok() {
|
||||
p.print("\nreturn\n}\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) comment(s string) {
|
||||
p.print("\n// " + s)
|
||||
}
|
||||
|
||||
func (p *printer) printf(format string, args ...interface{}) {
|
||||
if p.err == nil {
|
||||
_, p.err = fmt.Fprintf(p.w, format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) print(format string) {
|
||||
if p.err == nil {
|
||||
_, p.err = io.WriteString(p.w, format)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) initPtr(pt *Ptr) {
|
||||
if pt.Needsinit() {
|
||||
vname := pt.Varname()
|
||||
p.printf("\nif %s == nil { %s = new(%s); }", vname, vname, pt.Value.TypeName())
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) ok() bool { return p.err == nil }
|
||||
|
||||
func tobaseConvert(b *BaseElem) string {
|
||||
return b.ToBase() + "(" + b.Varname() + ")"
|
||||
}
|
||||
|
||||
func (p *printer) varWriteMapHeader(receiver string, sizeVarname string, maxSize int) {
|
||||
if maxSize <= 15 {
|
||||
p.printf("\nerr = %s.Append(0x80 | uint8(%s))", receiver, sizeVarname)
|
||||
} else {
|
||||
p.printf("\nerr = %s.WriteMapHeader(%s)", receiver, sizeVarname)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) varAppendMapHeader(sliceVarname string, sizeVarname string, maxSize int) {
|
||||
if maxSize <= 15 {
|
||||
p.printf("\n%s = append(%s, 0x80 | uint8(%s))", sliceVarname, sliceVarname, sizeVarname)
|
||||
} else {
|
||||
p.printf("\n%s = msgp.AppendMapHeader(%s, %s)", sliceVarname, sliceVarname, sizeVarname)
|
||||
}
|
||||
}
|
||||
|
||||
// bmask is a bitmask of a the specified number of bits
|
||||
type bmask struct {
|
||||
bitlen int
|
||||
varname string
|
||||
}
|
||||
|
||||
// typeDecl returns the variable declaration as a var statement
|
||||
func (b *bmask) typeDecl() string {
|
||||
return fmt.Sprintf("var %s %s /* %d bits */", b.varname, b.typeName(), b.bitlen)
|
||||
}
|
||||
|
||||
// typeName returns the type, e.g. "uint8" or "[2]uint64"
|
||||
func (b *bmask) typeName() string {
|
||||
|
||||
if b.bitlen <= 8 {
|
||||
return "uint8"
|
||||
}
|
||||
if b.bitlen <= 16 {
|
||||
return "uint16"
|
||||
}
|
||||
if b.bitlen <= 32 {
|
||||
return "uint32"
|
||||
}
|
||||
if b.bitlen <= 64 {
|
||||
return "uint64"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[%d]uint64", (b.bitlen+64-1)/64)
|
||||
}
|
||||
|
||||
// readExpr returns the expression to read from a position in the bitmask.
|
||||
// Compare ==0 for false or !=0 for true.
|
||||
func (b *bmask) readExpr(bitoffset int) string {
|
||||
|
||||
if bitoffset < 0 || bitoffset >= b.bitlen {
|
||||
panic(fmt.Errorf("bitoffset %d out of range for bitlen %d", bitoffset, b.bitlen))
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Grow(len(b.varname) + 16)
|
||||
buf.WriteByte('(')
|
||||
buf.WriteString(b.varname)
|
||||
if b.bitlen > 64 {
|
||||
fmt.Fprintf(&buf, "[%d]", (bitoffset / 64))
|
||||
}
|
||||
buf.WriteByte('&')
|
||||
fmt.Fprintf(&buf, "0x%X", (uint64(1) << (uint64(bitoffset) % 64)))
|
||||
buf.WriteByte(')')
|
||||
|
||||
return buf.String()
|
||||
|
||||
}
|
||||
|
||||
// setStmt returns the statement to set the specified bit in the bitmask.
|
||||
func (b *bmask) setStmt(bitoffset int) string {
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Grow(len(b.varname) + 16)
|
||||
buf.WriteString(b.varname)
|
||||
if b.bitlen > 64 {
|
||||
fmt.Fprintf(&buf, "[%d]", (bitoffset / 64))
|
||||
}
|
||||
fmt.Fprintf(&buf, " |= 0x%X", (uint64(1) << (uint64(bitoffset) % 64)))
|
||||
|
||||
return buf.String()
|
||||
|
||||
}
|
||||
182
vendor/github.com/tinylib/msgp/gen/testgen.go
сгенерированный
поставляемый
Обычный файл
182
vendor/github.com/tinylib/msgp/gen/testgen.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,182 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"io"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var (
|
||||
marshalTestTempl = template.New("MarshalTest")
|
||||
encodeTestTempl = template.New("EncodeTest")
|
||||
)
|
||||
|
||||
// TODO(philhofer):
|
||||
// for simplicity's sake, right now
|
||||
// we can only generate tests for types
|
||||
// that can be initialized with the
|
||||
// "Type{}" syntax.
|
||||
// we should support all the types.
|
||||
|
||||
func mtest(w io.Writer) *mtestGen {
|
||||
return &mtestGen{w: w}
|
||||
}
|
||||
|
||||
type mtestGen struct {
|
||||
passes
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func (m *mtestGen) Execute(p Elem) error {
|
||||
p = m.applyall(p)
|
||||
if p != nil && IsPrintable(p) {
|
||||
switch p.(type) {
|
||||
case *Struct, *Array, *Slice, *Map:
|
||||
return marshalTestTempl.Execute(m.w, p)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mtestGen) Method() Method { return marshaltest }
|
||||
|
||||
type etestGen struct {
|
||||
passes
|
||||
w io.Writer
|
||||
}
|
||||
|
||||
func etest(w io.Writer) *etestGen {
|
||||
return &etestGen{w: w}
|
||||
}
|
||||
|
||||
func (e *etestGen) Execute(p Elem) error {
|
||||
p = e.applyall(p)
|
||||
if p != nil && IsPrintable(p) {
|
||||
switch p.(type) {
|
||||
case *Struct, *Array, *Slice, *Map:
|
||||
return encodeTestTempl.Execute(e.w, p)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *etestGen) Method() Method { return encodetest }
|
||||
|
||||
func init() {
|
||||
template.Must(marshalTestTempl.Parse(`func TestMarshalUnmarshal{{.TypeName}}(t *testing.T) {
|
||||
v := {{.TypeName}}{}
|
||||
bts, err := v.MarshalMsg(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
left, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left)
|
||||
}
|
||||
|
||||
left, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(left) > 0 {
|
||||
t.Errorf("%d bytes left over after Skip(): %q", len(left), left)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshalMsg{{.TypeName}}(b *testing.B) {
|
||||
v := {{.TypeName}}{}
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
v.MarshalMsg(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAppendMsg{{.TypeName}}(b *testing.B) {
|
||||
v := {{.TypeName}}{}
|
||||
bts := make([]byte, 0, v.Msgsize())
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
bts, _ = v.MarshalMsg(bts[0:0])
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshal{{.TypeName}}(b *testing.B) {
|
||||
v := {{.TypeName}}{}
|
||||
bts, _ := v.MarshalMsg(nil)
|
||||
b.ReportAllocs()
|
||||
b.SetBytes(int64(len(bts)))
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
_, err := v.UnmarshalMsg(bts)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
`))
|
||||
|
||||
template.Must(encodeTestTempl.Parse(`func TestEncodeDecode{{.TypeName}}(t *testing.T) {
|
||||
v := {{.TypeName}}{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
|
||||
m := v.Msgsize()
|
||||
if buf.Len() > m {
|
||||
t.Log("WARNING: TestEncodeDecode{{.TypeName}} Msgsize() is inaccurate")
|
||||
}
|
||||
|
||||
vn := {{.TypeName}}{}
|
||||
err := msgp.Decode(&buf, &vn)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
buf.Reset()
|
||||
msgp.Encode(&buf, &v)
|
||||
err = msgp.NewReader(&buf).Skip()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncode{{.TypeName}}(b *testing.B) {
|
||||
v := {{.TypeName}}{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
en := msgp.NewWriter(msgp.Nowhere)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
v.EncodeMsg(en)
|
||||
}
|
||||
en.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkDecode{{.TypeName}}(b *testing.B) {
|
||||
v := {{.TypeName}}{}
|
||||
var buf bytes.Buffer
|
||||
msgp.Encode(&buf, &v)
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
rd := msgp.NewEndlessReader(buf.Bytes(), b)
|
||||
dc := msgp.NewReader(rd)
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i:=0; i<b.N; i++ {
|
||||
err := v.DecodeMsg(dc)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
`))
|
||||
|
||||
}
|
||||
214
vendor/github.com/tinylib/msgp/gen/unmarshal.go
сгенерированный
поставляемый
Обычный файл
214
vendor/github.com/tinylib/msgp/gen/unmarshal.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,214 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func unmarshal(w io.Writer) *unmarshalGen {
|
||||
return &unmarshalGen{
|
||||
p: printer{w: w},
|
||||
}
|
||||
}
|
||||
|
||||
type unmarshalGen struct {
|
||||
passes
|
||||
p printer
|
||||
hasfield bool
|
||||
ctx *Context
|
||||
}
|
||||
|
||||
func (u *unmarshalGen) Method() Method { return Unmarshal }
|
||||
|
||||
func (u *unmarshalGen) needsField() {
|
||||
if u.hasfield {
|
||||
return
|
||||
}
|
||||
u.p.print("\nvar field []byte; _ = field")
|
||||
u.hasfield = true
|
||||
}
|
||||
|
||||
func (u *unmarshalGen) Execute(p Elem) error {
|
||||
u.hasfield = false
|
||||
if !u.p.ok() {
|
||||
return u.p.err
|
||||
}
|
||||
p = u.applyall(p)
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
if !IsPrintable(p) {
|
||||
return nil
|
||||
}
|
||||
|
||||
u.ctx = &Context{}
|
||||
|
||||
u.p.comment("UnmarshalMsg implements msgp.Unmarshaler")
|
||||
|
||||
u.p.printf("\nfunc (%s %s) UnmarshalMsg(bts []byte) (o []byte, err error) {", p.Varname(), methodReceiver(p))
|
||||
next(u, p)
|
||||
u.p.print("\no = bts")
|
||||
u.p.nakedReturn()
|
||||
unsetReceiver(p)
|
||||
return u.p.err
|
||||
}
|
||||
|
||||
// does assignment to the variable "name" with the type "base"
|
||||
func (u *unmarshalGen) assignAndCheck(name string, base string) {
|
||||
if !u.p.ok() {
|
||||
return
|
||||
}
|
||||
u.p.printf("\n%s, bts, err = msgp.Read%sBytes(bts)", name, base)
|
||||
u.p.wrapErrCheck(u.ctx.ArgsStr())
|
||||
}
|
||||
|
||||
func (u *unmarshalGen) gStruct(s *Struct) {
|
||||
if !u.p.ok() {
|
||||
return
|
||||
}
|
||||
if s.AsTuple {
|
||||
u.tuple(s)
|
||||
} else {
|
||||
u.mapstruct(s)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (u *unmarshalGen) tuple(s *Struct) {
|
||||
|
||||
// open block
|
||||
sz := randIdent()
|
||||
u.p.declare(sz, u32)
|
||||
u.assignAndCheck(sz, arrayHeader)
|
||||
u.p.arrayCheck(strconv.Itoa(len(s.Fields)), sz)
|
||||
for i := range s.Fields {
|
||||
if !u.p.ok() {
|
||||
return
|
||||
}
|
||||
u.ctx.PushString(s.Fields[i].FieldName)
|
||||
next(u, s.Fields[i].FieldElem)
|
||||
u.ctx.Pop()
|
||||
}
|
||||
}
|
||||
|
||||
func (u *unmarshalGen) mapstruct(s *Struct) {
|
||||
u.needsField()
|
||||
sz := randIdent()
|
||||
u.p.declare(sz, u32)
|
||||
u.assignAndCheck(sz, mapHeader)
|
||||
|
||||
u.p.printf("\nfor %s > 0 {", sz)
|
||||
u.p.printf("\n%s--; field, bts, err = msgp.ReadMapKeyZC(bts)", sz)
|
||||
u.p.wrapErrCheck(u.ctx.ArgsStr())
|
||||
u.p.print("\nswitch msgp.UnsafeString(field) {")
|
||||
for i := range s.Fields {
|
||||
if !u.p.ok() {
|
||||
return
|
||||
}
|
||||
u.p.printf("\ncase \"%s\":", s.Fields[i].FieldTag)
|
||||
u.ctx.PushString(s.Fields[i].FieldName)
|
||||
next(u, s.Fields[i].FieldElem)
|
||||
u.ctx.Pop()
|
||||
}
|
||||
u.p.print("\ndefault:\nbts, err = msgp.Skip(bts)")
|
||||
u.p.wrapErrCheck(u.ctx.ArgsStr())
|
||||
u.p.print("\n}\n}") // close switch and for loop
|
||||
}
|
||||
|
||||
func (u *unmarshalGen) gBase(b *BaseElem) {
|
||||
if !u.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
refname := b.Varname() // assigned to
|
||||
lowered := b.Varname() // passed as argument
|
||||
if b.Convert {
|
||||
// begin 'tmp' block
|
||||
refname = randIdent()
|
||||
lowered = b.ToBase() + "(" + lowered + ")"
|
||||
u.p.printf("\n{\nvar %s %s", refname, b.BaseType())
|
||||
}
|
||||
|
||||
switch b.Value {
|
||||
case Bytes:
|
||||
u.p.printf("\n%s, bts, err = msgp.ReadBytesBytes(bts, %s)", refname, lowered)
|
||||
case Ext:
|
||||
u.p.printf("\nbts, err = msgp.ReadExtensionBytes(bts, %s)", lowered)
|
||||
case IDENT:
|
||||
u.p.printf("\nbts, err = %s.UnmarshalMsg(bts)", lowered)
|
||||
default:
|
||||
u.p.printf("\n%s, bts, err = msgp.Read%sBytes(bts)", refname, b.BaseName())
|
||||
}
|
||||
u.p.wrapErrCheck(u.ctx.ArgsStr())
|
||||
|
||||
if b.Convert {
|
||||
// close 'tmp' block
|
||||
if b.ShimMode == Cast {
|
||||
u.p.printf("\n%s = %s(%s)\n", b.Varname(), b.FromBase(), refname)
|
||||
} else {
|
||||
u.p.printf("\n%s, err = %s(%s)", b.Varname(), b.FromBase(), refname)
|
||||
u.p.wrapErrCheck(u.ctx.ArgsStr())
|
||||
}
|
||||
u.p.printf("}")
|
||||
}
|
||||
}
|
||||
|
||||
func (u *unmarshalGen) gArray(a *Array) {
|
||||
if !u.p.ok() {
|
||||
return
|
||||
}
|
||||
|
||||
// special case for [const]byte objects
|
||||
// see decode.go for symmetry
|
||||
if be, ok := a.Els.(*BaseElem); ok && be.Value == Byte {
|
||||
u.p.printf("\nbts, err = msgp.ReadExactBytes(bts, (%s)[:])", a.Varname())
|
||||
u.p.wrapErrCheck(u.ctx.ArgsStr())
|
||||
return
|
||||
}
|
||||
|
||||
sz := randIdent()
|
||||
u.p.declare(sz, u32)
|
||||
u.assignAndCheck(sz, arrayHeader)
|
||||
u.p.arrayCheck(coerceArraySize(a.Size), sz)
|
||||
u.p.rangeBlock(u.ctx, a.Index, a.Varname(), u, a.Els)
|
||||
}
|
||||
|
||||
func (u *unmarshalGen) gSlice(s *Slice) {
|
||||
if !u.p.ok() {
|
||||
return
|
||||
}
|
||||
sz := randIdent()
|
||||
u.p.declare(sz, u32)
|
||||
u.assignAndCheck(sz, arrayHeader)
|
||||
u.p.resizeSlice(sz, s)
|
||||
u.p.rangeBlock(u.ctx, s.Index, s.Varname(), u, s.Els)
|
||||
}
|
||||
|
||||
func (u *unmarshalGen) gMap(m *Map) {
|
||||
if !u.p.ok() {
|
||||
return
|
||||
}
|
||||
sz := randIdent()
|
||||
u.p.declare(sz, u32)
|
||||
u.assignAndCheck(sz, mapHeader)
|
||||
|
||||
// allocate or clear map
|
||||
u.p.resizeMap(sz, m)
|
||||
|
||||
// loop and get key,value
|
||||
u.p.printf("\nfor %s > 0 {", sz)
|
||||
u.p.printf("\nvar %s string; var %s %s; %s--", m.Keyidx, m.Validx, m.Value.TypeName(), sz)
|
||||
u.assignAndCheck(m.Keyidx, stringTyp)
|
||||
u.ctx.PushVar(m.Keyidx)
|
||||
next(u, m.Value)
|
||||
u.ctx.Pop()
|
||||
u.p.mapAssign(m)
|
||||
u.p.closeblock()
|
||||
}
|
||||
|
||||
func (u *unmarshalGen) gPtr(p *Ptr) {
|
||||
u.p.printf("\nif msgp.IsNil(bts) { bts, err = msgp.ReadNilBytes(bts); if err != nil { return }; %s = nil; } else { ", p.Varname())
|
||||
u.p.initPtr(p)
|
||||
next(u, p.Value)
|
||||
u.p.closeblock()
|
||||
}
|
||||
119
vendor/github.com/tinylib/msgp/main.go
сгенерированный
поставляемый
Обычный файл
119
vendor/github.com/tinylib/msgp/main.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,119 @@
|
||||
// msgp is a code generation tool for
|
||||
// creating methods to serialize and de-serialize
|
||||
// Go data structures to and from MessagePack.
|
||||
//
|
||||
// This package is targeted at the `go generate` tool.
|
||||
// To use it, include the following directive in a
|
||||
// go source file with types requiring source generation:
|
||||
//
|
||||
// //go:generate msgp
|
||||
//
|
||||
// The go generate tool should set the proper environment variables for
|
||||
// the generator to execute without any command-line flags. However, the
|
||||
// following options are supported, if you need them:
|
||||
//
|
||||
// -o = output file name (default is {input}_gen.go)
|
||||
// -file = input file name (or directory; default is $GOFILE, which is set by the `go generate` command)
|
||||
// -io = satisfy the `msgp.Decodable` and `msgp.Encodable` interfaces (default is true)
|
||||
// -marshal = satisfy the `msgp.Marshaler` and `msgp.Unmarshaler` interfaces (default is true)
|
||||
// -tests = generate tests and benchmarks (default is true)
|
||||
//
|
||||
// For more information, please read README.md, and the wiki at github.com/tinylib/msgp
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/tinylib/msgp/gen"
|
||||
"github.com/tinylib/msgp/parse"
|
||||
"github.com/tinylib/msgp/printer"
|
||||
"github.com/ttacon/chalk"
|
||||
)
|
||||
|
||||
var (
|
||||
out = flag.String("o", "", "output file")
|
||||
file = flag.String("file", "", "input file")
|
||||
encode = flag.Bool("io", true, "create Encode and Decode methods")
|
||||
marshal = flag.Bool("marshal", true, "create Marshal and Unmarshal methods")
|
||||
tests = flag.Bool("tests", true, "create tests and benchmarks")
|
||||
unexported = flag.Bool("unexported", false, "also process unexported types")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
// GOFILE is set by go generate
|
||||
if *file == "" {
|
||||
*file = os.Getenv("GOFILE")
|
||||
if *file == "" {
|
||||
fmt.Println(chalk.Red.Color("No file to parse."))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var mode gen.Method
|
||||
if *encode {
|
||||
mode |= (gen.Encode | gen.Decode | gen.Size)
|
||||
}
|
||||
if *marshal {
|
||||
mode |= (gen.Marshal | gen.Unmarshal | gen.Size)
|
||||
}
|
||||
if *tests {
|
||||
mode |= gen.Test
|
||||
}
|
||||
|
||||
if mode&^gen.Test == 0 {
|
||||
fmt.Println(chalk.Red.Color("No methods to generate; -io=false && -marshal=false"))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := Run(*file, mode, *unexported); err != nil {
|
||||
fmt.Println(chalk.Red.Color(err.Error()))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Run writes all methods using the associated file or path, e.g.
|
||||
//
|
||||
// err := msgp.Run("path/to/myfile.go", gen.Size|gen.Marshal|gen.Unmarshal|gen.Test, false)
|
||||
//
|
||||
func Run(gofile string, mode gen.Method, unexported bool) error {
|
||||
if mode&^gen.Test == 0 {
|
||||
return nil
|
||||
}
|
||||
fmt.Println(chalk.Magenta.Color("======== MessagePack Code Generator ======="))
|
||||
fmt.Printf(chalk.Magenta.Color(">>> Input: \"%s\"\n"), gofile)
|
||||
fs, err := parse.File(gofile, unexported)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(fs.Identities) == 0 {
|
||||
fmt.Println(chalk.Magenta.Color("No types requiring code generation were found!"))
|
||||
return nil
|
||||
}
|
||||
|
||||
return printer.PrintFile(newFilename(gofile, fs.Package), fs, mode)
|
||||
}
|
||||
|
||||
// picks a new file name based on input flags and input filename(s).
|
||||
func newFilename(old string, pkg string) string {
|
||||
if *out != "" {
|
||||
if pre := strings.TrimPrefix(*out, old); len(pre) > 0 &&
|
||||
!strings.HasSuffix(*out, ".go") {
|
||||
return filepath.Join(old, *out)
|
||||
}
|
||||
return *out
|
||||
}
|
||||
|
||||
if fi, err := os.Stat(old); err == nil && fi.IsDir() {
|
||||
old = filepath.Join(old, pkg)
|
||||
}
|
||||
// new file name is old file name + _gen.go
|
||||
return strings.TrimSuffix(old, ".go") + "_gen.go"
|
||||
}
|
||||
130
vendor/github.com/tinylib/msgp/parse/directives.go
сгенерированный
поставляемый
Обычный файл
130
vendor/github.com/tinylib/msgp/parse/directives.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,130 @@
|
||||
package parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"strings"
|
||||
|
||||
"github.com/tinylib/msgp/gen"
|
||||
)
|
||||
|
||||
const linePrefix = "//msgp:"
|
||||
|
||||
// func(args, fileset)
|
||||
type directive func([]string, *FileSet) error
|
||||
|
||||
// func(passName, args, printer)
|
||||
type passDirective func(gen.Method, []string, *gen.Printer) error
|
||||
|
||||
// map of all recognized directives
|
||||
//
|
||||
// to add a directive, define a func([]string, *FileSet) error
|
||||
// and then add it to this list.
|
||||
var directives = map[string]directive{
|
||||
"shim": applyShim,
|
||||
"ignore": ignore,
|
||||
"tuple": astuple,
|
||||
}
|
||||
|
||||
var passDirectives = map[string]passDirective{
|
||||
"ignore": passignore,
|
||||
}
|
||||
|
||||
func passignore(m gen.Method, text []string, p *gen.Printer) error {
|
||||
pushstate(m.String())
|
||||
for _, a := range text {
|
||||
p.ApplyDirective(m, gen.IgnoreTypename(a))
|
||||
infof("ignoring %s\n", a)
|
||||
}
|
||||
popstate()
|
||||
return nil
|
||||
}
|
||||
|
||||
// find all comment lines that begin with //msgp:
|
||||
func yieldComments(c []*ast.CommentGroup) []string {
|
||||
var out []string
|
||||
for _, cg := range c {
|
||||
for _, line := range cg.List {
|
||||
if strings.HasPrefix(line.Text, linePrefix) {
|
||||
out = append(out, strings.TrimPrefix(line.Text, linePrefix))
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
//msgp:shim {Type} as:{Newtype} using:{toFunc/fromFunc} mode:{Mode}
|
||||
func applyShim(text []string, f *FileSet) error {
|
||||
if len(text) < 4 || len(text) > 5 {
|
||||
return fmt.Errorf("shim directive should have 3 or 4 arguments; found %d", len(text)-1)
|
||||
}
|
||||
|
||||
name := text[1]
|
||||
be := gen.Ident(strings.TrimPrefix(strings.TrimSpace(text[2]), "as:")) // parse as::{base}
|
||||
if name[0] == '*' {
|
||||
name = name[1:]
|
||||
be.Needsref(true)
|
||||
}
|
||||
be.Alias(name)
|
||||
|
||||
usestr := strings.TrimPrefix(strings.TrimSpace(text[3]), "using:") // parse using::{method/method}
|
||||
|
||||
methods := strings.Split(usestr, "/")
|
||||
if len(methods) != 2 {
|
||||
return fmt.Errorf("expected 2 using::{} methods; found %d (%q)", len(methods), text[3])
|
||||
}
|
||||
|
||||
be.ShimToBase = methods[0]
|
||||
be.ShimFromBase = methods[1]
|
||||
|
||||
if len(text) == 5 {
|
||||
modestr := strings.TrimPrefix(strings.TrimSpace(text[4]), "mode:") // parse mode::{mode}
|
||||
switch modestr {
|
||||
case "cast":
|
||||
be.ShimMode = gen.Cast
|
||||
case "convert":
|
||||
be.ShimMode = gen.Convert
|
||||
default:
|
||||
return fmt.Errorf("invalid shim mode; found %s, expected 'cast' or 'convert", modestr)
|
||||
}
|
||||
}
|
||||
|
||||
infof("%s -> %s\n", name, be.Value.String())
|
||||
f.findShim(name, be)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//msgp:ignore {TypeA} {TypeB}...
|
||||
func ignore(text []string, f *FileSet) error {
|
||||
if len(text) < 2 {
|
||||
return nil
|
||||
}
|
||||
for _, item := range text[1:] {
|
||||
name := strings.TrimSpace(item)
|
||||
if _, ok := f.Identities[name]; ok {
|
||||
delete(f.Identities, name)
|
||||
infof("ignoring %s\n", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//msgp:tuple {TypeA} {TypeB}...
|
||||
func astuple(text []string, f *FileSet) error {
|
||||
if len(text) < 2 {
|
||||
return nil
|
||||
}
|
||||
for _, item := range text[1:] {
|
||||
name := strings.TrimSpace(item)
|
||||
if el, ok := f.Identities[name]; ok {
|
||||
if st, ok := el.(*gen.Struct); ok {
|
||||
st.AsTuple = true
|
||||
infoln(name)
|
||||
} else {
|
||||
warnf("%s: only structs can be tuples\n", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
617
vendor/github.com/tinylib/msgp/parse/getast.go
сгенерированный
поставляемый
Обычный файл
617
vendor/github.com/tinylib/msgp/parse/getast.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,617 @@
|
||||
package parse
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/tinylib/msgp/gen"
|
||||
"github.com/ttacon/chalk"
|
||||
)
|
||||
|
||||
// A FileSet is the in-memory representation of a
|
||||
// parsed file.
|
||||
type FileSet struct {
|
||||
Package string // package name
|
||||
Specs map[string]ast.Expr // type specs in file
|
||||
Identities map[string]gen.Elem // processed from specs
|
||||
Directives []string // raw preprocessor directives
|
||||
Imports []*ast.ImportSpec // imports
|
||||
}
|
||||
|
||||
// File parses a file at the relative path
|
||||
// provided and produces a new *FileSet.
|
||||
// If you pass in a path to a directory, the entire
|
||||
// directory will be parsed.
|
||||
// If unexport is false, only exported identifiers are included in the FileSet.
|
||||
// If the resulting FileSet would be empty, an error is returned.
|
||||
func File(name string, unexported bool) (*FileSet, error) {
|
||||
pushstate(name)
|
||||
defer popstate()
|
||||
fs := &FileSet{
|
||||
Specs: make(map[string]ast.Expr),
|
||||
Identities: make(map[string]gen.Elem),
|
||||
}
|
||||
|
||||
fset := token.NewFileSet()
|
||||
finfo, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if finfo.IsDir() {
|
||||
pkgs, err := parser.ParseDir(fset, name, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(pkgs) != 1 {
|
||||
return nil, fmt.Errorf("multiple packages in directory: %s", name)
|
||||
}
|
||||
var one *ast.Package
|
||||
for _, nm := range pkgs {
|
||||
one = nm
|
||||
break
|
||||
}
|
||||
fs.Package = one.Name
|
||||
for _, fl := range one.Files {
|
||||
pushstate(fl.Name.Name)
|
||||
fs.Directives = append(fs.Directives, yieldComments(fl.Comments)...)
|
||||
if !unexported {
|
||||
ast.FileExports(fl)
|
||||
}
|
||||
fs.getTypeSpecs(fl)
|
||||
popstate()
|
||||
}
|
||||
} else {
|
||||
f, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fs.Package = f.Name.Name
|
||||
fs.Directives = yieldComments(f.Comments)
|
||||
if !unexported {
|
||||
ast.FileExports(f)
|
||||
}
|
||||
fs.getTypeSpecs(f)
|
||||
}
|
||||
|
||||
if len(fs.Specs) == 0 {
|
||||
return nil, fmt.Errorf("no definitions in %s", name)
|
||||
}
|
||||
|
||||
fs.process()
|
||||
fs.applyDirectives()
|
||||
fs.propInline()
|
||||
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
// applyDirectives applies all of the directives that
|
||||
// are known to the parser. additional method-specific
|
||||
// directives remain in f.Directives
|
||||
func (f *FileSet) applyDirectives() {
|
||||
newdirs := make([]string, 0, len(f.Directives))
|
||||
for _, d := range f.Directives {
|
||||
chunks := strings.Split(d, " ")
|
||||
if len(chunks) > 0 {
|
||||
if fn, ok := directives[chunks[0]]; ok {
|
||||
pushstate(chunks[0])
|
||||
err := fn(chunks, f)
|
||||
if err != nil {
|
||||
warnln(err.Error())
|
||||
}
|
||||
popstate()
|
||||
} else {
|
||||
newdirs = append(newdirs, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
f.Directives = newdirs
|
||||
}
|
||||
|
||||
// A linkset is a graph of unresolved
|
||||
// identities.
|
||||
//
|
||||
// Since gen.Ident can only represent
|
||||
// one level of type indirection (e.g. Foo -> uint8),
|
||||
// type declarations like `type Foo Bar`
|
||||
// aren't resolve-able until we've processed
|
||||
// everything else.
|
||||
//
|
||||
// The goal of this dependency resolution
|
||||
// is to distill the type declaration
|
||||
// into just one level of indirection.
|
||||
// In other words, if we have:
|
||||
//
|
||||
// type A uint64
|
||||
// type B A
|
||||
// type C B
|
||||
// type D C
|
||||
//
|
||||
// ... then we want to end up
|
||||
// figuring out that D is just a uint64.
|
||||
type linkset map[string]*gen.BaseElem
|
||||
|
||||
func (f *FileSet) resolve(ls linkset) {
|
||||
progress := true
|
||||
for progress && len(ls) > 0 {
|
||||
progress = false
|
||||
for name, elem := range ls {
|
||||
real, ok := f.Identities[elem.TypeName()]
|
||||
if ok {
|
||||
// copy the old type descriptor,
|
||||
// alias it to the new value,
|
||||
// and insert it into the resolved
|
||||
// identities list
|
||||
progress = true
|
||||
nt := real.Copy()
|
||||
nt.Alias(name)
|
||||
f.Identities[name] = nt
|
||||
delete(ls, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// what's left can't be resolved
|
||||
for name, elem := range ls {
|
||||
warnf("couldn't resolve type %s (%s)\n", name, elem.TypeName())
|
||||
}
|
||||
}
|
||||
|
||||
// process takes the contents of f.Specs and
|
||||
// uses them to populate f.Identities
|
||||
func (f *FileSet) process() {
|
||||
|
||||
deferred := make(linkset)
|
||||
parse:
|
||||
for name, def := range f.Specs {
|
||||
pushstate(name)
|
||||
el := f.parseExpr(def)
|
||||
if el == nil {
|
||||
warnln("failed to parse")
|
||||
popstate()
|
||||
continue parse
|
||||
}
|
||||
// push unresolved identities into
|
||||
// the graph of links and resolve after
|
||||
// we've handled every possible named type.
|
||||
if be, ok := el.(*gen.BaseElem); ok && be.Value == gen.IDENT {
|
||||
deferred[name] = be
|
||||
popstate()
|
||||
continue parse
|
||||
}
|
||||
el.Alias(name)
|
||||
f.Identities[name] = el
|
||||
popstate()
|
||||
}
|
||||
|
||||
if len(deferred) > 0 {
|
||||
f.resolve(deferred)
|
||||
}
|
||||
}
|
||||
|
||||
func strToMethod(s string) gen.Method {
|
||||
switch s {
|
||||
case "encode":
|
||||
return gen.Encode
|
||||
case "decode":
|
||||
return gen.Decode
|
||||
case "test":
|
||||
return gen.Test
|
||||
case "size":
|
||||
return gen.Size
|
||||
case "marshal":
|
||||
return gen.Marshal
|
||||
case "unmarshal":
|
||||
return gen.Unmarshal
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FileSet) applyDirs(p *gen.Printer) {
|
||||
// apply directives of the form
|
||||
//
|
||||
// //msgp:encode ignore {{TypeName}}
|
||||
//
|
||||
loop:
|
||||
for _, d := range f.Directives {
|
||||
chunks := strings.Split(d, " ")
|
||||
if len(chunks) > 1 {
|
||||
for i := range chunks {
|
||||
chunks[i] = strings.TrimSpace(chunks[i])
|
||||
}
|
||||
m := strToMethod(chunks[0])
|
||||
if m == 0 {
|
||||
warnf("unknown pass name: %q\n", chunks[0])
|
||||
continue loop
|
||||
}
|
||||
if fn, ok := passDirectives[chunks[1]]; ok {
|
||||
pushstate(chunks[1])
|
||||
err := fn(m, chunks[2:], p)
|
||||
if err != nil {
|
||||
warnf("error applying directive: %s\n", err)
|
||||
}
|
||||
popstate()
|
||||
} else {
|
||||
warnf("unrecognized directive %q\n", chunks[1])
|
||||
}
|
||||
} else {
|
||||
warnf("empty directive: %q\n", d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FileSet) PrintTo(p *gen.Printer) error {
|
||||
f.applyDirs(p)
|
||||
names := make([]string, 0, len(f.Identities))
|
||||
for name := range f.Identities {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
el := f.Identities[name]
|
||||
el.SetVarname("z")
|
||||
pushstate(el.TypeName())
|
||||
err := p.Print(el)
|
||||
popstate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getTypeSpecs extracts all of the *ast.TypeSpecs in the file
|
||||
// into fs.Identities, but does not set the actual element
|
||||
func (fs *FileSet) getTypeSpecs(f *ast.File) {
|
||||
|
||||
// collect all imports...
|
||||
fs.Imports = append(fs.Imports, f.Imports...)
|
||||
|
||||
// check all declarations...
|
||||
for i := range f.Decls {
|
||||
|
||||
// for GenDecls...
|
||||
if g, ok := f.Decls[i].(*ast.GenDecl); ok {
|
||||
|
||||
// and check the specs...
|
||||
for _, s := range g.Specs {
|
||||
|
||||
// for ast.TypeSpecs....
|
||||
if ts, ok := s.(*ast.TypeSpec); ok {
|
||||
switch ts.Type.(type) {
|
||||
|
||||
// this is the list of parse-able
|
||||
// type specs
|
||||
case *ast.StructType,
|
||||
*ast.ArrayType,
|
||||
*ast.StarExpr,
|
||||
*ast.MapType,
|
||||
*ast.Ident:
|
||||
fs.Specs[ts.Name.Name] = ts.Type
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fieldName(f *ast.Field) string {
|
||||
switch len(f.Names) {
|
||||
case 0:
|
||||
return stringify(f.Type)
|
||||
case 1:
|
||||
return f.Names[0].Name
|
||||
default:
|
||||
return f.Names[0].Name + " (and others)"
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *FileSet) parseFieldList(fl *ast.FieldList) []gen.StructField {
|
||||
if fl == nil || fl.NumFields() == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]gen.StructField, 0, fl.NumFields())
|
||||
for _, field := range fl.List {
|
||||
pushstate(fieldName(field))
|
||||
fds := fs.getField(field)
|
||||
if len(fds) > 0 {
|
||||
out = append(out, fds...)
|
||||
} else {
|
||||
warnln("ignored.")
|
||||
}
|
||||
popstate()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// translate *ast.Field into []gen.StructField
|
||||
func (fs *FileSet) getField(f *ast.Field) []gen.StructField {
|
||||
sf := make([]gen.StructField, 1)
|
||||
var extension, flatten bool
|
||||
// parse tag; otherwise field name is field tag
|
||||
if f.Tag != nil {
|
||||
body := reflect.StructTag(strings.Trim(f.Tag.Value, "`")).Get("msg")
|
||||
if body == "" {
|
||||
body = reflect.StructTag(strings.Trim(f.Tag.Value, "`")).Get("msgpack")
|
||||
}
|
||||
tags := strings.Split(body, ",")
|
||||
if len(tags) >= 2 {
|
||||
switch tags[1] {
|
||||
case "extension":
|
||||
extension = true
|
||||
case "flatten":
|
||||
flatten = true
|
||||
}
|
||||
}
|
||||
// ignore "-" fields
|
||||
if tags[0] == "-" {
|
||||
return nil
|
||||
}
|
||||
sf[0].FieldTag = tags[0]
|
||||
sf[0].FieldTagParts = tags
|
||||
sf[0].RawTag = f.Tag.Value
|
||||
}
|
||||
|
||||
ex := fs.parseExpr(f.Type)
|
||||
if ex == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// parse field name
|
||||
switch len(f.Names) {
|
||||
case 0:
|
||||
if flatten {
|
||||
return fs.getFieldsFromEmbeddedStruct(f.Type)
|
||||
} else {
|
||||
sf[0].FieldName = embedded(f.Type)
|
||||
}
|
||||
case 1:
|
||||
sf[0].FieldName = f.Names[0].Name
|
||||
default:
|
||||
// this is for a multiple in-line declaration,
|
||||
// e.g. type A struct { One, Two int }
|
||||
sf = sf[0:0]
|
||||
for _, nm := range f.Names {
|
||||
sf = append(sf, gen.StructField{
|
||||
FieldTag: nm.Name,
|
||||
FieldName: nm.Name,
|
||||
FieldElem: ex.Copy(),
|
||||
})
|
||||
}
|
||||
return sf
|
||||
}
|
||||
sf[0].FieldElem = ex
|
||||
if sf[0].FieldTag == "" {
|
||||
sf[0].FieldTag = sf[0].FieldName
|
||||
sf[0].FieldTagParts = []string{sf[0].FieldName}
|
||||
}
|
||||
|
||||
// validate extension
|
||||
if extension {
|
||||
switch ex := ex.(type) {
|
||||
case *gen.Ptr:
|
||||
if b, ok := ex.Value.(*gen.BaseElem); ok {
|
||||
b.Value = gen.Ext
|
||||
} else {
|
||||
warnln("couldn't cast to extension.")
|
||||
return nil
|
||||
}
|
||||
case *gen.BaseElem:
|
||||
ex.Value = gen.Ext
|
||||
default:
|
||||
warnln("couldn't cast to extension.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return sf
|
||||
}
|
||||
|
||||
func (fs *FileSet) getFieldsFromEmbeddedStruct(f ast.Expr) []gen.StructField {
|
||||
switch f := f.(type) {
|
||||
case *ast.Ident:
|
||||
s := fs.Specs[f.Name]
|
||||
switch s := s.(type) {
|
||||
case *ast.StructType:
|
||||
return fs.parseFieldList(s.Fields)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
// other possibilities are disallowed
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// extract embedded field name
|
||||
//
|
||||
// so, for a struct like
|
||||
//
|
||||
// type A struct {
|
||||
// io.Writer
|
||||
// }
|
||||
//
|
||||
// we want "Writer"
|
||||
func embedded(f ast.Expr) string {
|
||||
switch f := f.(type) {
|
||||
case *ast.Ident:
|
||||
return f.Name
|
||||
case *ast.StarExpr:
|
||||
return embedded(f.X)
|
||||
case *ast.SelectorExpr:
|
||||
return f.Sel.Name
|
||||
default:
|
||||
// other possibilities are disallowed
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// stringify a field type name
|
||||
func stringify(e ast.Expr) string {
|
||||
switch e := e.(type) {
|
||||
case *ast.Ident:
|
||||
return e.Name
|
||||
case *ast.StarExpr:
|
||||
return "*" + stringify(e.X)
|
||||
case *ast.SelectorExpr:
|
||||
return stringify(e.X) + "." + e.Sel.Name
|
||||
case *ast.ArrayType:
|
||||
if e.Len == nil {
|
||||
return "[]" + stringify(e.Elt)
|
||||
}
|
||||
return fmt.Sprintf("[%s]%s", stringify(e.Len), stringify(e.Elt))
|
||||
case *ast.InterfaceType:
|
||||
if e.Methods == nil || e.Methods.NumFields() == 0 {
|
||||
return "interface{}"
|
||||
}
|
||||
}
|
||||
return "<BAD>"
|
||||
}
|
||||
|
||||
// recursively translate ast.Expr to gen.Elem; nil means type not supported
|
||||
// expected input types:
|
||||
// - *ast.MapType (map[T]J)
|
||||
// - *ast.Ident (name)
|
||||
// - *ast.ArrayType ([(sz)]T)
|
||||
// - *ast.StarExpr (*T)
|
||||
// - *ast.StructType (struct {})
|
||||
// - *ast.SelectorExpr (a.B)
|
||||
// - *ast.InterfaceType (interface {})
|
||||
func (fs *FileSet) parseExpr(e ast.Expr) gen.Elem {
|
||||
switch e := e.(type) {
|
||||
|
||||
case *ast.MapType:
|
||||
if k, ok := e.Key.(*ast.Ident); ok && k.Name == "string" {
|
||||
if in := fs.parseExpr(e.Value); in != nil {
|
||||
return &gen.Map{Value: in}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *ast.Ident:
|
||||
b := gen.Ident(e.Name)
|
||||
|
||||
// work to resove this expression
|
||||
// can be done later, once we've resolved
|
||||
// everything else.
|
||||
if b.Value == gen.IDENT {
|
||||
if _, ok := fs.Specs[e.Name]; !ok {
|
||||
warnf("non-local identifier: %s\n", e.Name)
|
||||
}
|
||||
}
|
||||
return b
|
||||
|
||||
case *ast.ArrayType:
|
||||
|
||||
// special case for []byte
|
||||
if e.Len == nil {
|
||||
if i, ok := e.Elt.(*ast.Ident); ok && i.Name == "byte" {
|
||||
return &gen.BaseElem{Value: gen.Bytes}
|
||||
}
|
||||
}
|
||||
|
||||
// return early if we don't know
|
||||
// what the slice element type is
|
||||
els := fs.parseExpr(e.Elt)
|
||||
if els == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// array and not a slice
|
||||
if e.Len != nil {
|
||||
switch s := e.Len.(type) {
|
||||
case *ast.BasicLit:
|
||||
return &gen.Array{
|
||||
Size: s.Value,
|
||||
Els: els,
|
||||
}
|
||||
|
||||
case *ast.Ident:
|
||||
return &gen.Array{
|
||||
Size: s.String(),
|
||||
Els: els,
|
||||
}
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
return &gen.Array{
|
||||
Size: stringify(s),
|
||||
Els: els,
|
||||
}
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return &gen.Slice{Els: els}
|
||||
|
||||
case *ast.StarExpr:
|
||||
if v := fs.parseExpr(e.X); v != nil {
|
||||
return &gen.Ptr{Value: v}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *ast.StructType:
|
||||
return &gen.Struct{Fields: fs.parseFieldList(e.Fields)}
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
return gen.Ident(stringify(e))
|
||||
|
||||
case *ast.InterfaceType:
|
||||
// support `interface{}`
|
||||
if len(e.Methods.List) == 0 {
|
||||
return &gen.BaseElem{Value: gen.Intf}
|
||||
}
|
||||
return nil
|
||||
|
||||
default: // other types not supported
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func infof(s string, v ...interface{}) {
|
||||
pushstate(s)
|
||||
fmt.Printf(chalk.Green.Color(strings.Join(logctx, ": ")), v...)
|
||||
popstate()
|
||||
}
|
||||
|
||||
func infoln(s string) {
|
||||
pushstate(s)
|
||||
fmt.Println(chalk.Green.Color(strings.Join(logctx, ": ")))
|
||||
popstate()
|
||||
}
|
||||
|
||||
func warnf(s string, v ...interface{}) {
|
||||
pushstate(s)
|
||||
fmt.Printf(chalk.Yellow.Color(strings.Join(logctx, ": ")), v...)
|
||||
popstate()
|
||||
}
|
||||
|
||||
func warnln(s string) {
|
||||
pushstate(s)
|
||||
fmt.Println(chalk.Yellow.Color(strings.Join(logctx, ": ")))
|
||||
popstate()
|
||||
}
|
||||
|
||||
func fatalf(s string, v ...interface{}) {
|
||||
pushstate(s)
|
||||
fmt.Printf(chalk.Red.Color(strings.Join(logctx, ": ")), v...)
|
||||
popstate()
|
||||
}
|
||||
|
||||
var logctx []string
|
||||
|
||||
// push logging state
|
||||
func pushstate(s string) {
|
||||
logctx = append(logctx, s)
|
||||
}
|
||||
|
||||
// pop logging state
|
||||
func popstate() {
|
||||
logctx = logctx[:len(logctx)-1]
|
||||
}
|
||||
169
vendor/github.com/tinylib/msgp/parse/inline.go
сгенерированный
поставляемый
Обычный файл
169
vendor/github.com/tinylib/msgp/parse/inline.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,169 @@
|
||||
package parse
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/tinylib/msgp/gen"
|
||||
)
|
||||
|
||||
// This file defines when and how we
|
||||
// propagate type information from
|
||||
// one type declaration to another.
|
||||
// After the processing pass, every
|
||||
// non-primitive type is marshalled/unmarshalled/etc.
|
||||
// through a function call. Here, we propagate
|
||||
// the type information into the caller's type
|
||||
// tree *if* the child type is simple enough.
|
||||
//
|
||||
// For example, types like
|
||||
//
|
||||
// type A [4]int
|
||||
//
|
||||
// will get pushed into parent methods,
|
||||
// whereas types like
|
||||
//
|
||||
// type B [3]map[string]struct{A, B [4]string}
|
||||
//
|
||||
// will not.
|
||||
|
||||
// this is an approximate measure
|
||||
// of the number of children in a node
|
||||
const maxComplex = 5
|
||||
|
||||
// begin recursive search for identities with the
|
||||
// given name and replace them with be
|
||||
func (f *FileSet) findShim(id string, be *gen.BaseElem) {
|
||||
for name, el := range f.Identities {
|
||||
pushstate(name)
|
||||
switch el := el.(type) {
|
||||
case *gen.Struct:
|
||||
for i := range el.Fields {
|
||||
f.nextShim(&el.Fields[i].FieldElem, id, be)
|
||||
}
|
||||
case *gen.Array:
|
||||
f.nextShim(&el.Els, id, be)
|
||||
case *gen.Slice:
|
||||
f.nextShim(&el.Els, id, be)
|
||||
case *gen.Map:
|
||||
f.nextShim(&el.Value, id, be)
|
||||
case *gen.Ptr:
|
||||
f.nextShim(&el.Value, id, be)
|
||||
}
|
||||
popstate()
|
||||
}
|
||||
// we'll need this at the top level as well
|
||||
f.Identities[id] = be
|
||||
}
|
||||
|
||||
func (f *FileSet) nextShim(ref *gen.Elem, id string, be *gen.BaseElem) {
|
||||
if (*ref).TypeName() == id {
|
||||
vn := (*ref).Varname()
|
||||
*ref = be.Copy()
|
||||
(*ref).SetVarname(vn)
|
||||
} else {
|
||||
switch el := (*ref).(type) {
|
||||
case *gen.Struct:
|
||||
for i := range el.Fields {
|
||||
f.nextShim(&el.Fields[i].FieldElem, id, be)
|
||||
}
|
||||
case *gen.Array:
|
||||
f.nextShim(&el.Els, id, be)
|
||||
case *gen.Slice:
|
||||
f.nextShim(&el.Els, id, be)
|
||||
case *gen.Map:
|
||||
f.nextShim(&el.Value, id, be)
|
||||
case *gen.Ptr:
|
||||
f.nextShim(&el.Value, id, be)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// propInline identifies and inlines candidates
|
||||
func (f *FileSet) propInline() {
|
||||
type gelem struct {
|
||||
name string
|
||||
el gen.Elem
|
||||
}
|
||||
|
||||
all := make([]gelem, 0, len(f.Identities))
|
||||
|
||||
for name, el := range f.Identities {
|
||||
all = append(all, gelem{name: name, el: el})
|
||||
}
|
||||
|
||||
// make sure we process inlining determinstically:
|
||||
// start with the least-complex elems;
|
||||
// use identifier names as a tie-breaker
|
||||
sort.Slice(all, func(i, j int) bool {
|
||||
ig, jg := &all[i], &all[j]
|
||||
ic, jc := ig.el.Complexity(), jg.el.Complexity()
|
||||
return ic < jc || (ic == jc && ig.name < jg.name)
|
||||
})
|
||||
|
||||
for i := range all {
|
||||
name := all[i].name
|
||||
pushstate(name)
|
||||
switch el := all[i].el.(type) {
|
||||
case *gen.Struct:
|
||||
for i := range el.Fields {
|
||||
f.nextInline(&el.Fields[i].FieldElem, name)
|
||||
}
|
||||
case *gen.Array:
|
||||
f.nextInline(&el.Els, name)
|
||||
case *gen.Slice:
|
||||
f.nextInline(&el.Els, name)
|
||||
case *gen.Map:
|
||||
f.nextInline(&el.Value, name)
|
||||
case *gen.Ptr:
|
||||
f.nextInline(&el.Value, name)
|
||||
}
|
||||
popstate()
|
||||
}
|
||||
}
|
||||
|
||||
const fatalloop = `detected infinite recursion in inlining loop!
|
||||
Please file a bug at github.com/tinylib/msgp/issues!
|
||||
Thanks!
|
||||
`
|
||||
|
||||
func (f *FileSet) nextInline(ref *gen.Elem, root string) {
|
||||
switch el := (*ref).(type) {
|
||||
case *gen.BaseElem:
|
||||
// ensure that we're not inlining
|
||||
// a type into itself
|
||||
typ := el.TypeName()
|
||||
if el.Value == gen.IDENT && typ != root {
|
||||
if node, ok := f.Identities[typ]; ok && node.Complexity() < maxComplex {
|
||||
infof("inlining %s\n", typ)
|
||||
|
||||
// This should never happen; it will cause
|
||||
// infinite recursion.
|
||||
if node == *ref {
|
||||
panic(fatalloop)
|
||||
}
|
||||
|
||||
*ref = node.Copy()
|
||||
f.nextInline(ref, node.TypeName())
|
||||
} else if !ok && !el.Resolved() {
|
||||
// this is the point at which we're sure that
|
||||
// we've got a type that isn't a primitive,
|
||||
// a library builtin, or a processed type
|
||||
warnf("unresolved identifier: %s\n", typ)
|
||||
}
|
||||
}
|
||||
case *gen.Struct:
|
||||
for i := range el.Fields {
|
||||
f.nextInline(&el.Fields[i].FieldElem, root)
|
||||
}
|
||||
case *gen.Array:
|
||||
f.nextInline(&el.Els, root)
|
||||
case *gen.Slice:
|
||||
f.nextInline(&el.Els, root)
|
||||
case *gen.Map:
|
||||
f.nextInline(&el.Value, root)
|
||||
case *gen.Ptr:
|
||||
f.nextInline(&el.Value, root)
|
||||
default:
|
||||
panic("bad elem type")
|
||||
}
|
||||
}
|
||||
132
vendor/github.com/tinylib/msgp/printer/print.go
сгенерированный
поставляемый
Обычный файл
132
vendor/github.com/tinylib/msgp/printer/print.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,132 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/tinylib/msgp/gen"
|
||||
"github.com/tinylib/msgp/parse"
|
||||
"github.com/ttacon/chalk"
|
||||
"golang.org/x/tools/imports"
|
||||
)
|
||||
|
||||
func infof(s string, v ...interface{}) {
|
||||
fmt.Printf(chalk.Magenta.Color(s), v...)
|
||||
}
|
||||
|
||||
// PrintFile prints the methods for the provided list
|
||||
// of elements to the given file name and canonical
|
||||
// package path.
|
||||
func PrintFile(file string, f *parse.FileSet, mode gen.Method) error {
|
||||
out, tests, err := generate(f, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// we'll run goimports on the main file
|
||||
// in another goroutine, and run it here
|
||||
// for the test file. empirically, this
|
||||
// takes about the same amount of time as
|
||||
// doing them in serial when GOMAXPROCS=1,
|
||||
// and faster otherwise.
|
||||
res := goformat(file, out.Bytes())
|
||||
if tests != nil {
|
||||
testfile := strings.TrimSuffix(file, ".go") + "_test.go"
|
||||
err = format(testfile, tests.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
infof(">>> Wrote and formatted \"%s\"\n", testfile)
|
||||
}
|
||||
err = <-res
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func format(file string, data []byte) error {
|
||||
out, err := imports.Process(file, data, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(file, out, 0600)
|
||||
}
|
||||
|
||||
func goformat(file string, data []byte) <-chan error {
|
||||
out := make(chan error, 1)
|
||||
go func(file string, data []byte, end chan error) {
|
||||
end <- format(file, data)
|
||||
infof(">>> Wrote and formatted \"%s\"\n", file)
|
||||
}(file, data, out)
|
||||
return out
|
||||
}
|
||||
|
||||
func dedupImports(imp []string) []string {
|
||||
m := make(map[string]struct{})
|
||||
for i := range imp {
|
||||
m[imp[i]] = struct{}{}
|
||||
}
|
||||
r := []string{}
|
||||
for k := range m {
|
||||
r = append(r, k)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func generate(f *parse.FileSet, mode gen.Method) (*bytes.Buffer, *bytes.Buffer, error) {
|
||||
outbuf := bytes.NewBuffer(make([]byte, 0, 4096))
|
||||
writePkgHeader(outbuf, f.Package)
|
||||
|
||||
myImports := []string{"github.com/tinylib/msgp/msgp"}
|
||||
for _, imp := range f.Imports {
|
||||
if imp.Name != nil {
|
||||
// have an alias, include it.
|
||||
myImports = append(myImports, imp.Name.Name+` `+imp.Path.Value)
|
||||
} else {
|
||||
myImports = append(myImports, imp.Path.Value)
|
||||
}
|
||||
}
|
||||
dedup := dedupImports(myImports)
|
||||
writeImportHeader(outbuf, dedup...)
|
||||
|
||||
var testbuf *bytes.Buffer
|
||||
var testwr io.Writer
|
||||
if mode&gen.Test == gen.Test {
|
||||
testbuf = bytes.NewBuffer(make([]byte, 0, 4096))
|
||||
writePkgHeader(testbuf, f.Package)
|
||||
if mode&(gen.Encode|gen.Decode) != 0 {
|
||||
writeImportHeader(testbuf, "bytes", "github.com/tinylib/msgp/msgp", "testing")
|
||||
} else {
|
||||
writeImportHeader(testbuf, "github.com/tinylib/msgp/msgp", "testing")
|
||||
}
|
||||
testwr = testbuf
|
||||
}
|
||||
return outbuf, testbuf, f.PrintTo(gen.NewPrinter(mode, outbuf, testwr))
|
||||
}
|
||||
|
||||
func writePkgHeader(b *bytes.Buffer, name string) {
|
||||
b.WriteString("package ")
|
||||
b.WriteString(name)
|
||||
b.WriteByte('\n')
|
||||
// write generated code marker
|
||||
// https://github.com/tinylib/msgp/issues/229
|
||||
// https://golang.org/s/generatedcode
|
||||
b.WriteString("// Code generated by github.com/tinylib/msgp DO NOT EDIT.\n\n")
|
||||
}
|
||||
|
||||
func writeImportHeader(b *bytes.Buffer, imports ...string) {
|
||||
b.WriteString("import (\n")
|
||||
for _, im := range imports {
|
||||
if im[len(im)-1] == '"' {
|
||||
// support aliased imports
|
||||
fmt.Fprintf(b, "\t%s\n", im)
|
||||
} else {
|
||||
fmt.Fprintf(b, "\t%q\n", im)
|
||||
}
|
||||
}
|
||||
b.WriteString(")\n\n")
|
||||
}
|
||||
23
vendor/github.com/ttacon/chalk/.gitignore
сгенерированный
поставляемый
Обычный файл
23
vendor/github.com/ttacon/chalk/.gitignore
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,23 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
21
vendor/github.com/ttacon/chalk/LICENSE
сгенерированный
поставляемый
Обычный файл
21
vendor/github.com/ttacon/chalk/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Trey Tacon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
67
vendor/github.com/ttacon/chalk/README.md
сгенерированный
поставляемый
Обычный файл
67
vendor/github.com/ttacon/chalk/README.md
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,67 @@
|
||||
chalk
|
||||
=============
|
||||
|
||||
Chalk is a go package for styling console/terminal output.
|
||||
|
||||
Check out godoc for some example usage:
|
||||
http://godoc.org/github.com/ttacon/chalk
|
||||
|
||||
The api is pretty clean, there are default Colors and TextStyles
|
||||
which can be mixed to create more intense Styles. Styles and Colors
|
||||
can be printed in normal strings (i.e. ```fmt.Sprintf(chalk.Red)```), but
|
||||
Styles, Colors and TextStyles are more meant to be used to style specific
|
||||
text segments (i.e. ```fmt.Println(chalk.Red.Color("this is red")```) or
|
||||
```fmt.Println(myStyle.Style("this is blue text that is underlined"))```).
|
||||
|
||||
Examples
|
||||
=============
|
||||
|
||||
There are a few examples in the examples directory if you want to see a very
|
||||
simplified version of what you can do with chalk.
|
||||
|
||||
The following code:
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ttacon/chalk"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// You can just use colors
|
||||
fmt.Println(chalk.Red, "Writing in colors", chalk.Cyan, "is so much fun", chalk.Reset)
|
||||
fmt.Println(chalk.Magenta.Color("You can use colors to color specific phrases"))
|
||||
|
||||
// You can just use text styles
|
||||
fmt.Println(chalk.Bold.TextStyle("We can have bold text"))
|
||||
fmt.Println(chalk.Underline.TextStyle("We can have underlined text"))
|
||||
fmt.Println(chalk.Bold, "But text styles don't work quite like colors :(")
|
||||
|
||||
// Or you can use styles
|
||||
blueOnWhite := chalk.Blue.NewStyle().WithBackground(chalk.White)
|
||||
fmt.Printf("%s%s%s\n", blueOnWhite, "And they also have backgrounds!", chalk.Reset)
|
||||
fmt.Println(
|
||||
blueOnWhite.Style("You can style strings the same way you can color them!"))
|
||||
fmt.Println(
|
||||
blueOnWhite.WithTextStyle(chalk.Bold).
|
||||
Style("You can mix text styles with colors, too!"))
|
||||
|
||||
// You can also easily make styling functions thanks to go's functional side
|
||||
lime := chalk.Green.NewStyle().
|
||||
WithBackground(chalk.Black).
|
||||
WithTextStyle(chalk.Bold).
|
||||
Style
|
||||
fmt.Println(lime("look at this cool lime text!"))
|
||||
}
|
||||
|
||||
```
|
||||
Outputs
|
||||

|
||||
|
||||
|
||||
WARNING
|
||||
=============
|
||||
|
||||
This package should be pretty stable (I don't forsee backwards incompatible changes), but I'm not making any promises :)
|
||||
162
vendor/github.com/ttacon/chalk/chalk.go
сгенерированный
поставляемый
Обычный файл
162
vendor/github.com/ttacon/chalk/chalk.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,162 @@
|
||||
package chalk
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Color represents one of the ANSI color escape codes.
|
||||
// http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
||||
type Color struct {
|
||||
value int
|
||||
}
|
||||
|
||||
// Value returns the individual value for this color
|
||||
// (Actually it's really just its index in the list
|
||||
// of color escape codes with the list being
|
||||
// [black, red, green, yellow, blue, magenta, cyan, white].
|
||||
func (c Color) Value() int {
|
||||
return c.value
|
||||
}
|
||||
|
||||
// Color colors the foreground of the given string
|
||||
// (whatever the previous background color was, it is
|
||||
// left alone).
|
||||
func (c Color) Color(val string) string {
|
||||
return fmt.Sprintf("%s%s%s", c, val, ResetColor)
|
||||
}
|
||||
|
||||
func (c Color) String() string {
|
||||
return fmt.Sprintf("\u001b[%dm", 30+c.value)
|
||||
}
|
||||
|
||||
// NewStyle creates a style with a foreground of the
|
||||
// color we're creating the style from.
|
||||
func (c Color) NewStyle() Style {
|
||||
return &style{foreground: c}
|
||||
}
|
||||
|
||||
type textStyleDemarcation int
|
||||
|
||||
func (t textStyleDemarcation) String() string {
|
||||
return fmt.Sprintf("\u001b[%dm", t)
|
||||
}
|
||||
|
||||
// A TextStyle represents the ways we can style the text:
|
||||
// bold, dim, italic, underline, inverse, hidden or strikethrough.
|
||||
type TextStyle struct {
|
||||
start, stop textStyleDemarcation
|
||||
}
|
||||
|
||||
// TextStyle styles the given string using the desired text style.
|
||||
func (t TextStyle) TextStyle(val string) string {
|
||||
if t == emptyTextStyle {
|
||||
return val
|
||||
}
|
||||
return fmt.Sprintf("%s%s%s", t.start, val, t.stop)
|
||||
}
|
||||
|
||||
// NOTE: this function specifically does not work as desired because
|
||||
// text styles must be wrapped around the text they are meant to style.
|
||||
// As such, use TextStyle() or Style.Style() instead.
|
||||
func (t TextStyle) String() string {
|
||||
return fmt.Sprintf("%s%s", t.start, t.stop)
|
||||
}
|
||||
|
||||
// NewStyle creates a style starting with the current TextStyle
|
||||
// as its text style.
|
||||
func (t TextStyle) NewStyle() Style {
|
||||
return &style{textStyle: t}
|
||||
}
|
||||
|
||||
// A Style is how we want our text to look in the console.
|
||||
// Consequently, we can set the foreground and background
|
||||
// to specific colors, we can style specific strings and
|
||||
// can also use this style in a builder pattern should we
|
||||
// wish (these will be more useful once styles such as
|
||||
// italics are supported).
|
||||
type Style interface {
|
||||
// Foreground sets the foreground of the style to the specific color.
|
||||
Foreground(Color)
|
||||
// Background sets the background of the style to the specific color.
|
||||
Background(Color)
|
||||
// Style styles the given string with the current style.
|
||||
Style(string) string
|
||||
// WithBackground allows us to set the background in a builder
|
||||
// pattern style.
|
||||
WithBackground(Color) Style
|
||||
// WithForeground allows us to set the foreground in a builder
|
||||
// pattern style.
|
||||
WithForeground(Color) Style
|
||||
// WithStyle allows us to set the text style in a builder pattern
|
||||
// style.
|
||||
WithTextStyle(TextStyle) Style
|
||||
String() string
|
||||
}
|
||||
|
||||
type style struct {
|
||||
foreground Color
|
||||
background Color
|
||||
textStyle TextStyle
|
||||
}
|
||||
|
||||
func (s *style) WithBackground(col Color) Style {
|
||||
s.Background(col)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *style) WithForeground(col Color) Style {
|
||||
s.Foreground(col)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *style) String() string {
|
||||
var toReturn string
|
||||
toReturn = fmt.Sprintf("\u001b[%dm", 40+s.background.Value())
|
||||
return toReturn + fmt.Sprintf("\u001b[%dm", 30+s.foreground.Value())
|
||||
}
|
||||
|
||||
func (s *style) Style(val string) string {
|
||||
return fmt.Sprintf("%s%s%s", s, s.textStyle.TextStyle(val), Reset)
|
||||
}
|
||||
|
||||
func (s *style) Foreground(col Color) {
|
||||
s.foreground = col
|
||||
}
|
||||
|
||||
func (s *style) Background(col Color) {
|
||||
s.background = col
|
||||
}
|
||||
|
||||
func (s *style) WithTextStyle(textStyle TextStyle) Style {
|
||||
s.textStyle = textStyle
|
||||
return s
|
||||
}
|
||||
|
||||
var (
|
||||
// Colors
|
||||
|
||||
Black = Color{0}
|
||||
Red = Color{1}
|
||||
Green = Color{2}
|
||||
Yellow = Color{3}
|
||||
Blue = Color{4}
|
||||
Magenta = Color{5}
|
||||
Cyan = Color{6}
|
||||
White = Color{7}
|
||||
ResetColor = Color{9}
|
||||
|
||||
// Text Styles
|
||||
|
||||
Bold = TextStyle{1, 22}
|
||||
Dim = TextStyle{2, 22}
|
||||
Italic = TextStyle{3, 23}
|
||||
Underline = TextStyle{4, 24}
|
||||
Inverse = TextStyle{7, 27}
|
||||
Hidden = TextStyle{8, 28}
|
||||
Strikethrough = TextStyle{9, 29}
|
||||
|
||||
Reset = &style{
|
||||
foreground: ResetColor,
|
||||
background: ResetColor,
|
||||
}
|
||||
|
||||
emptyTextStyle = TextStyle{}
|
||||
)
|
||||
59
vendor/github.com/ttacon/chalk/doc.go
сгенерированный
поставляемый
Обычный файл
59
vendor/github.com/ttacon/chalk/doc.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,59 @@
|
||||
// Package chalk is a package for styling terminal/console output.
|
||||
// There are three main components:
|
||||
//
|
||||
//
|
||||
// Colors
|
||||
//
|
||||
// There are eight default colors: black, red, green, yellow, blue,
|
||||
// magenta, cyan and white. You can use them in two main ways
|
||||
// (note the need for the reset color if you don't use Color()):
|
||||
//
|
||||
// fmt.Println(chalk.Red, "this is red text", chalk.ResetColor)
|
||||
// fmt.Println(chalk.Red.Color("this is red text")
|
||||
//
|
||||
//
|
||||
// TextStyles
|
||||
//
|
||||
// There are seven default text styles: bold, dim, italic, underline,
|
||||
// inverse, hidden and strikethrough. Unlike colors, you should only
|
||||
// really use TextStyles in the following manner:
|
||||
//
|
||||
// fmt.Println(chalk.Bold.TextStyle("this is bold text"))
|
||||
//
|
||||
//
|
||||
// Styles
|
||||
//
|
||||
// Styles are where all the business really is. Styles can have a
|
||||
// foreground color, a background color and a text style (sweet!).
|
||||
// They're also pretty simply to make, you just need a starting point:
|
||||
//
|
||||
// blue := chalk.Blue.NewStyle()
|
||||
// bold := chalk.Bold.NewStyle()
|
||||
//
|
||||
// When a color is your starting point for a style, it will be the
|
||||
// foreground color, when a style is your starting point, well, yeah,
|
||||
// it's your style's text style. You can also alter a style's foreground,
|
||||
// background or text style in a builder-esque pattern.
|
||||
//
|
||||
// blueOnWhite := blue.WithBackground(chalk.White)
|
||||
// awesomeness := blueOnWhite.WithTextStyle(chalk.Underline).WithForeground(chalk.Green)
|
||||
//
|
||||
// Like both Colors and TextStyles you can style specific segments of text
|
||||
// with:
|
||||
//
|
||||
// fmt.Println(awesomeness.Style("this is so pretty!"))
|
||||
//
|
||||
// Like Colors, you can also print styles explicitly, but you'll need to
|
||||
// reset your console's colors with chalk.Reset if you use them this way:
|
||||
//
|
||||
// fmt.Println(awesomeness, "this is so pretty", chalk.Reset)
|
||||
//
|
||||
// Be aware though, that this (second) way of using styles will not add the
|
||||
// text style (as text styles require more specific end codes). So if you want
|
||||
// to fully utilize styles, use myStyle.Style() (unless you only care about
|
||||
// print your text with a specific foreground and background, then printing
|
||||
// the style is awesome too!).
|
||||
//
|
||||
// Have fun!
|
||||
//
|
||||
package chalk
|
||||
7
vendor/modules.txt
поставляемый
7
vendor/modules.txt
поставляемый
@@ -471,7 +471,14 @@ github.com/throttled/throttled
|
||||
github.com/throttled/throttled/store/memstore
|
||||
# github.com/tinylib/msgp v1.1.2
|
||||
## explicit
|
||||
github.com/tinylib/msgp
|
||||
github.com/tinylib/msgp/gen
|
||||
github.com/tinylib/msgp/msgp
|
||||
github.com/tinylib/msgp/parse
|
||||
github.com/tinylib/msgp/printer
|
||||
# github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31
|
||||
## explicit
|
||||
github.com/ttacon/chalk
|
||||
# github.com/tylerb/graceful v1.2.15
|
||||
## explicit
|
||||
github.com/tylerb/graceful
|
||||
|
||||
Ссылка в новой задаче
Block a user