MM-27456: Use reflect-free serialization for hot structs (#15171)

Automatic Merge
Этот коммит содержится в:
Agniva De Sarker
2020-08-13 13:05:57 +05:30
коммит произвёл GitHub
родитель 32b7d2b5f1
Коммит 91a76b2df9
34 изменённых файлов: 6224 добавлений и 6 удалений

Просмотреть файл

@@ -554,7 +554,7 @@ vet: ## Run mattermost go vet specific checks
echo "mattermost-govet is not installed. Please install it executing \"GO111MODULE=off GOBIN=$(PWD)/bin go get -u github.com/mattermost/mattermost-govet\""; \
exit 1; \
fi;
@VET_CMD="-license -structuredLogging -inconsistentReceiverName -tFatal"; \
@VET_CMD="-license -structuredLogging -inconsistentReceiverName -inconsistentReceiverName.ignore=serialized_gen.go -tFatal"; \
if ! [ -z "${MM_VET_OPENSPEC_PATH}" ] && [ -f "${MM_VET_OPENSPEC_PATH}" ]; then \
VET_CMD="$$VET_CMD -openApiSync -openApiSync.spec=$$MM_VET_OPENSPEC_PATH"; \
else \
@@ -567,6 +567,13 @@ ifneq ($(MM_NO_ENTERPRISE_LINT),true)
endif
endif
gen-serialized: ## Generates serialization methods for hot structs
# This tool only works at a file level, not at a package level.
# So you would need to move the structs that need to be serialized temporarily
# to session.go and run the tool.
$(GO) get -modfile=go.tools.mod github.com/tinylib/msgp
$(GOBIN)/msgp -file=./model/session.go -tests=false -o=./model/serialized_gen.go
todo: ## Display TODO and FIXME items in the source code.
@! ag --ignore Makefile --ignore-dir vendor --ignore-dir runtime TODO
@! ag --ignore Makefile --ignore-dir vendor --ignore-dir runtime XXX

3
go.mod
Просмотреть файл

@@ -103,7 +103,8 @@ require (
github.com/stretchr/testify v1.6.1
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
github.com/throttled/throttled v2.2.4+incompatible
github.com/tinylib/msgp v1.1.2 // indirect
github.com/tinylib/msgp v1.1.2
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 // indirect
github.com/tylerb/graceful v1.2.15
github.com/uber/jaeger-client-go v2.24.0+incompatible
github.com/uber/jaeger-lib v2.2.0+incompatible

2
go.sum
Просмотреть файл

@@ -719,6 +719,8 @@ github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDW
github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ=
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 h1:OXcKh35JaYsGMRzpvFkLv/MEyPuL49CThT1pZ8aSml4=
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/tylerb/graceful v1.2.15 h1:B0x01Y8fsJpogzZTkDg6BDi6eMf03s01lEKGdrv83oA=
github.com/tylerb/graceful v1.2.15/go.mod h1:LPYTbOYmUTdabwRt0TGhLllQ0MUNbs0Y5q1WXJOI9II=

Просмотреть файл

@@ -5,6 +5,9 @@ go 1.14
require (
github.com/jstemmer/go-junit-report v0.9.1 // indirect
github.com/mattermost/mattermost-utilities/mmgotool v0.0.0-20200730135456-e2334fe87160 // indirect
github.com/philhofer/fwd v1.0.0 // indirect
github.com/reflog/struct2interface v0.6.1 // indirect
github.com/tinylib/msgp v1.1.2 // indirect
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 // indirect
github.com/vektra/mockery v1.1.2 // indirect
)

Просмотреть файл

@@ -64,6 +64,8 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -100,7 +102,11 @@ github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ=
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 h1:OXcKh35JaYsGMRzpvFkLv/MEyPuL49CThT1pZ8aSml4=
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/vektra/mockery v1.1.2 h1:uc0Yn67rJpjt8U/mAZimdCKn9AeA97BOkjpmtBSlfP4=

1622
model/serialized_gen.go Обычный файл

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@@ -30,6 +30,11 @@ const (
SESSION_USER_ACCESS_TOKEN_EXPIRY = 100 * 365 // 100 years
)
//msgp:tuple Session
// Session contains the user session details.
// This struct's serializer methods are auto-generated. If a new field is added/removed,
// please run make gen-serialized.
type Session struct {
Id string `json:"id"`
Token string `json:"token"`

Просмотреть файл

@@ -59,6 +59,11 @@ const (
USER_LOCALE_MAX_LENGTH = 5
)
//msgp:tuple User
// User contains the details about the user.
// This struct's serializer methods are auto-generated. If a new field is added/removed,
// please run make gen-serialized.
type User struct {
Id string `json:"id"`
CreateAt int64 `json:"create_at,omitempty"`

48
services/cache/lru.go поставляемый
Просмотреть файл

@@ -8,6 +8,8 @@ import (
"sync"
"time"
"github.com/mattermost/mattermost-server/v5/model"
"github.com/tinylib/msgp/msgp"
"github.com/vmihailenco/msgpack/v5"
)
@@ -141,9 +143,21 @@ func (l *LRU) set(key string, value interface{}, ttl time.Duration) error {
expires = time.Now().Add(ttl)
}
buf, err := msgpack.Marshal(value)
if err != nil {
return err
var buf []byte
var err error
// We use a fast path for hot structs.
if msgpVal, ok := value.(msgp.Marshaler); ok {
buf, err = msgpVal.MarshalMsg(nil)
if err != nil {
return err
}
} else {
// Slow path for other structs.
buf, err = msgpack.Marshal(value)
if err != nil {
return err
}
}
// Check for existing item, ignoring expiry since we'd update anyway.
@@ -182,6 +196,34 @@ func (l *LRU) get(key string, value interface{}) error {
l.evictList.MoveToFront(ent)
// We use a fast path for hot structs.
if msgpVal, ok := value.(msgp.Unmarshaler); ok {
_, err := msgpVal.UnmarshalMsg(e.value)
return err
}
// This is ugly and makes the cache package aware of the model package.
// But this is due to 2 things.
// 1. The msgp package works on methods on structs rather than functions.
// 2. Our cache interface passes pointers to empty pointers, and not pointers
// to values. This is mainly how all our model structs are passed around.
// It might be technically possible to use values _just_ for hot structs
// like these and then return a pointer while returning from the cache function,
// but it will make the codebase inconsistent, and has some edge-cases to take care of.
switch v := value.(type) {
case **model.User:
var u model.User
_, err := u.UnmarshalMsg(e.value)
*v = &u
return err
case **model.Session:
var s model.Session
_, err := s.UnmarshalMsg(e.value)
*v = &s
return err
}
// Slow path for other structs.
return msgpack.Unmarshal(e.value, value)
}
return ErrKeyNotFound

112
services/cache/lru_test.go поставляемый
Просмотреть файл

@@ -205,6 +205,80 @@ func TestLRUMarshalUnMarshal(t *testing.T) {
err = l.Get("post", &p)
require.Nil(t, err)
require.Equal(t, post.Clone(), p.Clone())
session := &model.Session{
Id: "ty7ia14yuty5bmpt8wmz6da1fw",
Token: "79c3iq6nzpycmkkawudanqhg5c",
CreateAt: 1595445296960,
ExpiresAt: 1598296496960,
LastActivityAt: 1595445296960,
UserId: "rpgh1q5ra38y9xjn9z8fjctezr",
Roles: "system_admin system_user",
IsOAuth: false,
ExpiredNotify: false,
Props: map[string]string{
"csrf": "33zb7h7rk3rfffztojn5pxbkxe",
"isMobile": "false",
"isSaml": "false",
"is_guest": "false",
"os": "",
"platform": "Windows",
},
}
err = l.Set("session", session)
require.Nil(t, err)
var s *model.Session
err = l.Get("session", &s)
require.Nil(t, err)
require.Equal(t, session, s)
user := &model.User{
Id: "id",
CreateAt: 11111,
UpdateAt: 11111,
DeleteAt: 11111,
Username: "username",
Password: "password",
AuthService: "AuthService",
AuthData: nil,
Email: "Email",
EmailVerified: true,
Nickname: "Nickname",
FirstName: "FirstName",
LastName: "LastName",
Position: "Position",
Roles: "Roles",
AllowMarketing: true,
Props: map[string]string{
"key0": "value0",
},
NotifyProps: map[string]string{
"key0": "value0",
},
LastPasswordUpdate: 111111,
LastPictureUpdate: 111111,
FailedAttempts: 111111,
Locale: "Locale",
MfaActive: true,
MfaSecret: "MfaSecret",
LastActivityAt: 111111,
IsBot: true,
TermsOfServiceId: "TermsOfServiceId",
TermsOfServiceCreateAt: 111111,
}
err = l.Set("user", user)
require.Nil(t, err)
var u *model.User
err = l.Get("user", &u)
require.Nil(t, err)
// msgp returns an empty map instead of a nil map.
// This does not make an actual difference in terms of functionality.
u.Timezone = nil
require.Equal(t, user, u)
}
func BenchmarkLRU(b *testing.B) {
@@ -458,7 +532,43 @@ func BenchmarkLRU(b *testing.B) {
err := l2.Set("test", status)
require.Nil(b, err)
var val model.Status
var val *model.Status
err = l2.Get("test", &val)
require.Nil(b, err)
}
})
session := model.Session{
Id: "ty7ia14yuty5bmpt8wmz6da1fw",
Token: "79c3iq6nzpycmkkawudanqhg5c",
CreateAt: 1595445296960,
ExpiresAt: 1598296496960,
LastActivityAt: 1595445296960,
UserId: "rpgh1q5ra38y9xjn9z8fjctezr",
Roles: "system_admin system_user",
IsOAuth: false,
ExpiredNotify: false,
Props: map[string]string{
"csrf": "33zb7h7rk3rfffztojn5pxbkxe",
"isMobile": "false",
"isSaml": "false",
"is_guest": "false",
"os": "",
"platform": "Windows",
},
}
b.Run("Session=new", func(b *testing.B) {
for i := 0; i < b.N; i++ {
l2 := NewLRU(&LRUOptions{
Size: 1,
DefaultExpiry: 0,
InvalidateClusterEvent: "",
})
err := l2.Set("test", &session)
require.Nil(b, err)
var val *model.Session
err = l2.Get("test", &val)
require.Nil(b, err)
}

Просмотреть файл

@@ -101,6 +101,8 @@ func TestUserStoreCache(t *testing.T) {
cachedUsers, err = cachedStore.User().GetProfileByIds(fakeUserIds, &store.UserGetByIdsOpts{}, true)
require.Nil(t, err)
for i := 0; i < len(storedUsers); i++ {
storedUsers[i].Props = model.StringMap{}
storedUsers[i].Timezone = model.StringMap{}
assert.Equal(t, storedUsers[i], cachedUsers[i])
if storedUsers[i] == cachedUsers[i] {
assert.Fail(t, "should be different pointers")
@@ -241,6 +243,8 @@ func TestUserStoreGetCache(t *testing.T) {
cachedUser.NotifyProps["key"] = "othervalue"
assert.NotEqual(t, storedUser, cachedUser)
storedUser.Props = model.StringMap{}
storedUser.Timezone = model.StringMap{}
cachedUser, err = cachedStore.User().Get(fakeUserId)
require.Nil(t, err)
assert.Equal(t, storedUser, cachedUser)

10
vendor/github.com/tinylib/msgp/.gitignore сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,102 @@
MessagePack Code Generator [![Build Status](https://travis-ci.org/tinylib/msgp.svg?branch=master)](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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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
![screenshot](https://raw.githubusercontent.com/ttacon/chalk/master/img/chalk_example.png)
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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -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 поставляемый
Просмотреть файл

@@ -471,7 +471,14 @@ github.com/throttled/throttled
github.com/throttled/throttled/store/memstore
# github.com/tinylib/msgp v1.1.2
## explicit
github.com/tinylib/msgp
github.com/tinylib/msgp/gen
github.com/tinylib/msgp/msgp
github.com/tinylib/msgp/parse
github.com/tinylib/msgp/printer
# github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31
## explicit
github.com/ttacon/chalk
# github.com/tylerb/graceful v1.2.15
## explicit
github.com/tylerb/graceful