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\""; \
|
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; \
|
exit 1; \
|
||||||
fi;
|
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 \
|
if ! [ -z "${MM_VET_OPENSPEC_PATH}" ] && [ -f "${MM_VET_OPENSPEC_PATH}" ]; then \
|
||||||
VET_CMD="$$VET_CMD -openApiSync -openApiSync.spec=$$MM_VET_OPENSPEC_PATH"; \
|
VET_CMD="$$VET_CMD -openApiSync -openApiSync.spec=$$MM_VET_OPENSPEC_PATH"; \
|
||||||
else \
|
else \
|
||||||
@@ -567,6 +567,13 @@ ifneq ($(MM_NO_ENTERPRISE_LINT),true)
|
|||||||
endif
|
endif
|
||||||
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.
|
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 TODO
|
||||||
@! ag --ignore Makefile --ignore-dir vendor --ignore-dir runtime XXX
|
@! 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/stretchr/testify v1.6.1
|
||||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
|
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
|
||||||
github.com/throttled/throttled v2.2.4+incompatible
|
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/tylerb/graceful v1.2.15
|
||||||
github.com/uber/jaeger-client-go v2.24.0+incompatible
|
github.com/uber/jaeger-client-go v2.24.0+incompatible
|
||||||
github.com/uber/jaeger-lib v2.2.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 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ=
|
||||||
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
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/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/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 h1:B0x01Y8fsJpogzZTkDg6BDi6eMf03s01lEKGdrv83oA=
|
||||||
github.com/tylerb/graceful v1.2.15/go.mod h1:LPYTbOYmUTdabwRt0TGhLllQ0MUNbs0Y5q1WXJOI9II=
|
github.com/tylerb/graceful v1.2.15/go.mod h1:LPYTbOYmUTdabwRt0TGhLllQ0MUNbs0Y5q1WXJOI9II=
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ go 1.14
|
|||||||
require (
|
require (
|
||||||
github.com/jstemmer/go-junit-report v0.9.1 // indirect
|
github.com/jstemmer/go-junit-report v0.9.1 // indirect
|
||||||
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200730135456-e2334fe87160 // 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/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
|
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/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/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/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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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/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 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
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/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 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/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
github.com/vektra/mockery v1.1.2 h1:uc0Yn67rJpjt8U/mAZimdCKn9AeA97BOkjpmtBSlfP4=
|
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
|
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 {
|
type Session struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ const (
|
|||||||
USER_LOCALE_MAX_LENGTH = 5
|
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 {
|
type User struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
CreateAt int64 `json:"create_at,omitempty"`
|
CreateAt int64 `json:"create_at,omitempty"`
|
||||||
|
|||||||
48
services/cache/lru.go
поставляемый
48
services/cache/lru.go
поставляемый
@@ -8,6 +8,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/v5/model"
|
||||||
|
"github.com/tinylib/msgp/msgp"
|
||||||
"github.com/vmihailenco/msgpack/v5"
|
"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)
|
expires = time.Now().Add(ttl)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, err := msgpack.Marshal(value)
|
var buf []byte
|
||||||
if err != nil {
|
var err error
|
||||||
return err
|
|
||||||
|
// 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.
|
// 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)
|
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 msgpack.Unmarshal(e.value, value)
|
||||||
}
|
}
|
||||||
return ErrKeyNotFound
|
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)
|
err = l.Get("post", &p)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, post.Clone(), p.Clone())
|
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) {
|
func BenchmarkLRU(b *testing.B) {
|
||||||
@@ -458,7 +532,43 @@ func BenchmarkLRU(b *testing.B) {
|
|||||||
err := l2.Set("test", status)
|
err := l2.Set("test", status)
|
||||||
require.Nil(b, err)
|
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)
|
err = l2.Get("test", &val)
|
||||||
require.Nil(b, err)
|
require.Nil(b, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,8 @@ func TestUserStoreCache(t *testing.T) {
|
|||||||
cachedUsers, err = cachedStore.User().GetProfileByIds(fakeUserIds, &store.UserGetByIdsOpts{}, true)
|
cachedUsers, err = cachedStore.User().GetProfileByIds(fakeUserIds, &store.UserGetByIdsOpts{}, true)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
for i := 0; i < len(storedUsers); i++ {
|
for i := 0; i < len(storedUsers); i++ {
|
||||||
|
storedUsers[i].Props = model.StringMap{}
|
||||||
|
storedUsers[i].Timezone = model.StringMap{}
|
||||||
assert.Equal(t, storedUsers[i], cachedUsers[i])
|
assert.Equal(t, storedUsers[i], cachedUsers[i])
|
||||||
if storedUsers[i] == cachedUsers[i] {
|
if storedUsers[i] == cachedUsers[i] {
|
||||||
assert.Fail(t, "should be different pointers")
|
assert.Fail(t, "should be different pointers")
|
||||||
@@ -241,6 +243,8 @@ func TestUserStoreGetCache(t *testing.T) {
|
|||||||
cachedUser.NotifyProps["key"] = "othervalue"
|
cachedUser.NotifyProps["key"] = "othervalue"
|
||||||
assert.NotEqual(t, storedUser, cachedUser)
|
assert.NotEqual(t, storedUser, cachedUser)
|
||||||
|
|
||||||
|
storedUser.Props = model.StringMap{}
|
||||||
|
storedUser.Timezone = model.StringMap{}
|
||||||
cachedUser, err = cachedStore.User().Get(fakeUserId)
|
cachedUser, err = cachedStore.User().Get(fakeUserId)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
assert.Equal(t, storedUser, cachedUser)
|
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/throttled/throttled/store/memstore
|
||||||
# github.com/tinylib/msgp v1.1.2
|
# github.com/tinylib/msgp v1.1.2
|
||||||
## explicit
|
## explicit
|
||||||
|
github.com/tinylib/msgp
|
||||||
|
github.com/tinylib/msgp/gen
|
||||||
github.com/tinylib/msgp/msgp
|
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
|
# github.com/tylerb/graceful v1.2.15
|
||||||
## explicit
|
## explicit
|
||||||
github.com/tylerb/graceful
|
github.com/tylerb/graceful
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user