deps: bump go-cty version (#26079)
go-cty v1.6.1 fixes a panic in the `element` function when called with a negative offset.
This commit is contained in:
parent
b4017f693d
commit
e6e35f2077
3
go.mod
3
go.mod
|
@ -120,11 +120,10 @@ require (
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
|
||||||
github.com/tombuildsstuff/giovanni v0.12.0
|
github.com/tombuildsstuff/giovanni v0.12.0
|
||||||
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 // indirect
|
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 // indirect
|
||||||
github.com/vmihailenco/msgpack v4.0.1+incompatible // indirect
|
|
||||||
github.com/xanzy/ssh-agent v0.2.1
|
github.com/xanzy/ssh-agent v0.2.1
|
||||||
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect
|
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect
|
||||||
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557
|
github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557
|
||||||
github.com/zclconf/go-cty v1.5.1
|
github.com/zclconf/go-cty v1.6.1
|
||||||
github.com/zclconf/go-cty-yaml v1.0.2
|
github.com/zclconf/go-cty-yaml v1.0.2
|
||||||
go.uber.org/atomic v1.3.2 // indirect
|
go.uber.org/atomic v1.3.2 // indirect
|
||||||
go.uber.org/multierr v1.1.0 // indirect
|
go.uber.org/multierr v1.1.0 // indirect
|
||||||
|
|
14
go.sum
14
go.sum
|
@ -486,9 +486,12 @@ github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 h1:cMjKdf4PxEBN9K5HaD9UM
|
||||||
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||||
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
|
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
|
||||||
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||||
|
github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI=
|
||||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||||
github.com/vmihailenco/msgpack v4.0.1+incompatible h1:RMF1enSPeKTlXrXdOcqjFUElywVZjjC6pqse21bKbEU=
|
github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U=
|
||||||
github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
||||||
|
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
|
||||||
|
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||||
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 h1:MPPkRncZLN9Kh4MEFmbnK4h3BD7AUmskWv2+EeZJCCs=
|
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 h1:MPPkRncZLN9Kh4MEFmbnK4h3BD7AUmskWv2+EeZJCCs=
|
||||||
|
@ -498,8 +501,8 @@ github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6Ut
|
||||||
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||||
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||||
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
||||||
github.com/zclconf/go-cty v1.5.1 h1:oALUZX+aJeEBUe2a1+uD2+UTaYfEjnKFDEMRydkGvWE=
|
github.com/zclconf/go-cty v1.6.1 h1:wHtZ+LSSQVwUSb+XIJ5E9hgAQxyWATZsAWT+ESJ9dQ0=
|
||||||
github.com/zclconf/go-cty v1.5.1/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
|
github.com/zclconf/go-cty v1.6.1/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPBoP54+o=
|
||||||
github.com/zclconf/go-cty-yaml v1.0.2 h1:dNyg4QLTrv2IfJpm7Wtxi55ed5gLGOlPrZ6kMd51hY0=
|
github.com/zclconf/go-cty-yaml v1.0.2 h1:dNyg4QLTrv2IfJpm7Wtxi55ed5gLGOlPrZ6kMd51hY0=
|
||||||
github.com/zclconf/go-cty-yaml v1.0.2/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=
|
github.com/zclconf/go-cty-yaml v1.0.2/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
@ -556,6 +559,7 @@ golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
|
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
|
||||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
@ -630,6 +634,8 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
sudo: false
|
|
||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.7.x
|
|
||||||
- 1.8.x
|
|
||||||
- 1.9.x
|
|
||||||
- 1.10.x
|
|
||||||
- 1.11.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
allow_failures:
|
|
||||||
- go: tip
|
|
||||||
|
|
||||||
install:
|
|
||||||
- go get gopkg.in/check.v1
|
|
|
@ -1,170 +0,0 @@
|
||||||
package msgpack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/codes"
|
|
||||||
)
|
|
||||||
|
|
||||||
type writer interface {
|
|
||||||
io.Writer
|
|
||||||
WriteByte(byte) error
|
|
||||||
WriteString(string) (int, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type byteWriter struct {
|
|
||||||
io.Writer
|
|
||||||
|
|
||||||
buf []byte
|
|
||||||
bootstrap [64]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func newByteWriter(w io.Writer) *byteWriter {
|
|
||||||
bw := &byteWriter{
|
|
||||||
Writer: w,
|
|
||||||
}
|
|
||||||
bw.buf = bw.bootstrap[:]
|
|
||||||
return bw
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *byteWriter) WriteByte(c byte) error {
|
|
||||||
w.buf = w.buf[:1]
|
|
||||||
w.buf[0] = c
|
|
||||||
_, err := w.Write(w.buf)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *byteWriter) WriteString(s string) (int, error) {
|
|
||||||
w.buf = append(w.buf[:0], s...)
|
|
||||||
return w.Write(w.buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Marshal returns the MessagePack encoding of v.
|
|
||||||
func Marshal(v interface{}) ([]byte, error) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := NewEncoder(&buf).Encode(v)
|
|
||||||
return buf.Bytes(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
type Encoder struct {
|
|
||||||
w writer
|
|
||||||
buf []byte
|
|
||||||
|
|
||||||
sortMapKeys bool
|
|
||||||
structAsArray bool
|
|
||||||
useJSONTag bool
|
|
||||||
useCompact bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEncoder returns a new encoder that writes to w.
|
|
||||||
func NewEncoder(w io.Writer) *Encoder {
|
|
||||||
bw, ok := w.(writer)
|
|
||||||
if !ok {
|
|
||||||
bw = newByteWriter(w)
|
|
||||||
}
|
|
||||||
return &Encoder{
|
|
||||||
w: bw,
|
|
||||||
buf: make([]byte, 9),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SortMapKeys causes the Encoder to encode map keys in increasing order.
|
|
||||||
// Supported map types are:
|
|
||||||
// - map[string]string
|
|
||||||
// - map[string]interface{}
|
|
||||||
func (e *Encoder) SortMapKeys(flag bool) *Encoder {
|
|
||||||
e.sortMapKeys = flag
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// StructAsArray causes the Encoder to encode Go structs as MessagePack arrays.
|
|
||||||
func (e *Encoder) StructAsArray(flag bool) *Encoder {
|
|
||||||
e.structAsArray = flag
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// UseJSONTag causes the Encoder to use json struct tag as fallback option
|
|
||||||
// if there is no msgpack tag.
|
|
||||||
func (e *Encoder) UseJSONTag(flag bool) *Encoder {
|
|
||||||
e.useJSONTag = flag
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// UseCompactEncoding causes the Encoder to chose the most compact encoding.
|
|
||||||
// For example, it allows to encode Go int64 as msgpack int8 saving 7 bytes.
|
|
||||||
func (e *Encoder) UseCompactEncoding(flag bool) *Encoder {
|
|
||||||
e.useCompact = flag
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) Encode(v interface{}) error {
|
|
||||||
switch v := v.(type) {
|
|
||||||
case nil:
|
|
||||||
return e.EncodeNil()
|
|
||||||
case string:
|
|
||||||
return e.EncodeString(v)
|
|
||||||
case []byte:
|
|
||||||
return e.EncodeBytes(v)
|
|
||||||
case int:
|
|
||||||
return e.encodeInt64Cond(int64(v))
|
|
||||||
case int64:
|
|
||||||
return e.encodeInt64Cond(v)
|
|
||||||
case uint:
|
|
||||||
return e.encodeUint64Cond(uint64(v))
|
|
||||||
case uint64:
|
|
||||||
return e.encodeUint64Cond(v)
|
|
||||||
case bool:
|
|
||||||
return e.EncodeBool(v)
|
|
||||||
case float32:
|
|
||||||
return e.EncodeFloat32(v)
|
|
||||||
case float64:
|
|
||||||
return e.EncodeFloat64(v)
|
|
||||||
case time.Duration:
|
|
||||||
return e.encodeInt64Cond(int64(v))
|
|
||||||
case time.Time:
|
|
||||||
return e.EncodeTime(v)
|
|
||||||
}
|
|
||||||
return e.EncodeValue(reflect.ValueOf(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) EncodeMulti(v ...interface{}) error {
|
|
||||||
for _, vv := range v {
|
|
||||||
if err := e.Encode(vv); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) EncodeValue(v reflect.Value) error {
|
|
||||||
fn := getEncoder(v.Type())
|
|
||||||
return fn(e, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) EncodeNil() error {
|
|
||||||
return e.writeCode(codes.Nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) EncodeBool(value bool) error {
|
|
||||||
if value {
|
|
||||||
return e.writeCode(codes.True)
|
|
||||||
}
|
|
||||||
return e.writeCode(codes.False)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) writeCode(c codes.Code) error {
|
|
||||||
return e.w.WriteByte(byte(c))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) write(b []byte) error {
|
|
||||||
_, err := e.w.Write(b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) writeString(s string) error {
|
|
||||||
_, err := e.w.WriteString(s)
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package msgpack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tagOptions string
|
|
||||||
|
|
||||||
func (o tagOptions) Get(name string) (string, bool) {
|
|
||||||
s := string(o)
|
|
||||||
for len(s) > 0 {
|
|
||||||
var next string
|
|
||||||
idx := strings.IndexByte(s, ',')
|
|
||||||
if idx >= 0 {
|
|
||||||
s, next = s[:idx], s[idx+1:]
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(s, name) {
|
|
||||||
return s[len(name):], true
|
|
||||||
}
|
|
||||||
s = next
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o tagOptions) Contains(name string) bool {
|
|
||||||
_, ok := o.Get(name)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseTag(tag string) (string, tagOptions) {
|
|
||||||
if idx := strings.IndexByte(tag, ','); idx != -1 {
|
|
||||||
name := tag[:idx]
|
|
||||||
if strings.IndexByte(name, ':') == -1 {
|
|
||||||
return name, tagOptions(tag[idx+1:])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.IndexByte(tag, ':') == -1 {
|
|
||||||
return tag, ""
|
|
||||||
}
|
|
||||||
return "", tagOptions(tag)
|
|
||||||
}
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
run:
|
||||||
|
concurrency: 8
|
||||||
|
deadline: 5m
|
||||||
|
tests: false
|
||||||
|
linters:
|
||||||
|
enable-all: true
|
||||||
|
disable:
|
||||||
|
- gochecknoglobals
|
||||||
|
- gocognit
|
||||||
|
- godox
|
||||||
|
- wsl
|
||||||
|
- funlen
|
|
@ -0,0 +1,21 @@
|
||||||
|
sudo: false
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.11.x
|
||||||
|
- 1.12.x
|
||||||
|
- 1.13.x
|
||||||
|
- 1.14.x
|
||||||
|
- tip
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
|
|
||||||
|
go_import_path: github.com/vmihailenco/msgpack
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.21.0
|
|
@ -1,4 +1,4 @@
|
||||||
## 3.4
|
## v4
|
||||||
|
|
||||||
- Encode, Decode, Marshal, and Unmarshal are changed to accept single argument. EncodeMulti and DecodeMulti are added as replacement.
|
- Encode, Decode, Marshal, and Unmarshal are changed to accept single argument. EncodeMulti and DecodeMulti are added as replacement.
|
||||||
- Added EncodeInt8/16/32/64 and EncodeUint8/16/32/64.
|
- Added EncodeInt8/16/32/64 and EncodeUint8/16/32/64.
|
|
@ -1,5 +1,6 @@
|
||||||
all:
|
all:
|
||||||
go test ./...
|
go test ./...
|
||||||
env GOOS=linux GOARCH=386 go test ./...
|
|
||||||
go test ./... -short -race
|
go test ./... -short -race
|
||||||
go vet
|
go test ./... -run=NONE -bench=. -benchmem
|
||||||
|
env GOOS=linux GOARCH=386 go test ./...
|
||||||
|
golangci-lint run
|
|
@ -8,10 +8,10 @@ Supports:
|
||||||
- Appengine *datastore.Key and datastore.Cursor.
|
- Appengine *datastore.Key and datastore.Cursor.
|
||||||
- [CustomEncoder](https://godoc.org/github.com/vmihailenco/msgpack#example-CustomEncoder)/CustomDecoder interfaces for custom encoding.
|
- [CustomEncoder](https://godoc.org/github.com/vmihailenco/msgpack#example-CustomEncoder)/CustomDecoder interfaces for custom encoding.
|
||||||
- [Extensions](https://godoc.org/github.com/vmihailenco/msgpack#example-RegisterExt) to encode type information.
|
- [Extensions](https://godoc.org/github.com/vmihailenco/msgpack#example-RegisterExt) to encode type information.
|
||||||
- Renaming fields via `msgpack:"my_field_name"`.
|
- Renaming fields via `msgpack:"my_field_name"` and alias via `msgpack:"alias:another_name"`.
|
||||||
- Omitting individual empty fields via `msgpack:",omitempty"` tag or all [empty fields in a struct](https://godoc.org/github.com/vmihailenco/msgpack#example-Marshal--OmitEmpty).
|
- Omitting individual empty fields via `msgpack:",omitempty"` tag or all [empty fields in a struct](https://godoc.org/github.com/vmihailenco/msgpack#example-Marshal--OmitEmpty).
|
||||||
- [Map keys sorting](https://godoc.org/github.com/vmihailenco/msgpack#Encoder.SortMapKeys).
|
- [Map keys sorting](https://godoc.org/github.com/vmihailenco/msgpack#Encoder.SortMapKeys).
|
||||||
- Encoding/decoding all [structs as arrays](https://godoc.org/github.com/vmihailenco/msgpack#Encoder.StructAsArray) or [individual structs](https://godoc.org/github.com/vmihailenco/msgpack#example-Marshal--AsArray).
|
- Encoding/decoding all [structs as arrays](https://godoc.org/github.com/vmihailenco/msgpack#Encoder.UseArrayForStructs) or [individual structs](https://godoc.org/github.com/vmihailenco/msgpack#example-Marshal--AsArray).
|
||||||
- [Encoder.UseJSONTag](https://godoc.org/github.com/vmihailenco/msgpack#Encoder.UseJSONTag) with [Decoder.UseJSONTag](https://godoc.org/github.com/vmihailenco/msgpack#Decoder.UseJSONTag) can turn msgpack into drop-in replacement for JSON.
|
- [Encoder.UseJSONTag](https://godoc.org/github.com/vmihailenco/msgpack#Encoder.UseJSONTag) with [Decoder.UseJSONTag](https://godoc.org/github.com/vmihailenco/msgpack#Decoder.UseJSONTag) can turn msgpack into drop-in replacement for JSON.
|
||||||
- Simple but very fast and efficient [queries](https://godoc.org/github.com/vmihailenco/msgpack#example-Decoder-Query).
|
- Simple but very fast and efficient [queries](https://godoc.org/github.com/vmihailenco/msgpack#example-Decoder-Query).
|
||||||
|
|
||||||
|
@ -20,15 +20,18 @@ Examples: https://godoc.org/github.com/vmihailenco/msgpack#pkg-examples.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Install:
|
This project uses [Go Modules](https://github.com/golang/go/wiki/Modules) and semantic import versioning since v4:
|
||||||
|
|
||||||
```shell
|
``` shell
|
||||||
go get -u github.com/vmihailenco/msgpack
|
go mod init github.com/my/repo
|
||||||
|
go get github.com/vmihailenco/msgpack/v4
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
```go
|
``` go
|
||||||
|
import "github.com/vmihailenco/msgpack/v4"
|
||||||
|
|
||||||
func ExampleMarshal() {
|
func ExampleMarshal() {
|
||||||
type Item struct {
|
type Item struct {
|
||||||
Foo string
|
Foo string
|
||||||
|
@ -66,4 +69,4 @@ Please go through [examples](https://godoc.org/github.com/vmihailenco/msgpack#pk
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [Golang PostgreSQL ORM](https://github.com/go-pg/pg)
|
- [Golang PostgreSQL ORM](https://github.com/go-pg/pg)
|
||||||
- [Golang message task queue](https://github.com/go-msgqueue/msgqueue)
|
- [Golang message task queue](https://github.com/vmihailenco/taskq)
|
|
@ -77,6 +77,10 @@ func IsString(c Code) bool {
|
||||||
return IsFixedString(c) || c == Str8 || c == Str16 || c == Str32
|
return IsFixedString(c) || c == Str8 || c == Str16 || c == Str32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsBin(c Code) bool {
|
||||||
|
return c == Bin8 || c == Bin16 || c == Bin32
|
||||||
|
}
|
||||||
|
|
||||||
func IsFixedExt(c Code) bool {
|
func IsFixedExt(c Code) bool {
|
||||||
return c >= FixExt1 && c <= FixExt16
|
return c >= FixExt1 && c <= FixExt16
|
||||||
}
|
}
|
|
@ -7,35 +7,55 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/codes"
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const bytesAllocLimit = 1024 * 1024 // 1mb
|
const (
|
||||||
|
looseIfaceFlag uint32 = 1 << iota
|
||||||
|
decodeUsingJSONFlag
|
||||||
|
disallowUnknownFieldsFlag
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
bytesAllocLimit = 1e6 // 1mb
|
||||||
|
sliceAllocLimit = 1e4
|
||||||
|
maxMapSize = 1e6
|
||||||
|
)
|
||||||
|
|
||||||
type bufReader interface {
|
type bufReader interface {
|
||||||
io.Reader
|
io.Reader
|
||||||
io.ByteScanner
|
io.ByteScanner
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBufReader(r io.Reader) bufReader {
|
//------------------------------------------------------------------------------
|
||||||
if br, ok := r.(bufReader); ok {
|
|
||||||
return br
|
|
||||||
}
|
|
||||||
return bufio.NewReader(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeBuffer() []byte {
|
var decPool = sync.Pool{
|
||||||
return make([]byte, 0, 64)
|
New: func() interface{} {
|
||||||
|
return NewDecoder(nil)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal decodes the MessagePack-encoded data and stores the result
|
// Unmarshal decodes the MessagePack-encoded data and stores the result
|
||||||
// in the value pointed to by v.
|
// in the value pointed to by v.
|
||||||
func Unmarshal(data []byte, v interface{}) error {
|
func Unmarshal(data []byte, v interface{}) error {
|
||||||
return NewDecoder(bytes.NewReader(data)).Decode(v)
|
dec := decPool.Get().(*Decoder)
|
||||||
|
|
||||||
|
if r, ok := dec.r.(*bytes.Reader); ok {
|
||||||
|
r.Reset(data)
|
||||||
|
} else {
|
||||||
|
dec.Reset(bytes.NewReader(data))
|
||||||
|
}
|
||||||
|
err := dec.Decode(v)
|
||||||
|
|
||||||
|
decPool.Put(dec)
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A Decoder reads and decodes MessagePack values from an input stream.
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
r io.Reader
|
r io.Reader
|
||||||
s io.ByteScanner
|
s io.ByteScanner
|
||||||
|
@ -44,9 +64,8 @@ type Decoder struct {
|
||||||
extLen int
|
extLen int
|
||||||
rec []byte // accumulates read data if not nil
|
rec []byte // accumulates read data if not nil
|
||||||
|
|
||||||
useLoose bool
|
intern []string
|
||||||
useJSONTag bool
|
flags uint32
|
||||||
|
|
||||||
decodeMapFunc func(*Decoder) (interface{}, error)
|
decodeMapFunc func(*Decoder) (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,41 +75,80 @@ type Decoder struct {
|
||||||
// beyond the MessagePack values requested. Buffering can be disabled
|
// beyond the MessagePack values requested. Buffering can be disabled
|
||||||
// by passing a reader that implements io.ByteScanner interface.
|
// by passing a reader that implements io.ByteScanner interface.
|
||||||
func NewDecoder(r io.Reader) *Decoder {
|
func NewDecoder(r io.Reader) *Decoder {
|
||||||
d := &Decoder{
|
d := new(Decoder)
|
||||||
buf: makeBuffer(),
|
d.Reset(r)
|
||||||
}
|
|
||||||
d.resetReader(r)
|
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset discards any buffered data, resets all state, and switches the buffered
|
||||||
|
// reader to read from r.
|
||||||
|
func (d *Decoder) Reset(r io.Reader) {
|
||||||
|
if br, ok := r.(bufReader); ok {
|
||||||
|
d.r = br
|
||||||
|
d.s = br
|
||||||
|
} else if br, ok := d.r.(*bufio.Reader); ok {
|
||||||
|
br.Reset(r)
|
||||||
|
} else {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
d.r = br
|
||||||
|
d.s = br
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.intern != nil {
|
||||||
|
d.intern = d.intern[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO:
|
||||||
|
//d.useLoose = false
|
||||||
|
//d.useJSONTag = false
|
||||||
|
//d.disallowUnknownFields = false
|
||||||
|
//d.decodeMapFunc = nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Decoder) SetDecodeMapFunc(fn func(*Decoder) (interface{}, error)) {
|
func (d *Decoder) SetDecodeMapFunc(fn func(*Decoder) (interface{}, error)) {
|
||||||
d.decodeMapFunc = fn
|
d.decodeMapFunc = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
// UseDecodeInterfaceLoose causes decoder to use DecodeInterfaceLoose
|
// UseDecodeInterfaceLoose causes decoder to use DecodeInterfaceLoose
|
||||||
// to decode msgpack value into Go interface{}.
|
// to decode msgpack value into Go interface{}.
|
||||||
func (d *Decoder) UseDecodeInterfaceLoose(flag bool) {
|
func (d *Decoder) UseDecodeInterfaceLoose(on bool) *Decoder {
|
||||||
d.useLoose = flag
|
if on {
|
||||||
|
d.flags |= looseIfaceFlag
|
||||||
|
} else {
|
||||||
|
d.flags &= ^looseIfaceFlag
|
||||||
|
}
|
||||||
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
// UseJSONTag causes the Decoder to use json struct tag as fallback option
|
// UseJSONTag causes the Decoder to use json struct tag as fallback option
|
||||||
// if there is no msgpack tag.
|
// if there is no msgpack tag.
|
||||||
func (d *Decoder) UseJSONTag(v bool) *Decoder {
|
func (d *Decoder) UseJSONTag(on bool) *Decoder {
|
||||||
d.useJSONTag = v
|
if on {
|
||||||
|
d.flags |= decodeUsingJSONFlag
|
||||||
|
} else {
|
||||||
|
d.flags &= ^decodeUsingJSONFlag
|
||||||
|
}
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) Reset(r io.Reader) error {
|
// DisallowUnknownFields causes the Decoder to return an error when the destination
|
||||||
d.resetReader(r)
|
// is a struct and the input contains object keys which do not match any
|
||||||
return nil
|
// non-ignored, exported fields in the destination.
|
||||||
|
func (d *Decoder) DisallowUnknownFields() {
|
||||||
|
if true {
|
||||||
|
d.flags |= disallowUnknownFieldsFlag
|
||||||
|
} else {
|
||||||
|
d.flags &= ^disallowUnknownFieldsFlag
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) resetReader(r io.Reader) {
|
// Buffered returns a reader of the data remaining in the Decoder's buffer.
|
||||||
reader := newBufReader(r)
|
// The reader is valid until the next call to Decode.
|
||||||
d.r = reader
|
func (d *Decoder) Buffered() io.Reader {
|
||||||
d.s = reader
|
return d.r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
func (d *Decoder) Decode(v interface{}) error {
|
func (d *Decoder) Decode(v interface{}) error {
|
||||||
var err error
|
var err error
|
||||||
switch v := v.(type) {
|
switch v := v.(type) {
|
||||||
|
@ -211,7 +269,7 @@ func (d *Decoder) DecodeMulti(v ...interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodeInterfaceCond() (interface{}, error) {
|
func (d *Decoder) decodeInterfaceCond() (interface{}, error) {
|
||||||
if d.useLoose {
|
if d.flags&looseIfaceFlag != 0 {
|
||||||
return d.DecodeInterfaceLoose()
|
return d.DecodeInterfaceLoose()
|
||||||
}
|
}
|
||||||
return d.DecodeInterface()
|
return d.DecodeInterface()
|
||||||
|
@ -263,6 +321,14 @@ func (d *Decoder) bool(c codes.Code) (bool, error) {
|
||||||
return false, fmt.Errorf("msgpack: invalid code=%x decoding bool", c)
|
return false, fmt.Errorf("msgpack: invalid code=%x decoding bool", c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeDuration() (time.Duration, error) {
|
||||||
|
n, err := d.DecodeInt64()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return time.Duration(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
// DecodeInterface decodes value into interface. It returns following types:
|
// DecodeInterface decodes value into interface. It returns following types:
|
||||||
// - nil,
|
// - nil,
|
||||||
// - bool,
|
// - bool,
|
||||||
|
@ -356,7 +422,7 @@ func (d *Decoder) DecodeInterfaceLoose() (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if codes.IsFixedNum(c) {
|
if codes.IsFixedNum(c) {
|
||||||
return int64(c), nil
|
return int64(int8(c)), nil
|
||||||
}
|
}
|
||||||
if codes.IsFixedMap(c) {
|
if codes.IsFixedMap(c) {
|
||||||
err = d.s.UnreadByte()
|
err = d.s.UnreadByte()
|
||||||
|
@ -412,11 +478,14 @@ func (d *Decoder) Skip() error {
|
||||||
|
|
||||||
if codes.IsFixedNum(c) {
|
if codes.IsFixedNum(c) {
|
||||||
return nil
|
return nil
|
||||||
} else if codes.IsFixedMap(c) {
|
}
|
||||||
|
if codes.IsFixedMap(c) {
|
||||||
return d.skipMap(c)
|
return d.skipMap(c)
|
||||||
} else if codes.IsFixedArray(c) {
|
}
|
||||||
|
if codes.IsFixedArray(c) {
|
||||||
return d.skipSlice(c)
|
return d.skipSlice(c)
|
||||||
} else if codes.IsFixedString(c) {
|
}
|
||||||
|
if codes.IsFixedString(c) {
|
||||||
return d.skipBytes(c)
|
return d.skipBytes(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,21 +549,23 @@ func (d *Decoder) readFull(b []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if d.rec != nil {
|
if d.rec != nil {
|
||||||
|
//TODO: read directly into d.rec?
|
||||||
d.rec = append(d.rec, b...)
|
d.rec = append(d.rec, b...)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) readN(n int) ([]byte, error) {
|
func (d *Decoder) readN(n int) ([]byte, error) {
|
||||||
buf, err := readN(d.r, d.buf, n)
|
var err error
|
||||||
|
d.buf, err = readN(d.r, d.buf, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
d.buf = buf
|
|
||||||
if d.rec != nil {
|
if d.rec != nil {
|
||||||
d.rec = append(d.rec, buf...)
|
//TODO: read directly into d.rec?
|
||||||
|
d.rec = append(d.rec, d.buf...)
|
||||||
}
|
}
|
||||||
return buf, nil
|
return d.buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readN(r io.Reader, b []byte, n int) ([]byte, error) {
|
func readN(r io.Reader, b []byte, n int) ([]byte, error) {
|
||||||
|
@ -502,10 +573,13 @@ func readN(r io.Reader, b []byte, n int) ([]byte, error) {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return make([]byte, 0), nil
|
return make([]byte, 0), nil
|
||||||
}
|
}
|
||||||
if n <= bytesAllocLimit {
|
switch {
|
||||||
b = make([]byte, n)
|
case n < 64:
|
||||||
} else {
|
b = make([]byte, 0, 64)
|
||||||
b = make([]byte, bytesAllocLimit)
|
case n <= bytesAllocLimit:
|
||||||
|
b = make([]byte, 0, n)
|
||||||
|
default:
|
||||||
|
b = make([]byte, 0, bytesAllocLimit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -518,15 +592,12 @@ func readN(r io.Reader, b []byte, n int) ([]byte, error) {
|
||||||
|
|
||||||
var pos int
|
var pos int
|
||||||
for {
|
for {
|
||||||
alloc := n - len(b)
|
alloc := min(n-len(b), bytesAllocLimit)
|
||||||
if alloc > bytesAllocLimit {
|
|
||||||
alloc = bytesAllocLimit
|
|
||||||
}
|
|
||||||
b = append(b, make([]byte, alloc)...)
|
b = append(b, make([]byte, alloc)...)
|
||||||
|
|
||||||
_, err := io.ReadFull(r, b[pos:])
|
_, err := io.ReadFull(r, b[pos:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(b) == n {
|
if len(b) == n {
|
||||||
|
@ -538,7 +609,7 @@ func readN(r io.Reader, b []byte, n int) ([]byte, error) {
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func min(a, b int) int {
|
func min(a, b int) int { //nolint:unparam
|
||||||
if a <= b {
|
if a <= b {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
|
@ -5,18 +5,18 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/codes"
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const mapElemsAllocLimit = 1e4
|
var (
|
||||||
|
mapStringStringPtrType = reflect.TypeOf((*map[string]string)(nil))
|
||||||
|
mapStringStringType = mapStringStringPtrType.Elem()
|
||||||
|
)
|
||||||
|
|
||||||
var mapStringStringPtrType = reflect.TypeOf((*map[string]string)(nil))
|
var (
|
||||||
var mapStringStringType = mapStringStringPtrType.Elem()
|
mapStringInterfacePtrType = reflect.TypeOf((*map[string]interface{})(nil))
|
||||||
|
mapStringInterfaceType = mapStringInterfacePtrType.Elem()
|
||||||
var mapStringInterfacePtrType = reflect.TypeOf((*map[string]interface{})(nil))
|
)
|
||||||
var mapStringInterfaceType = mapStringInterfacePtrType.Elem()
|
|
||||||
|
|
||||||
var errInvalidCode = errors.New("invalid code")
|
|
||||||
|
|
||||||
func decodeMapValue(d *Decoder, v reflect.Value) error {
|
func decodeMapValue(d *Decoder, v reflect.Value) error {
|
||||||
size, err := d.DecodeMapLen()
|
size, err := d.DecodeMapLen()
|
||||||
|
@ -106,6 +106,8 @@ func (d *Decoder) _mapLen(c codes.Code) (int, error) {
|
||||||
return 0, errInvalidCode
|
return 0, errInvalidCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errInvalidCode = errors.New("invalid code")
|
||||||
|
|
||||||
func expandInvalidCodeMapLenError(c codes.Code, err error) error {
|
func expandInvalidCodeMapLenError(c codes.Code, err error) error {
|
||||||
if err == errInvalidCode {
|
if err == errInvalidCode {
|
||||||
return fmt.Errorf("msgpack: invalid code=%x decoding map length", c)
|
return fmt.Errorf("msgpack: invalid code=%x decoding map length", c)
|
||||||
|
@ -130,7 +132,7 @@ func (d *Decoder) decodeMapStringStringPtr(ptr *map[string]string) error {
|
||||||
|
|
||||||
m := *ptr
|
m := *ptr
|
||||||
if m == nil {
|
if m == nil {
|
||||||
*ptr = make(map[string]string, min(size, mapElemsAllocLimit))
|
*ptr = make(map[string]string, min(size, maxMapSize))
|
||||||
m = *ptr
|
m = *ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +168,7 @@ func (d *Decoder) decodeMapStringInterfacePtr(ptr *map[string]interface{}) error
|
||||||
|
|
||||||
m := *ptr
|
m := *ptr
|
||||||
if m == nil {
|
if m == nil {
|
||||||
*ptr = make(map[string]interface{}, min(n, mapElemsAllocLimit))
|
*ptr = make(map[string]interface{}, min(n, maxMapSize))
|
||||||
m = *ptr
|
m = *ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +187,8 @@ func (d *Decoder) decodeMapStringInterfacePtr(ptr *map[string]interface{}) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errUnsupportedMapKey = errors.New("msgpack: unsupported map key")
|
||||||
|
|
||||||
func (d *Decoder) DecodeMap() (interface{}, error) {
|
func (d *Decoder) DecodeMap() (interface{}, error) {
|
||||||
if d.decodeMapFunc != nil {
|
if d.decodeMapFunc != nil {
|
||||||
return d.decodeMapFunc(d)
|
return d.decodeMapFunc(d)
|
||||||
|
@ -206,7 +210,7 @@ func (d *Decoder) DecodeMap() (interface{}, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if codes.IsString(code) {
|
if codes.IsString(code) || codes.IsBin(code) {
|
||||||
return d.decodeMapStringInterfaceSize(size)
|
return d.decodeMapStringInterfaceSize(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,6 +226,11 @@ func (d *Decoder) DecodeMap() (interface{}, error) {
|
||||||
|
|
||||||
keyType := reflect.TypeOf(key)
|
keyType := reflect.TypeOf(key)
|
||||||
valueType := reflect.TypeOf(value)
|
valueType := reflect.TypeOf(value)
|
||||||
|
|
||||||
|
if !keyType.Comparable() {
|
||||||
|
return nil, errUnsupportedMapKey
|
||||||
|
}
|
||||||
|
|
||||||
mapType := reflect.MapOf(keyType, valueType)
|
mapType := reflect.MapOf(keyType, valueType)
|
||||||
mapValue := reflect.MakeMap(mapType)
|
mapValue := reflect.MakeMap(mapType)
|
||||||
|
|
||||||
|
@ -237,7 +246,7 @@ func (d *Decoder) DecodeMap() (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodeMapStringInterfaceSize(size int) (map[string]interface{}, error) {
|
func (d *Decoder) decodeMapStringInterfaceSize(size int) (map[string]interface{}, error) {
|
||||||
m := make(map[string]interface{}, min(size, mapElemsAllocLimit))
|
m := make(map[string]interface{}, min(size, maxMapSize))
|
||||||
for i := 0; i < size; i++ {
|
for i := 0; i < size; i++ {
|
||||||
mk, err := d.DecodeString()
|
mk, err := d.DecodeString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -294,7 +303,7 @@ func decodeStructValue(d *Decoder, v reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var fields *fields
|
var fields *fields
|
||||||
if d.useJSONTag {
|
if d.flags&decodeUsingJSONFlag != 0 {
|
||||||
fields = jsonStructs.Fields(v.Type())
|
fields = jsonStructs.Fields(v.Type())
|
||||||
} else {
|
} else {
|
||||||
fields = structs.Fields(v.Type())
|
fields = structs.Fields(v.Type())
|
||||||
|
@ -309,12 +318,14 @@ func decodeStructValue(d *Decoder, v reflect.Value) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip extra values.
|
// Skip extra values.
|
||||||
for i := len(fields.List); i < n; i++ {
|
for i := len(fields.List); i < n; i++ {
|
||||||
if err := d.Skip(); err != nil {
|
if err := d.Skip(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,14 +334,15 @@ func decodeStructValue(d *Decoder, v reflect.Value) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if f := fields.Table[name]; f != nil {
|
|
||||||
|
if f := fields.Map[name]; f != nil {
|
||||||
if err := f.DecodeValue(d, v); err != nil {
|
if err := f.DecodeValue(d, v); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else if d.flags&disallowUnknownFieldsFlag != 0 {
|
||||||
if err := d.Skip(); err != nil {
|
return fmt.Errorf("msgpack: unknown field %q", name)
|
||||||
return err
|
} else if err := d.Skip(); err != nil {
|
||||||
}
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/codes"
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d *Decoder) skipN(n int) error {
|
func (d *Decoder) skipN(n int) error {
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/codes"
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type queryResult struct {
|
type queryResult struct {
|
|
@ -4,11 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/codes"
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const sliceElemsAllocLimit = 1e4
|
|
||||||
|
|
||||||
var sliceStringPtrType = reflect.TypeOf((*[]string)(nil))
|
var sliceStringPtrType = reflect.TypeOf((*[]string)(nil))
|
||||||
|
|
||||||
// DecodeArrayLen decodes array length. Length is -1 when array is nil.
|
// DecodeArrayLen decodes array length. Length is -1 when array is nil.
|
||||||
|
@ -51,7 +49,7 @@ func (d *Decoder) decodeStringSlicePtr(ptr *[]string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ss := setStringsCap(*ptr, n)
|
ss := makeStrings(*ptr, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
s, err := d.DecodeString()
|
s, err := d.DecodeString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -64,9 +62,9 @@ func (d *Decoder) decodeStringSlicePtr(ptr *[]string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setStringsCap(s []string, n int) []string {
|
func makeStrings(s []string, n int) []string {
|
||||||
if n > sliceElemsAllocLimit {
|
if n > sliceAllocLimit {
|
||||||
n = sliceElemsAllocLimit
|
n = sliceAllocLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
if s == nil {
|
if s == nil {
|
||||||
|
@ -107,8 +105,8 @@ func decodeSliceValue(d *Decoder, v reflect.Value) error {
|
||||||
if i >= v.Len() {
|
if i >= v.Len() {
|
||||||
v.Set(growSliceValue(v, n))
|
v.Set(growSliceValue(v, n))
|
||||||
}
|
}
|
||||||
sv := v.Index(i)
|
elem := v.Index(i)
|
||||||
if err := d.DecodeValue(sv); err != nil {
|
if err := d.DecodeValue(elem); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,8 +116,8 @@ func decodeSliceValue(d *Decoder, v reflect.Value) error {
|
||||||
|
|
||||||
func growSliceValue(v reflect.Value, n int) reflect.Value {
|
func growSliceValue(v reflect.Value, n int) reflect.Value {
|
||||||
diff := n - v.Len()
|
diff := n - v.Len()
|
||||||
if diff > sliceElemsAllocLimit {
|
if diff > sliceAllocLimit {
|
||||||
diff = sliceElemsAllocLimit
|
diff = sliceAllocLimit
|
||||||
}
|
}
|
||||||
v = reflect.AppendSlice(v, reflect.MakeSlice(v.Type(), diff, diff))
|
v = reflect.AppendSlice(v, reflect.MakeSlice(v.Type(), diff, diff))
|
||||||
return v
|
return v
|
||||||
|
@ -134,10 +132,10 @@ func decodeArrayValue(d *Decoder, v reflect.Value) error {
|
||||||
if n == -1 {
|
if n == -1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if n > v.Len() {
|
if n > v.Len() {
|
||||||
return fmt.Errorf("%s len is %d, but msgpack has %d elements", v.Type(), v.Len(), n)
|
return fmt.Errorf("%s len is %d, but msgpack has %d elements", v.Type(), v.Len(), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
sv := v.Index(i)
|
sv := v.Index(i)
|
||||||
if err := d.DecodeValue(sv); err != nil {
|
if err := d.DecodeValue(sv); err != nil {
|
||||||
|
@ -165,7 +163,7 @@ func (d *Decoder) decodeSlice(c codes.Code) ([]interface{}, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
s := make([]interface{}, 0, min(n, sliceElemsAllocLimit))
|
s := make([]interface{}, 0, min(n, sliceAllocLimit))
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
v, err := d.decodeInterfaceCond()
|
v, err := d.decodeInterfaceCond()
|
||||||
if err != nil {
|
if err != nil {
|
|
@ -4,15 +4,18 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/codes"
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d *Decoder) bytesLen(c codes.Code) (int, error) {
|
func (d *Decoder) bytesLen(c codes.Code) (int, error) {
|
||||||
if c == codes.Nil {
|
if c == codes.Nil {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
} else if codes.IsFixedString(c) {
|
}
|
||||||
|
|
||||||
|
if codes.IsFixedString(c) {
|
||||||
return int(c & codes.FixedStrMask), nil
|
return int(c & codes.FixedStrMask), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch c {
|
switch c {
|
||||||
case codes.Str8, codes.Bin8:
|
case codes.Str8, codes.Bin8:
|
||||||
n, err := d.uint8()
|
n, err := d.uint8()
|
||||||
|
@ -24,6 +27,7 @@ func (d *Decoder) bytesLen(c codes.Code) (int, error) {
|
||||||
n, err := d.uint32()
|
n, err := d.uint32()
|
||||||
return int(n), err
|
return int(n), err
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0, fmt.Errorf("msgpack: invalid code=%x decoding bytes length", c)
|
return 0, fmt.Errorf("msgpack: invalid code=%x decoding bytes length", c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +44,11 @@ func (d *Decoder) string(c codes.Code) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if n == -1 {
|
return d.stringWithLen(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) stringWithLen(n int) (string, error) {
|
||||||
|
if n <= 0 {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
b, err := d.readN(n)
|
b, err := d.readN(n)
|
||||||
|
@ -48,13 +56,15 @@ func (d *Decoder) string(c codes.Code) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeStringValue(d *Decoder, v reflect.Value) error {
|
func decodeStringValue(d *Decoder, v reflect.Value) error {
|
||||||
|
if err := mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
s, err := d.DecodeString()
|
s, err := d.DecodeString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = mustSet(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.SetString(s)
|
v.SetString(s)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -128,13 +138,17 @@ func (d *Decoder) skipBytes(c codes.Code) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if n == -1 {
|
if n <= 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return d.skipN(n)
|
return d.skipN(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeBytesValue(d *Decoder, v reflect.Value) error {
|
func decodeBytesValue(d *Decoder, v reflect.Value) error {
|
||||||
|
if err := mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
c, err := d.readCode()
|
c, err := d.readCode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -145,9 +159,6 @@ func decodeBytesValue(d *Decoder, v reflect.Value) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mustSet(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.SetBytes(b)
|
v.SetBytes(b)
|
||||||
|
|
||||||
return nil
|
return nil
|
|
@ -1,6 +1,7 @@
|
||||||
package msgpack
|
package msgpack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -11,6 +12,7 @@ var stringType = reflect.TypeOf((*string)(nil)).Elem()
|
||||||
|
|
||||||
var valueDecoders []decoderFunc
|
var valueDecoders []decoderFunc
|
||||||
|
|
||||||
|
//nolint:gochecknoinits
|
||||||
func init() {
|
func init() {
|
||||||
valueDecoders = []decoderFunc{
|
valueDecoders = []decoderFunc{
|
||||||
reflect.Bool: decodeBoolValue,
|
reflect.Bool: decodeBoolValue,
|
||||||
|
@ -49,12 +51,16 @@ func mustSet(v reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDecoder(typ reflect.Type) decoderFunc {
|
func getDecoder(typ reflect.Type) decoderFunc {
|
||||||
kind := typ.Kind()
|
if v, ok := typeDecMap.Load(typ); ok {
|
||||||
|
return v.(decoderFunc)
|
||||||
decoder, ok := typDecMap[typ]
|
|
||||||
if ok {
|
|
||||||
return decoder
|
|
||||||
}
|
}
|
||||||
|
fn := _getDecoder(typ)
|
||||||
|
typeDecMap.Store(typ, fn)
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func _getDecoder(typ reflect.Type) decoderFunc {
|
||||||
|
kind := typ.Kind()
|
||||||
|
|
||||||
if typ.Implements(customDecoderType) {
|
if typ.Implements(customDecoderType) {
|
||||||
return decodeCustomValue
|
return decodeCustomValue
|
||||||
|
@ -62,6 +68,9 @@ func getDecoder(typ reflect.Type) decoderFunc {
|
||||||
if typ.Implements(unmarshalerType) {
|
if typ.Implements(unmarshalerType) {
|
||||||
return unmarshalValue
|
return unmarshalValue
|
||||||
}
|
}
|
||||||
|
if typ.Implements(binaryUnmarshalerType) {
|
||||||
|
return unmarshalBinaryValue
|
||||||
|
}
|
||||||
|
|
||||||
// Addressable struct field value.
|
// Addressable struct field value.
|
||||||
if kind != reflect.Ptr {
|
if kind != reflect.Ptr {
|
||||||
|
@ -72,6 +81,9 @@ func getDecoder(typ reflect.Type) decoderFunc {
|
||||||
if ptr.Implements(unmarshalerType) {
|
if ptr.Implements(unmarshalerType) {
|
||||||
return unmarshalValueAddr
|
return unmarshalValueAddr
|
||||||
}
|
}
|
||||||
|
if ptr.Implements(binaryUnmarshalerType) {
|
||||||
|
return unmarshalBinaryValueAddr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch kind {
|
switch kind {
|
||||||
|
@ -79,12 +91,10 @@ func getDecoder(typ reflect.Type) decoderFunc {
|
||||||
return ptrDecoderFunc(typ)
|
return ptrDecoderFunc(typ)
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
elem := typ.Elem()
|
elem := typ.Elem()
|
||||||
switch elem.Kind() {
|
if elem.Kind() == reflect.Uint8 {
|
||||||
case reflect.Uint8:
|
|
||||||
return decodeBytesValue
|
return decodeBytesValue
|
||||||
}
|
}
|
||||||
switch elem {
|
if elem == stringType {
|
||||||
case stringType:
|
|
||||||
return decodeStringSliceValue
|
return decodeStringSliceValue
|
||||||
}
|
}
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
|
@ -101,6 +111,7 @@ func getDecoder(typ reflect.Type) decoderFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return valueDecoders[kind]
|
return valueDecoders[kind]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,31 +165,35 @@ func unmarshalValueAddr(d *Decoder, v reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarshalValue(d *Decoder, v reflect.Value) error {
|
func unmarshalValue(d *Decoder, v reflect.Value) error {
|
||||||
if d.hasNilCode() {
|
if d.extLen == 0 || d.extLen == 1 {
|
||||||
return d.decodeNilValue(v)
|
if d.hasNilCode() {
|
||||||
|
return d.decodeNilValue(v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.IsNil() {
|
if v.IsNil() {
|
||||||
v.Set(reflect.New(v.Type().Elem()))
|
v.Set(reflect.New(v.Type().Elem()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
|
||||||
if d.extLen != 0 {
|
if d.extLen != 0 {
|
||||||
b, err := d.readN(d.extLen)
|
var err error
|
||||||
|
b, err = d.readN(d.extLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
d.rec = b
|
|
||||||
} else {
|
} else {
|
||||||
d.rec = makeBuffer()
|
d.rec = make([]byte, 0, 64)
|
||||||
if err := d.Skip(); err != nil {
|
if err := d.Skip(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
b = d.rec
|
||||||
|
d.rec = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
unmarshaler := v.Interface().(Unmarshaler)
|
unmarshaler := v.Interface().(Unmarshaler)
|
||||||
err := unmarshaler.UnmarshalMsgpack(d.rec)
|
return unmarshaler.UnmarshalMsgpack(b)
|
||||||
d.rec = nil
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeBoolValue(d *Decoder, v reflect.Value) error {
|
func decodeBoolValue(d *Decoder, v reflect.Value) error {
|
||||||
|
@ -232,3 +247,30 @@ func (d *Decoder) interfaceValue(v reflect.Value) error {
|
||||||
func decodeUnsupportedValue(d *Decoder, v reflect.Value) error {
|
func decodeUnsupportedValue(d *Decoder, v reflect.Value) error {
|
||||||
return fmt.Errorf("msgpack: Decode(unsupported %s)", v.Type())
|
return fmt.Errorf("msgpack: Decode(unsupported %s)", v.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func unmarshalBinaryValueAddr(d *Decoder, v reflect.Value) error {
|
||||||
|
if !v.CanAddr() {
|
||||||
|
return fmt.Errorf("msgpack: Decode(nonaddressable %T)", v.Interface())
|
||||||
|
}
|
||||||
|
return unmarshalBinaryValue(d, v.Addr())
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalBinaryValue(d *Decoder, v reflect.Value) error {
|
||||||
|
if d.hasNilCode() {
|
||||||
|
return d.decodeNilValue(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.IsNil() {
|
||||||
|
v.Set(reflect.New(v.Type().Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := d.DecodeBytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshaler := v.Interface().(encoding.BinaryUnmarshaler)
|
||||||
|
return unmarshaler.UnmarshalBinary(data)
|
||||||
|
}
|
|
@ -0,0 +1,241 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
sortMapKeysFlag uint32 = 1 << iota
|
||||||
|
structAsArrayFlag
|
||||||
|
encodeUsingJSONFlag
|
||||||
|
useCompactIntsFlag
|
||||||
|
useCompactFloatsFlag
|
||||||
|
)
|
||||||
|
|
||||||
|
type writer interface {
|
||||||
|
io.Writer
|
||||||
|
WriteByte(byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type byteWriter struct {
|
||||||
|
io.Writer
|
||||||
|
|
||||||
|
buf [1]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newByteWriter(w io.Writer) *byteWriter {
|
||||||
|
bw := new(byteWriter)
|
||||||
|
bw.Reset(w)
|
||||||
|
return bw
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bw *byteWriter) Reset(w io.Writer) {
|
||||||
|
bw.Writer = w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bw *byteWriter) WriteByte(c byte) error {
|
||||||
|
bw.buf[0] = c
|
||||||
|
_, err := bw.Write(bw.buf[:])
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
var encPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return NewEncoder(nil)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal returns the MessagePack encoding of v.
|
||||||
|
func Marshal(v interface{}) ([]byte, error) {
|
||||||
|
enc := encPool.Get().(*Encoder)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc.Reset(&buf)
|
||||||
|
|
||||||
|
err := enc.Encode(v)
|
||||||
|
b := buf.Bytes()
|
||||||
|
|
||||||
|
encPool.Put(enc)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Encoder struct {
|
||||||
|
w writer
|
||||||
|
|
||||||
|
buf []byte
|
||||||
|
timeBuf []byte
|
||||||
|
bootstrap [9 + 12]byte
|
||||||
|
|
||||||
|
intern map[string]int
|
||||||
|
|
||||||
|
flags uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns a new encoder that writes to w.
|
||||||
|
func NewEncoder(w io.Writer) *Encoder {
|
||||||
|
e := new(Encoder)
|
||||||
|
e.buf = e.bootstrap[:9]
|
||||||
|
e.timeBuf = e.bootstrap[9 : 9+12]
|
||||||
|
e.Reset(w)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) Reset(w io.Writer) {
|
||||||
|
if bw, ok := w.(writer); ok {
|
||||||
|
e.w = bw
|
||||||
|
} else if bw, ok := e.w.(*byteWriter); ok {
|
||||||
|
bw.Reset(w)
|
||||||
|
} else {
|
||||||
|
e.w = newByteWriter(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := range e.intern {
|
||||||
|
delete(e.intern, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO:
|
||||||
|
//e.sortMapKeys = false
|
||||||
|
//e.structAsArray = false
|
||||||
|
//e.useJSONTag = false
|
||||||
|
//e.useCompact = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortMapKeys causes the Encoder to encode map keys in increasing order.
|
||||||
|
// Supported map types are:
|
||||||
|
// - map[string]string
|
||||||
|
// - map[string]interface{}
|
||||||
|
func (e *Encoder) SortMapKeys(on bool) *Encoder {
|
||||||
|
if on {
|
||||||
|
e.flags |= sortMapKeysFlag
|
||||||
|
} else {
|
||||||
|
e.flags &= ^sortMapKeysFlag
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructAsArray causes the Encoder to encode Go structs as msgpack arrays.
|
||||||
|
func (e *Encoder) StructAsArray(on bool) *Encoder {
|
||||||
|
if on {
|
||||||
|
e.flags |= structAsArrayFlag
|
||||||
|
} else {
|
||||||
|
e.flags &= ^structAsArrayFlag
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseJSONTag causes the Encoder to use json struct tag as fallback option
|
||||||
|
// if there is no msgpack tag.
|
||||||
|
func (e *Encoder) UseJSONTag(on bool) *Encoder {
|
||||||
|
if on {
|
||||||
|
e.flags |= encodeUsingJSONFlag
|
||||||
|
} else {
|
||||||
|
e.flags &= ^encodeUsingJSONFlag
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseCompactEncoding causes the Encoder to chose the most compact encoding.
|
||||||
|
// For example, it allows to encode small Go int64 as msgpack int8 saving 7 bytes.
|
||||||
|
func (e *Encoder) UseCompactEncoding(on bool) *Encoder {
|
||||||
|
if on {
|
||||||
|
e.flags |= useCompactIntsFlag
|
||||||
|
} else {
|
||||||
|
e.flags &= ^useCompactIntsFlag
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseCompactFloats causes the Encoder to chose a compact integer encoding
|
||||||
|
// for floats that can be represented as integers.
|
||||||
|
func (e *Encoder) UseCompactFloats(on bool) {
|
||||||
|
if on {
|
||||||
|
e.flags |= useCompactFloatsFlag
|
||||||
|
} else {
|
||||||
|
e.flags &= ^useCompactFloatsFlag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) Encode(v interface{}) error {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case nil:
|
||||||
|
return e.EncodeNil()
|
||||||
|
case string:
|
||||||
|
return e.EncodeString(v)
|
||||||
|
case []byte:
|
||||||
|
return e.EncodeBytes(v)
|
||||||
|
case int:
|
||||||
|
return e.encodeInt64Cond(int64(v))
|
||||||
|
case int64:
|
||||||
|
return e.encodeInt64Cond(v)
|
||||||
|
case uint:
|
||||||
|
return e.encodeUint64Cond(uint64(v))
|
||||||
|
case uint64:
|
||||||
|
return e.encodeUint64Cond(v)
|
||||||
|
case bool:
|
||||||
|
return e.EncodeBool(v)
|
||||||
|
case float32:
|
||||||
|
return e.EncodeFloat32(v)
|
||||||
|
case float64:
|
||||||
|
return e.EncodeFloat64(v)
|
||||||
|
case time.Duration:
|
||||||
|
return e.encodeInt64Cond(int64(v))
|
||||||
|
case time.Time:
|
||||||
|
return e.EncodeTime(v)
|
||||||
|
}
|
||||||
|
return e.EncodeValue(reflect.ValueOf(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeMulti(v ...interface{}) error {
|
||||||
|
for _, vv := range v {
|
||||||
|
if err := e.Encode(vv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeValue(v reflect.Value) error {
|
||||||
|
fn := getEncoder(v.Type())
|
||||||
|
return fn(e, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeNil() error {
|
||||||
|
return e.writeCode(codes.Nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeBool(value bool) error {
|
||||||
|
if value {
|
||||||
|
return e.writeCode(codes.True)
|
||||||
|
}
|
||||||
|
return e.writeCode(codes.False)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeDuration(d time.Duration) error {
|
||||||
|
return e.EncodeInt(int64(d))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) writeCode(c codes.Code) error {
|
||||||
|
return e.w.WriteByte(byte(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) write(b []byte) error {
|
||||||
|
_, err := e.w.Write(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) writeString(s string) error {
|
||||||
|
_, err := e.w.Write(stringToBytes(s))
|
||||||
|
return err
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/codes"
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func encodeMapValue(e *Encoder, v reflect.Value) error {
|
func encodeMapValue(e *Encoder, v reflect.Value) error {
|
||||||
|
@ -38,7 +38,7 @@ func encodeMapStringStringValue(e *Encoder, v reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
m := v.Convert(mapStringStringType).Interface().(map[string]string)
|
m := v.Convert(mapStringStringType).Interface().(map[string]string)
|
||||||
if e.sortMapKeys {
|
if e.flags&sortMapKeysFlag != 0 {
|
||||||
return e.encodeSortedMapStringString(m)
|
return e.encodeSortedMapStringString(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ func encodeMapStringInterfaceValue(e *Encoder, v reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
m := v.Convert(mapStringInterfaceType).Interface().(map[string]interface{})
|
m := v.Convert(mapStringInterfaceType).Interface().(map[string]interface{})
|
||||||
if e.sortMapKeys {
|
if e.flags&sortMapKeysFlag != 0 {
|
||||||
return e.encodeSortedMapStringInterface(m)
|
return e.encodeSortedMapStringInterface(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ func encodeMapStringInterfaceValue(e *Encoder, v reflect.Value) error {
|
||||||
|
|
||||||
func (e *Encoder) encodeSortedMapStringString(m map[string]string) error {
|
func (e *Encoder) encodeSortedMapStringString(m map[string]string) error {
|
||||||
keys := make([]string, 0, len(m))
|
keys := make([]string, 0, len(m))
|
||||||
for k, _ := range m {
|
for k := range m {
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
}
|
}
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
|
@ -102,7 +102,7 @@ func (e *Encoder) encodeSortedMapStringString(m map[string]string) error {
|
||||||
|
|
||||||
func (e *Encoder) encodeSortedMapStringInterface(m map[string]interface{}) error {
|
func (e *Encoder) encodeSortedMapStringInterface(m map[string]interface{}) error {
|
||||||
keys := make([]string, 0, len(m))
|
keys := make([]string, 0, len(m))
|
||||||
for k, _ := range m {
|
for k := range m {
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
}
|
}
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
|
@ -132,13 +132,13 @@ func (e *Encoder) EncodeMapLen(l int) error {
|
||||||
|
|
||||||
func encodeStructValue(e *Encoder, strct reflect.Value) error {
|
func encodeStructValue(e *Encoder, strct reflect.Value) error {
|
||||||
var structFields *fields
|
var structFields *fields
|
||||||
if e.useJSONTag {
|
if e.flags&encodeUsingJSONFlag != 0 {
|
||||||
structFields = jsonStructs.Fields(strct.Type())
|
structFields = jsonStructs.Fields(strct.Type())
|
||||||
} else {
|
} else {
|
||||||
structFields = structs.Fields(strct.Type())
|
structFields = structs.Fields(strct.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.structAsArray || structFields.AsArray {
|
if e.flags&structAsArrayFlag != 0 || structFields.AsArray {
|
||||||
return encodeStructValueAsArray(e, strct, structFields.List)
|
return encodeStructValueAsArray(e, strct, structFields.List)
|
||||||
}
|
}
|
||||||
fields := structFields.OmitEmpty(strct)
|
fields := structFields.OmitEmpty(strct)
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/codes"
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EncodeUint8 encodes an uint8 in 2 bytes preserving type of the number.
|
// EncodeUint8 encodes an uint8 in 2 bytes preserving type of the number.
|
||||||
|
@ -13,7 +13,7 @@ func (e *Encoder) EncodeUint8(n uint8) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) encodeUint8Cond(n uint8) error {
|
func (e *Encoder) encodeUint8Cond(n uint8) error {
|
||||||
if e.useCompact {
|
if e.flags&useCompactIntsFlag != 0 {
|
||||||
return e.EncodeUint(uint64(n))
|
return e.EncodeUint(uint64(n))
|
||||||
}
|
}
|
||||||
return e.EncodeUint8(n)
|
return e.EncodeUint8(n)
|
||||||
|
@ -25,7 +25,7 @@ func (e *Encoder) EncodeUint16(n uint16) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) encodeUint16Cond(n uint16) error {
|
func (e *Encoder) encodeUint16Cond(n uint16) error {
|
||||||
if e.useCompact {
|
if e.flags&useCompactIntsFlag != 0 {
|
||||||
return e.EncodeUint(uint64(n))
|
return e.EncodeUint(uint64(n))
|
||||||
}
|
}
|
||||||
return e.EncodeUint16(n)
|
return e.EncodeUint16(n)
|
||||||
|
@ -37,7 +37,7 @@ func (e *Encoder) EncodeUint32(n uint32) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) encodeUint32Cond(n uint32) error {
|
func (e *Encoder) encodeUint32Cond(n uint32) error {
|
||||||
if e.useCompact {
|
if e.flags&useCompactIntsFlag != 0 {
|
||||||
return e.EncodeUint(uint64(n))
|
return e.EncodeUint(uint64(n))
|
||||||
}
|
}
|
||||||
return e.EncodeUint32(n)
|
return e.EncodeUint32(n)
|
||||||
|
@ -49,7 +49,7 @@ func (e *Encoder) EncodeUint64(n uint64) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) encodeUint64Cond(n uint64) error {
|
func (e *Encoder) encodeUint64Cond(n uint64) error {
|
||||||
if e.useCompact {
|
if e.flags&useCompactIntsFlag != 0 {
|
||||||
return e.EncodeUint(n)
|
return e.EncodeUint(n)
|
||||||
}
|
}
|
||||||
return e.EncodeUint64(n)
|
return e.EncodeUint64(n)
|
||||||
|
@ -61,7 +61,7 @@ func (e *Encoder) EncodeInt8(n int8) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) encodeInt8Cond(n int8) error {
|
func (e *Encoder) encodeInt8Cond(n int8) error {
|
||||||
if e.useCompact {
|
if e.flags&useCompactIntsFlag != 0 {
|
||||||
return e.EncodeInt(int64(n))
|
return e.EncodeInt(int64(n))
|
||||||
}
|
}
|
||||||
return e.EncodeInt8(n)
|
return e.EncodeInt8(n)
|
||||||
|
@ -73,7 +73,7 @@ func (e *Encoder) EncodeInt16(n int16) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) encodeInt16Cond(n int16) error {
|
func (e *Encoder) encodeInt16Cond(n int16) error {
|
||||||
if e.useCompact {
|
if e.flags&useCompactIntsFlag != 0 {
|
||||||
return e.EncodeInt(int64(n))
|
return e.EncodeInt(int64(n))
|
||||||
}
|
}
|
||||||
return e.EncodeInt16(n)
|
return e.EncodeInt16(n)
|
||||||
|
@ -85,7 +85,7 @@ func (e *Encoder) EncodeInt32(n int32) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) encodeInt32Cond(n int32) error {
|
func (e *Encoder) encodeInt32Cond(n int32) error {
|
||||||
if e.useCompact {
|
if e.flags&useCompactIntsFlag != 0 {
|
||||||
return e.EncodeInt(int64(n))
|
return e.EncodeInt(int64(n))
|
||||||
}
|
}
|
||||||
return e.EncodeInt32(n)
|
return e.EncodeInt32(n)
|
||||||
|
@ -97,7 +97,7 @@ func (e *Encoder) EncodeInt64(n int64) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) encodeInt64Cond(n int64) error {
|
func (e *Encoder) encodeInt64Cond(n int64) error {
|
||||||
if e.useCompact {
|
if e.flags&useCompactIntsFlag != 0 {
|
||||||
return e.EncodeInt(n)
|
return e.EncodeInt(n)
|
||||||
}
|
}
|
||||||
return e.EncodeInt64(n)
|
return e.EncodeInt64(n)
|
||||||
|
@ -118,11 +118,11 @@ func (e *Encoder) EncodeUint(n uint64) error {
|
||||||
if n <= math.MaxUint32 {
|
if n <= math.MaxUint32 {
|
||||||
return e.EncodeUint32(uint32(n))
|
return e.EncodeUint32(uint32(n))
|
||||||
}
|
}
|
||||||
return e.EncodeUint64(uint64(n))
|
return e.EncodeUint64(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeNumber encodes an int64 in 1, 2, 3, 5, or 9 bytes.
|
// EncodeNumber encodes an int64 in 1, 2, 3, 5, or 9 bytes.
|
||||||
// Type of number is lost during encoding.
|
// Type of the number is lost during encoding.
|
||||||
func (e *Encoder) EncodeInt(n int64) error {
|
func (e *Encoder) EncodeInt(n int64) error {
|
||||||
if n >= 0 {
|
if n >= 0 {
|
||||||
return e.EncodeUint(uint64(n))
|
return e.EncodeUint(uint64(n))
|
||||||
|
@ -139,21 +139,35 @@ func (e *Encoder) EncodeInt(n int64) error {
|
||||||
if n >= math.MinInt32 {
|
if n >= math.MinInt32 {
|
||||||
return e.EncodeInt32(int32(n))
|
return e.EncodeInt32(int32(n))
|
||||||
}
|
}
|
||||||
return e.EncodeInt64(int64(n))
|
return e.EncodeInt64(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) EncodeFloat32(n float32) error {
|
func (e *Encoder) EncodeFloat32(n float32) error {
|
||||||
|
if e.flags&useCompactFloatsFlag != 0 {
|
||||||
|
if float32(int64(n)) == n {
|
||||||
|
return e.EncodeInt(int64(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
return e.write4(codes.Float, math.Float32bits(n))
|
return e.write4(codes.Float, math.Float32bits(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) EncodeFloat64(n float64) error {
|
func (e *Encoder) EncodeFloat64(n float64) error {
|
||||||
|
if e.flags&useCompactFloatsFlag != 0 {
|
||||||
|
// Both NaN and Inf convert to int64(-0x8000000000000000)
|
||||||
|
// If n is NaN then it never compares true with any other value
|
||||||
|
// If n is Inf then it doesn't convert from int64 back to +/-Inf
|
||||||
|
// In both cases the comparison works.
|
||||||
|
if float64(int64(n)) == n {
|
||||||
|
return e.EncodeInt(int64(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
return e.write8(codes.Double, math.Float64bits(n))
|
return e.write8(codes.Double, math.Float64bits(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) write1(code codes.Code, n uint8) error {
|
func (e *Encoder) write1(code codes.Code, n uint8) error {
|
||||||
e.buf = e.buf[:2]
|
e.buf = e.buf[:2]
|
||||||
e.buf[0] = byte(code)
|
e.buf[0] = byte(code)
|
||||||
e.buf[1] = byte(n)
|
e.buf[1] = n
|
||||||
return e.write(e.buf)
|
return e.write(e.buf)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,11 @@ package msgpack
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/codes"
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var sliceStringType = reflect.TypeOf(([]string)(nil))
|
||||||
|
|
||||||
func encodeStringValue(e *Encoder, v reflect.Value) error {
|
func encodeStringValue(e *Encoder, v reflect.Value) error {
|
||||||
return e.EncodeString(v.String())
|
return e.EncodeString(v.String())
|
||||||
}
|
}
|
||||||
|
@ -88,6 +90,11 @@ func (e *Encoder) EncodeArrayLen(l int) error {
|
||||||
return e.write4(codes.Array32, uint32(l))
|
return e.write4(codes.Array32, uint32(l))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func encodeStringSliceValue(e *Encoder, v reflect.Value) error {
|
||||||
|
ss := v.Convert(sliceStringType).Interface().([]string)
|
||||||
|
return e.encodeStringSlice(ss)
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Encoder) encodeStringSlice(s []string) error {
|
func (e *Encoder) encodeStringSlice(s []string) error {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return e.EncodeNil()
|
return e.EncodeNil()
|
|
@ -1,12 +1,14 @@
|
||||||
package msgpack
|
package msgpack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
var valueEncoders []encoderFunc
|
var valueEncoders []encoderFunc
|
||||||
|
|
||||||
|
//nolint:gochecknoinits
|
||||||
func init() {
|
func init() {
|
||||||
valueEncoders = []encoderFunc{
|
valueEncoders = []encoderFunc{
|
||||||
reflect.Bool: encodeBoolValue,
|
reflect.Bool: encodeBoolValue,
|
||||||
|
@ -38,8 +40,21 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEncoder(typ reflect.Type) encoderFunc {
|
func getEncoder(typ reflect.Type) encoderFunc {
|
||||||
if encoder, ok := typEncMap[typ]; ok {
|
if v, ok := typeEncMap.Load(typ); ok {
|
||||||
return encoder
|
return v.(encoderFunc)
|
||||||
|
}
|
||||||
|
fn := _getEncoder(typ)
|
||||||
|
typeEncMap.Store(typ, fn)
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func _getEncoder(typ reflect.Type) encoderFunc {
|
||||||
|
kind := typ.Kind()
|
||||||
|
|
||||||
|
if kind == reflect.Ptr {
|
||||||
|
if _, ok := typeEncMap.Load(typ.Elem()); ok {
|
||||||
|
return ptrEncoderFunc(typ)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ.Implements(customEncoderType) {
|
if typ.Implements(customEncoderType) {
|
||||||
|
@ -48,8 +63,9 @@ func getEncoder(typ reflect.Type) encoderFunc {
|
||||||
if typ.Implements(marshalerType) {
|
if typ.Implements(marshalerType) {
|
||||||
return marshalValue
|
return marshalValue
|
||||||
}
|
}
|
||||||
|
if typ.Implements(binaryMarshalerType) {
|
||||||
kind := typ.Kind()
|
return marshalBinaryValue
|
||||||
|
}
|
||||||
|
|
||||||
// Addressable struct field value.
|
// Addressable struct field value.
|
||||||
if kind != reflect.Ptr {
|
if kind != reflect.Ptr {
|
||||||
|
@ -60,6 +76,9 @@ func getEncoder(typ reflect.Type) encoderFunc {
|
||||||
if ptr.Implements(marshalerType) {
|
if ptr.Implements(marshalerType) {
|
||||||
return marshalValuePtr
|
return marshalValuePtr
|
||||||
}
|
}
|
||||||
|
if ptr.Implements(binaryMarshalerType) {
|
||||||
|
return marshalBinaryValuePtr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if typ == errorType {
|
if typ == errorType {
|
||||||
|
@ -70,9 +89,13 @@ func getEncoder(typ reflect.Type) encoderFunc {
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
return ptrEncoderFunc(typ)
|
return ptrEncoderFunc(typ)
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
if typ.Elem().Kind() == reflect.Uint8 {
|
elem := typ.Elem()
|
||||||
|
if elem.Kind() == reflect.Uint8 {
|
||||||
return encodeByteSliceValue
|
return encodeByteSliceValue
|
||||||
}
|
}
|
||||||
|
if elem == stringType {
|
||||||
|
return encodeStringSliceValue
|
||||||
|
}
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
if typ.Elem().Kind() == reflect.Uint8 {
|
if typ.Elem().Kind() == reflect.Uint8 {
|
||||||
return encodeByteArrayValue
|
return encodeByteArrayValue
|
||||||
|
@ -87,6 +110,7 @@ func getEncoder(typ reflect.Type) encoderFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return valueEncoders[kind]
|
return valueEncoders[kind]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,11 +133,8 @@ func encodeCustomValuePtr(e *Encoder, v reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeCustomValue(e *Encoder, v reflect.Value) error {
|
func encodeCustomValue(e *Encoder, v reflect.Value) error {
|
||||||
switch v.Kind() {
|
if nilable(v) && v.IsNil() {
|
||||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
return e.EncodeNil()
|
||||||
if v.IsNil() {
|
|
||||||
return e.EncodeNil()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
encoder := v.Interface().(CustomEncoder)
|
encoder := v.Interface().(CustomEncoder)
|
||||||
|
@ -128,11 +149,8 @@ func marshalValuePtr(e *Encoder, v reflect.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func marshalValue(e *Encoder, v reflect.Value) error {
|
func marshalValue(e *Encoder, v reflect.Value) error {
|
||||||
switch v.Kind() {
|
if nilable(v) && v.IsNil() {
|
||||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
return e.EncodeNil()
|
||||||
if v.IsNil() {
|
|
||||||
return e.EncodeNil()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
marshaler := v.Interface().(Marshaler)
|
marshaler := v.Interface().(Marshaler)
|
||||||
|
@ -165,3 +183,34 @@ func encodeErrorValue(e *Encoder, v reflect.Value) error {
|
||||||
func encodeUnsupportedValue(e *Encoder, v reflect.Value) error {
|
func encodeUnsupportedValue(e *Encoder, v reflect.Value) error {
|
||||||
return fmt.Errorf("msgpack: Encode(unsupported %s)", v.Type())
|
return fmt.Errorf("msgpack: Encode(unsupported %s)", v.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nilable(v reflect.Value) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func marshalBinaryValuePtr(e *Encoder, v reflect.Value) error {
|
||||||
|
if !v.CanAddr() {
|
||||||
|
return fmt.Errorf("msgpack: Encode(non-addressable %T)", v.Interface())
|
||||||
|
}
|
||||||
|
return marshalBinaryValue(e, v.Addr())
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalBinaryValue(e *Encoder, v reflect.Value) error {
|
||||||
|
if nilable(v) && v.IsNil() {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
marshaler := v.Interface().(encoding.BinaryMarshaler)
|
||||||
|
data, err := marshaler.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.EncodeBytes(data)
|
||||||
|
}
|
|
@ -6,10 +6,15 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/codes"
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
var extTypes = make(map[int8]reflect.Type)
|
type extInfo struct {
|
||||||
|
Type reflect.Type
|
||||||
|
Decoder decoderFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
var extTypes = make(map[int8]*extInfo)
|
||||||
|
|
||||||
var bufferPool = &sync.Pool{
|
var bufferPool = &sync.Pool{
|
||||||
New: func() interface{} {
|
New: func() interface{} {
|
||||||
|
@ -39,28 +44,29 @@ func RegisterExt(id int8, value interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerExt(id int8, typ reflect.Type, enc encoderFunc, dec decoderFunc) {
|
func registerExt(id int8, typ reflect.Type, enc encoderFunc, dec decoderFunc) {
|
||||||
if dec != nil {
|
|
||||||
extTypes[id] = typ
|
|
||||||
}
|
|
||||||
if enc != nil {
|
if enc != nil {
|
||||||
typEncMap[typ] = makeExtEncoder(id, enc)
|
typeEncMap.Store(typ, makeExtEncoder(id, enc))
|
||||||
}
|
}
|
||||||
if dec != nil {
|
if dec != nil {
|
||||||
typDecMap[typ] = makeExtDecoder(id, dec)
|
extTypes[id] = &extInfo{
|
||||||
|
Type: typ,
|
||||||
|
Decoder: dec,
|
||||||
|
}
|
||||||
|
typeDecMap.Store(typ, makeExtDecoder(id, dec))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) EncodeExtHeader(typeId int8, length int) error {
|
func (e *Encoder) EncodeExtHeader(typeID int8, length int) error {
|
||||||
if err := e.encodeExtLen(length); err != nil {
|
if err := e.encodeExtLen(length); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := e.w.WriteByte(byte(typeId)); err != nil {
|
if err := e.w.WriteByte(byte(typeID)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeExtEncoder(typeId int8, enc encoderFunc) encoderFunc {
|
func makeExtEncoder(typeID int8, enc encoderFunc) encoderFunc {
|
||||||
return func(e *Encoder, v reflect.Value) error {
|
return func(e *Encoder, v reflect.Value) error {
|
||||||
buf := bufferPool.Get().(*bytes.Buffer)
|
buf := bufferPool.Get().(*bytes.Buffer)
|
||||||
defer bufferPool.Put(buf)
|
defer bufferPool.Put(buf)
|
||||||
|
@ -75,7 +81,7 @@ func makeExtEncoder(typeId int8, enc encoderFunc) encoderFunc {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = e.EncodeExtHeader(typeId, buf.Len())
|
err = e.EncodeExtHeader(typeID, buf.Len())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -83,7 +89,7 @@ func makeExtEncoder(typeId int8, enc encoderFunc) encoderFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeExtDecoder(typeId int8, dec decoderFunc) decoderFunc {
|
func makeExtDecoder(typeID int8, dec decoderFunc) decoderFunc {
|
||||||
return func(d *Decoder, v reflect.Value) error {
|
return func(d *Decoder, v reflect.Value) error {
|
||||||
c, err := d.PeekCode()
|
c, err := d.PeekCode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -99,8 +105,8 @@ func makeExtDecoder(typeId int8, dec decoderFunc) decoderFunc {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if id != typeId {
|
if id != typeID {
|
||||||
return fmt.Errorf("msgpack: got ext type=%d, wanted %d", int8(c), typeId)
|
return fmt.Errorf("msgpack: got ext type=%d, wanted %d", id, typeID)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.extLen = extLen
|
d.extLen = extLen
|
||||||
|
@ -156,43 +162,43 @@ func (d *Decoder) parseExtLen(c codes.Code) (int, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) decodeExtHeader(c codes.Code) (int8, int, error) {
|
func (d *Decoder) extHeader(c codes.Code) (int8, int, error) {
|
||||||
length, err := d.parseExtLen(c)
|
length, err := d.parseExtLen(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
typeId, err := d.readCode()
|
typeID, err := d.readCode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return int8(typeId), length, nil
|
return int8(typeID), length, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) DecodeExtHeader() (typeId int8, length int, err error) {
|
func (d *Decoder) DecodeExtHeader() (typeID int8, length int, err error) {
|
||||||
c, err := d.readCode()
|
c, err := d.readCode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return d.decodeExtHeader(c)
|
return d.extHeader(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) extInterface(c codes.Code) (interface{}, error) {
|
func (d *Decoder) extInterface(c codes.Code) (interface{}, error) {
|
||||||
extId, extLen, err := d.decodeExtHeader(c)
|
extID, extLen, err := d.extHeader(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
typ, ok := extTypes[extId]
|
info, ok := extTypes[extID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("msgpack: unregistered ext id=%d", extId)
|
return nil, fmt.Errorf("msgpack: unknown ext id=%d", extID)
|
||||||
}
|
}
|
||||||
|
|
||||||
v := reflect.New(typ)
|
v := reflect.New(info.Type)
|
||||||
|
|
||||||
d.extLen = extLen
|
d.extLen = extLen
|
||||||
err = d.DecodeValue(v.Elem())
|
err = info.Decoder(d, v.Elem())
|
||||||
d.extLen = 0
|
d.extLen = 0
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
|
@ -0,0 +1,13 @@
|
||||||
|
module github.com/vmihailenco/msgpack/v4
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1
|
||||||
|
github.com/golang/protobuf v1.3.4 // indirect
|
||||||
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
|
github.com/vmihailenco/tagparser v0.1.1
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect
|
||||||
|
google.golang.org/appengine v1.6.5
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
|
||||||
|
)
|
||||||
|
|
||||||
|
go 1.11
|
|
@ -0,0 +1,24 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
|
||||||
|
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
@ -0,0 +1,236 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var internStringExtID int8 = -128
|
||||||
|
|
||||||
|
var errUnexpectedCode = errors.New("msgpack: unexpected code")
|
||||||
|
|
||||||
|
func encodeInternInterfaceValue(e *Encoder, v reflect.Value) error {
|
||||||
|
if v.IsNil() {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
v = v.Elem()
|
||||||
|
if v.Kind() == reflect.String {
|
||||||
|
return encodeInternStringValue(e, v)
|
||||||
|
}
|
||||||
|
return e.EncodeValue(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeInternStringValue(e *Encoder, v reflect.Value) error {
|
||||||
|
s := v.String()
|
||||||
|
|
||||||
|
if s != "" {
|
||||||
|
if idx, ok := e.intern[s]; ok {
|
||||||
|
return e.internStringIndex(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.intern == nil {
|
||||||
|
e.intern = make(map[string]int)
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := len(e.intern)
|
||||||
|
e.intern[s] = idx
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.EncodeString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) internStringIndex(idx int) error {
|
||||||
|
if idx < math.MaxUint8 {
|
||||||
|
if err := e.writeCode(codes.FixExt1); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.w.WriteByte(byte(internStringExtID)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return e.w.WriteByte(byte(idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx < math.MaxUint16 {
|
||||||
|
if err := e.writeCode(codes.FixExt2); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.w.WriteByte(byte(internStringExtID)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.w.WriteByte(byte(idx >> 8)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return e.w.WriteByte(byte(idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
if int64(idx) < math.MaxUint32 {
|
||||||
|
if err := e.writeCode(codes.FixExt4); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.w.WriteByte(byte(internStringExtID)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.w.WriteByte(byte(idx >> 24)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.w.WriteByte(byte(idx >> 16)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.w.WriteByte(byte(idx >> 8)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return e.w.WriteByte(byte(idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("msgpack: intern string index=%d is too large", idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func decodeInternInterfaceValue(d *Decoder, v reflect.Value) error {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := d.internString(c)
|
||||||
|
if err == nil {
|
||||||
|
v.Set(reflect.ValueOf(s))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil && err != errUnexpectedCode {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.s.UnreadByte(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodeInterfaceValue(d, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeInternStringValue(d *Decoder, v reflect.Value) error {
|
||||||
|
if err := mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := d.internString(c)
|
||||||
|
if err != nil {
|
||||||
|
if err == errUnexpectedCode {
|
||||||
|
return fmt.Errorf("msgpack: invalid code=%x decoding intern string", c)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
v.SetString(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) internString(c codes.Code) (string, error) {
|
||||||
|
if codes.IsFixedString(c) {
|
||||||
|
n := int(c & codes.FixedStrMask)
|
||||||
|
return d.internStringWithLen(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c {
|
||||||
|
case codes.FixExt1, codes.FixExt2, codes.FixExt4:
|
||||||
|
typeID, length, err := d.extHeader(c)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if typeID != internStringExtID {
|
||||||
|
err := fmt.Errorf("msgpack: got ext type=%d, wanted %d",
|
||||||
|
typeID, internStringExtID)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, err := d.internStringIndex(length)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.internStringAtIndex(idx)
|
||||||
|
case codes.Str8, codes.Bin8:
|
||||||
|
n, err := d.uint8()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return d.internStringWithLen(int(n))
|
||||||
|
case codes.Str16, codes.Bin16:
|
||||||
|
n, err := d.uint16()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return d.internStringWithLen(int(n))
|
||||||
|
case codes.Str32, codes.Bin32:
|
||||||
|
n, err := d.uint32()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return d.internStringWithLen(int(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errUnexpectedCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) internStringIndex(length int) (int, error) {
|
||||||
|
switch length {
|
||||||
|
case 1:
|
||||||
|
c, err := d.s.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int(c), nil
|
||||||
|
case 2:
|
||||||
|
b, err := d.readN(2)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
n := binary.BigEndian.Uint16(b)
|
||||||
|
return int(n), nil
|
||||||
|
case 4:
|
||||||
|
b, err := d.readN(4)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
n := binary.BigEndian.Uint32(b)
|
||||||
|
return int(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := fmt.Errorf("msgpack: unsupported intern string index length=%d", length)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) internStringAtIndex(idx int) (string, error) {
|
||||||
|
if idx >= len(d.intern) {
|
||||||
|
err := fmt.Errorf("msgpack: intern string with index=%d does not exist", idx)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return d.intern[idx], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) internStringWithLen(n int) (string, error) {
|
||||||
|
if n <= 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := d.stringWithLen(n)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.intern = append(d.intern, s)
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// +build appengine
|
||||||
|
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
// bytesToString converts byte slice to string.
|
||||||
|
func bytesToString(b []byte) string {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringToBytes converts string to byte slice.
|
||||||
|
func stringToBytes(s string) []byte {
|
||||||
|
return []byte(s)
|
||||||
|
}
|
|
@ -6,14 +6,16 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/codes"
|
"github.com/vmihailenco/msgpack/v4/codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
var timeExtId int8 = -1
|
var timeExtID int8 = -1
|
||||||
|
|
||||||
|
var timePtrType = reflect.TypeOf((*time.Time)(nil))
|
||||||
|
|
||||||
|
//nolint:gochecknoinits
|
||||||
func init() {
|
func init() {
|
||||||
timeType := reflect.TypeOf((*time.Time)(nil)).Elem()
|
registerExt(timeExtID, timePtrType.Elem(), encodeTimeValue, decodeTimeValue)
|
||||||
registerExt(timeExtId, timeType, encodeTimeValue, decodeTimeValue)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) EncodeTime(tm time.Time) error {
|
func (e *Encoder) EncodeTime(tm time.Time) error {
|
||||||
|
@ -21,7 +23,7 @@ func (e *Encoder) EncodeTime(tm time.Time) error {
|
||||||
if err := e.encodeExtLen(len(b)); err != nil {
|
if err := e.encodeExtLen(len(b)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := e.w.WriteByte(byte(timeExtId)); err != nil {
|
if err := e.w.WriteByte(byte(timeExtID)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return e.write(b)
|
return e.write(b)
|
||||||
|
@ -32,19 +34,18 @@ func (e *Encoder) encodeTime(tm time.Time) []byte {
|
||||||
if secs>>34 == 0 {
|
if secs>>34 == 0 {
|
||||||
data := uint64(tm.Nanosecond())<<34 | secs
|
data := uint64(tm.Nanosecond())<<34 | secs
|
||||||
if data&0xffffffff00000000 == 0 {
|
if data&0xffffffff00000000 == 0 {
|
||||||
b := make([]byte, 4)
|
b := e.timeBuf[:4]
|
||||||
binary.BigEndian.PutUint32(b, uint32(data))
|
binary.BigEndian.PutUint32(b, uint32(data))
|
||||||
return b
|
return b
|
||||||
} else {
|
|
||||||
b := make([]byte, 8)
|
|
||||||
binary.BigEndian.PutUint64(b, data)
|
|
||||||
return b
|
|
||||||
}
|
}
|
||||||
|
b := e.timeBuf[:8]
|
||||||
|
binary.BigEndian.PutUint64(b, data)
|
||||||
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
b := make([]byte, 12)
|
b := e.timeBuf[:12]
|
||||||
binary.BigEndian.PutUint32(b, uint32(tm.Nanosecond()))
|
binary.BigEndian.PutUint32(b, uint32(tm.Nanosecond()))
|
||||||
binary.BigEndian.PutUint64(b[4:], uint64(secs))
|
binary.BigEndian.PutUint64(b[4:], secs)
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,6 +141,9 @@ func decodeTimeValue(d *Decoder, v reflect.Value) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
v.Set(reflect.ValueOf(tm))
|
|
||||||
|
ptr := v.Addr().Interface().(*time.Time)
|
||||||
|
*ptr = tm
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
|
@ -1,23 +1,41 @@
|
||||||
package msgpack
|
package msgpack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/tagparser"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||||
|
|
||||||
var customEncoderType = reflect.TypeOf((*CustomEncoder)(nil)).Elem()
|
var (
|
||||||
var customDecoderType = reflect.TypeOf((*CustomDecoder)(nil)).Elem()
|
customEncoderType = reflect.TypeOf((*CustomEncoder)(nil)).Elem()
|
||||||
|
customDecoderType = reflect.TypeOf((*CustomDecoder)(nil)).Elem()
|
||||||
|
)
|
||||||
|
|
||||||
var marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
var (
|
||||||
var unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||||
|
unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
||||||
|
)
|
||||||
|
|
||||||
type encoderFunc func(*Encoder, reflect.Value) error
|
var (
|
||||||
type decoderFunc func(*Decoder, reflect.Value) error
|
binaryMarshalerType = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem()
|
||||||
|
binaryUnmarshalerType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()
|
||||||
|
)
|
||||||
|
|
||||||
var typEncMap = make(map[reflect.Type]encoderFunc)
|
type (
|
||||||
var typDecMap = make(map[reflect.Type]decoderFunc)
|
encoderFunc func(*Encoder, reflect.Value) error
|
||||||
|
decoderFunc func(*Decoder, reflect.Value) error
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
typeEncMap sync.Map
|
||||||
|
typeDecMap sync.Map
|
||||||
|
)
|
||||||
|
|
||||||
// Register registers encoder and decoder functions for a value.
|
// Register registers encoder and decoder functions for a value.
|
||||||
// This is low level API and in most cases you should prefer implementing
|
// This is low level API and in most cases you should prefer implementing
|
||||||
|
@ -25,49 +43,39 @@ var typDecMap = make(map[reflect.Type]decoderFunc)
|
||||||
func Register(value interface{}, enc encoderFunc, dec decoderFunc) {
|
func Register(value interface{}, enc encoderFunc, dec decoderFunc) {
|
||||||
typ := reflect.TypeOf(value)
|
typ := reflect.TypeOf(value)
|
||||||
if enc != nil {
|
if enc != nil {
|
||||||
typEncMap[typ] = enc
|
typeEncMap.Store(typ, enc)
|
||||||
}
|
}
|
||||||
if dec != nil {
|
if dec != nil {
|
||||||
typDecMap[typ] = dec
|
typeDecMap.Store(typ, dec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
var structs = newStructCache(false)
|
var (
|
||||||
var jsonStructs = newStructCache(true)
|
structs = newStructCache(false)
|
||||||
|
jsonStructs = newStructCache(true)
|
||||||
|
)
|
||||||
|
|
||||||
type structCache struct {
|
type structCache struct {
|
||||||
mu sync.RWMutex
|
m sync.Map
|
||||||
m map[reflect.Type]*fields
|
|
||||||
|
|
||||||
useJSONTag bool
|
useJSONTag bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStructCache(useJSONTag bool) *structCache {
|
func newStructCache(useJSONTag bool) *structCache {
|
||||||
return &structCache{
|
return &structCache{
|
||||||
m: make(map[reflect.Type]*fields),
|
|
||||||
|
|
||||||
useJSONTag: useJSONTag,
|
useJSONTag: useJSONTag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *structCache) Fields(typ reflect.Type) *fields {
|
func (m *structCache) Fields(typ reflect.Type) *fields {
|
||||||
m.mu.RLock()
|
if v, ok := m.m.Load(typ); ok {
|
||||||
fs, ok := m.m[typ]
|
return v.(*fields)
|
||||||
m.mu.RUnlock()
|
|
||||||
if ok {
|
|
||||||
return fs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m.mu.Lock()
|
fs := getFields(typ, m.useJSONTag)
|
||||||
fs, ok = m.m[typ]
|
m.m.Store(typ, fs)
|
||||||
if !ok {
|
|
||||||
fs = getFields(typ, m.useJSONTag)
|
|
||||||
m.m[typ] = fs
|
|
||||||
}
|
|
||||||
m.mu.Unlock()
|
|
||||||
|
|
||||||
return fs
|
return fs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,84 +89,99 @@ type field struct {
|
||||||
decoder decoderFunc
|
decoder decoderFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *field) value(v reflect.Value) reflect.Value {
|
|
||||||
return fieldByIndex(v, f.index)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *field) Omit(strct reflect.Value) bool {
|
func (f *field) Omit(strct reflect.Value) bool {
|
||||||
return f.omitEmpty && isEmptyValue(f.value(strct))
|
v, isNil := fieldByIndex(strct, f.index)
|
||||||
|
if isNil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return f.omitEmpty && isEmptyValue(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *field) EncodeValue(e *Encoder, strct reflect.Value) error {
|
func (f *field) EncodeValue(e *Encoder, strct reflect.Value) error {
|
||||||
return f.encoder(e, f.value(strct))
|
v, isNil := fieldByIndex(strct, f.index)
|
||||||
|
if isNil {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
return f.encoder(e, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *field) DecodeValue(d *Decoder, strct reflect.Value) error {
|
func (f *field) DecodeValue(d *Decoder, strct reflect.Value) error {
|
||||||
return f.decoder(d, f.value(strct))
|
v := fieldByIndexAlloc(strct, f.index)
|
||||||
|
return f.decoder(d, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
type fields struct {
|
type fields struct {
|
||||||
Table map[string]*field
|
Type reflect.Type
|
||||||
|
Map map[string]*field
|
||||||
List []*field
|
List []*field
|
||||||
AsArray bool
|
AsArray bool
|
||||||
|
|
||||||
hasOmitEmpty bool
|
hasOmitEmpty bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFields(numField int) *fields {
|
func newFields(typ reflect.Type) *fields {
|
||||||
return &fields{
|
return &fields{
|
||||||
Table: make(map[string]*field, numField),
|
Type: typ,
|
||||||
List: make([]*field, 0, numField),
|
Map: make(map[string]*field, typ.NumField()),
|
||||||
|
List: make([]*field, 0, typ.NumField()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *fields) Add(field *field) {
|
func (fs *fields) Add(field *field) {
|
||||||
fs.Table[field.name] = field
|
fs.warnIfFieldExists(field.name)
|
||||||
|
fs.Map[field.name] = field
|
||||||
fs.List = append(fs.List, field)
|
fs.List = append(fs.List, field)
|
||||||
if field.omitEmpty {
|
if field.omitEmpty {
|
||||||
fs.hasOmitEmpty = true
|
fs.hasOmitEmpty = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fs *fields) warnIfFieldExists(name string) {
|
||||||
|
if _, ok := fs.Map[name]; ok {
|
||||||
|
log.Printf("msgpack: %s already has field=%s", fs.Type, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (fs *fields) OmitEmpty(strct reflect.Value) []*field {
|
func (fs *fields) OmitEmpty(strct reflect.Value) []*field {
|
||||||
if !fs.hasOmitEmpty {
|
if !fs.hasOmitEmpty {
|
||||||
return fs.List
|
return fs.List
|
||||||
}
|
}
|
||||||
|
|
||||||
fields := make([]*field, 0, len(fs.List))
|
fields := make([]*field, 0, len(fs.List))
|
||||||
|
|
||||||
for _, f := range fs.List {
|
for _, f := range fs.List {
|
||||||
if !f.Omit(strct) {
|
if !f.Omit(strct) {
|
||||||
fields = append(fields, f)
|
fields = append(fields, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fields
|
return fields
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFields(typ reflect.Type, useJSONTag bool) *fields {
|
func getFields(typ reflect.Type, useJSONTag bool) *fields {
|
||||||
numField := typ.NumField()
|
fs := newFields(typ)
|
||||||
fs := newFields(numField)
|
|
||||||
|
|
||||||
var omitEmpty bool
|
var omitEmpty bool
|
||||||
for i := 0; i < numField; i++ {
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
f := typ.Field(i)
|
f := typ.Field(i)
|
||||||
|
|
||||||
tag := f.Tag.Get("msgpack")
|
tagStr := f.Tag.Get("msgpack")
|
||||||
if useJSONTag && tag == "" {
|
if useJSONTag && tagStr == "" {
|
||||||
tag = f.Tag.Get("json")
|
tagStr = f.Tag.Get("json")
|
||||||
}
|
}
|
||||||
|
|
||||||
name, opt := parseTag(tag)
|
tag := tagparser.Parse(tagStr)
|
||||||
if name == "-" {
|
if tag.Name == "-" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Name == "_msgpack" {
|
if f.Name == "_msgpack" {
|
||||||
if opt.Contains("asArray") {
|
if tag.HasOption("asArray") {
|
||||||
fs.AsArray = true
|
fs.AsArray = true
|
||||||
}
|
}
|
||||||
if opt.Contains("omitempty") {
|
if tag.HasOption("omitempty") {
|
||||||
omitEmpty = true
|
omitEmpty = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,38 +191,65 @@ func getFields(typ reflect.Type, useJSONTag bool) *fields {
|
||||||
}
|
}
|
||||||
|
|
||||||
field := &field{
|
field := &field{
|
||||||
name: name,
|
name: tag.Name,
|
||||||
index: f.Index,
|
index: f.Index,
|
||||||
omitEmpty: omitEmpty || opt.Contains("omitempty"),
|
omitEmpty: omitEmpty || tag.HasOption("omitempty"),
|
||||||
encoder: getEncoder(f.Type),
|
}
|
||||||
decoder: getDecoder(f.Type),
|
|
||||||
|
if tag.HasOption("intern") {
|
||||||
|
switch f.Type.Kind() {
|
||||||
|
case reflect.Interface:
|
||||||
|
field.encoder = encodeInternInterfaceValue
|
||||||
|
field.decoder = decodeInternInterfaceValue
|
||||||
|
case reflect.String:
|
||||||
|
field.encoder = encodeInternStringValue
|
||||||
|
field.decoder = decodeInternStringValue
|
||||||
|
default:
|
||||||
|
err := fmt.Errorf("msgpack: intern strings are not supported on %s", f.Type)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
field.encoder = getEncoder(f.Type)
|
||||||
|
field.decoder = getDecoder(f.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if field.name == "" {
|
if field.name == "" {
|
||||||
field.name = f.Name
|
field.name = f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.Anonymous && !opt.Contains("noinline") {
|
if f.Anonymous && !tag.HasOption("noinline") {
|
||||||
inline := opt.Contains("inline")
|
inline := tag.HasOption("inline")
|
||||||
if inline {
|
if inline {
|
||||||
inlineFields(fs, f.Type, field, useJSONTag)
|
inlineFields(fs, f.Type, field, useJSONTag)
|
||||||
} else {
|
} else {
|
||||||
inline = autoinlineFields(fs, f.Type, field, useJSONTag)
|
inline = shouldInline(fs, f.Type, field, useJSONTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
if inline {
|
if inline {
|
||||||
fs.Table[field.name] = field
|
if _, ok := fs.Map[field.name]; ok {
|
||||||
|
log.Printf("msgpack: %s already has field=%s", fs.Type, field.name)
|
||||||
|
}
|
||||||
|
fs.Map[field.name] = field
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.Add(field)
|
fs.Add(field)
|
||||||
|
|
||||||
|
if alias, ok := tag.Options["alias"]; ok {
|
||||||
|
fs.warnIfFieldExists(alias)
|
||||||
|
fs.Map[alias] = field
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return fs
|
return fs
|
||||||
}
|
}
|
||||||
|
|
||||||
var encodeStructValuePtr uintptr
|
var (
|
||||||
var decodeStructValuePtr uintptr
|
encodeStructValuePtr uintptr
|
||||||
|
decodeStructValuePtr uintptr
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint:gochecknoinits
|
||||||
func init() {
|
func init() {
|
||||||
encodeStructValuePtr = reflect.ValueOf(encodeStructValue).Pointer()
|
encodeStructValuePtr = reflect.ValueOf(encodeStructValue).Pointer()
|
||||||
decodeStructValuePtr = reflect.ValueOf(decodeStructValue).Pointer()
|
decodeStructValuePtr = reflect.ValueOf(decodeStructValue).Pointer()
|
||||||
|
@ -208,7 +258,7 @@ func init() {
|
||||||
func inlineFields(fs *fields, typ reflect.Type, f *field, useJSONTag bool) {
|
func inlineFields(fs *fields, typ reflect.Type, f *field, useJSONTag bool) {
|
||||||
inlinedFields := getFields(typ, useJSONTag).List
|
inlinedFields := getFields(typ, useJSONTag).List
|
||||||
for _, field := range inlinedFields {
|
for _, field := range inlinedFields {
|
||||||
if _, ok := fs.Table[field.name]; ok {
|
if _, ok := fs.Map[field.name]; ok {
|
||||||
// Don't inline shadowed fields.
|
// Don't inline shadowed fields.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -217,7 +267,7 @@ func inlineFields(fs *fields, typ reflect.Type, f *field, useJSONTag bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func autoinlineFields(fs *fields, typ reflect.Type, f *field, useJSONTag bool) bool {
|
func shouldInline(fs *fields, typ reflect.Type, f *field, useJSONTag bool) bool {
|
||||||
var encoder encoderFunc
|
var encoder encoderFunc
|
||||||
var decoder decoderFunc
|
var decoder decoderFunc
|
||||||
|
|
||||||
|
@ -244,7 +294,7 @@ func autoinlineFields(fs *fields, typ reflect.Type, f *field, useJSONTag bool) b
|
||||||
|
|
||||||
inlinedFields := getFields(typ, useJSONTag).List
|
inlinedFields := getFields(typ, useJSONTag).List
|
||||||
for _, field := range inlinedFields {
|
for _, field := range inlinedFields {
|
||||||
if _, ok := fs.Table[field.name]; ok {
|
if _, ok := fs.Map[field.name]; ok {
|
||||||
// Don't auto inline if there are shadowed fields.
|
// Don't auto inline if there are shadowed fields.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -275,11 +325,32 @@ func isEmptyValue(v reflect.Value) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func fieldByIndex(v reflect.Value, index []int) reflect.Value {
|
func fieldByIndex(v reflect.Value, index []int) (_ reflect.Value, isNil bool) {
|
||||||
|
if len(index) == 1 {
|
||||||
|
return v.Field(index[0]), false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, idx := range index {
|
||||||
|
if i > 0 {
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
if v.IsNil() {
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v = v.Field(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldByIndexAlloc(v reflect.Value, index []int) reflect.Value {
|
||||||
if len(index) == 1 {
|
if len(index) == 1 {
|
||||||
return v.Field(index[0])
|
return v.Field(index[0])
|
||||||
}
|
}
|
||||||
for i, x := range index {
|
|
||||||
|
for i, idx := range index {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
var ok bool
|
var ok bool
|
||||||
v, ok = indirectNew(v)
|
v, ok = indirectNew(v)
|
||||||
|
@ -287,8 +358,9 @@ func fieldByIndex(v reflect.Value, index []int) reflect.Value {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
v = v.Field(x)
|
v = v.Field(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// bytesToString converts byte slice to string.
|
||||||
|
func bytesToString(b []byte) string { //nolint:deadcode,unused
|
||||||
|
return *(*string)(unsafe.Pointer(&b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// stringToBytes converts string to byte slice.
|
||||||
|
func stringToBytes(s string) []byte {
|
||||||
|
return *(*[]byte)(unsafe.Pointer(
|
||||||
|
&struct {
|
||||||
|
string
|
||||||
|
Cap int
|
||||||
|
}{s, len(s)},
|
||||||
|
))
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
dist: xenial
|
||||||
|
sudo: false
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.11.x
|
||||||
|
- 1.12.x
|
||||||
|
- tip
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
|
|
||||||
|
go_import_path: github.com/vmihailenco/tagparser
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.17.1
|
||||||
|
|
||||||
|
script:
|
||||||
|
- make
|
||||||
|
- golangci-lint run
|
|
@ -0,0 +1,25 @@
|
||||||
|
Copyright (c) 2019 The github.com/vmihailenco/tagparser Authors.
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,8 @@
|
||||||
|
all:
|
||||||
|
go test ./...
|
||||||
|
go test ./... -short -race
|
||||||
|
go test ./... -run=NONE -bench=. -benchmem
|
||||||
|
env GOOS=linux GOARCH=386 go test ./...
|
||||||
|
go vet ./...
|
||||||
|
go get github.com/gordonklaus/ineffassign
|
||||||
|
ineffassign .
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Opinionated Golang tag parser
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/vmihailenco/tagparser.png?branch=master)](https://travis-ci.org/vmihailenco/tagparser)
|
||||||
|
[![GoDoc](https://godoc.org/github.com/vmihailenco/tagparser?status.svg)](https://godoc.org/github.com/vmihailenco/tagparser)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Install:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
go get -u github.com/vmihailenco/tagparser
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ExampleParse() {
|
||||||
|
tag := tagparser.Parse("some_name,key:value,key2:'complex value'")
|
||||||
|
fmt.Println(tag.Name)
|
||||||
|
fmt.Println(tag.Options)
|
||||||
|
// Output: some_name
|
||||||
|
// map[key:value key2:'complex value']
|
||||||
|
}
|
||||||
|
```
|
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/vmihailenco/tagparser
|
||||||
|
|
||||||
|
go 1.13
|
|
@ -0,0 +1,82 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/tagparser/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Parser struct {
|
||||||
|
b []byte
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(b []byte) *Parser {
|
||||||
|
return &Parser{
|
||||||
|
b: b,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewString(s string) *Parser {
|
||||||
|
return New(internal.StringToBytes(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Bytes() []byte {
|
||||||
|
return p.b[p.i:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Valid() bool {
|
||||||
|
return p.i < len(p.b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Read() byte {
|
||||||
|
if p.Valid() {
|
||||||
|
c := p.b[p.i]
|
||||||
|
p.Advance()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Peek() byte {
|
||||||
|
if p.Valid() {
|
||||||
|
return p.b[p.i]
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Advance() {
|
||||||
|
p.i++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Skip(skip byte) bool {
|
||||||
|
if p.Peek() == skip {
|
||||||
|
p.Advance()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) SkipBytes(skip []byte) bool {
|
||||||
|
if len(skip) > len(p.b[p.i:]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !bytes.Equal(p.b[p.i:p.i+len(skip)], skip) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
p.i += len(skip)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) ReadSep(sep byte) ([]byte, bool) {
|
||||||
|
ind := bytes.IndexByte(p.b[p.i:], sep)
|
||||||
|
if ind == -1 {
|
||||||
|
b := p.b[p.i:]
|
||||||
|
p.i = len(p.b)
|
||||||
|
return b, false
|
||||||
|
}
|
||||||
|
|
||||||
|
b := p.b[p.i : p.i+ind]
|
||||||
|
p.i += ind + 1
|
||||||
|
return b, true
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// +build appengine
|
||||||
|
|
||||||
|
package internal
|
||||||
|
|
||||||
|
func BytesToString(b []byte) string {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringToBytes(s string) []byte {
|
||||||
|
return []byte(s)
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BytesToString converts byte slice to string.
|
||||||
|
func BytesToString(b []byte) string {
|
||||||
|
return *(*string)(unsafe.Pointer(&b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringToBytes converts string to byte slice.
|
||||||
|
func StringToBytes(s string) []byte {
|
||||||
|
return *(*[]byte)(unsafe.Pointer(
|
||||||
|
&struct {
|
||||||
|
string
|
||||||
|
Cap int
|
||||||
|
}{s, len(s)},
|
||||||
|
))
|
||||||
|
}
|
|
@ -0,0 +1,176 @@
|
||||||
|
package tagparser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vmihailenco/tagparser/internal/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Tag struct {
|
||||||
|
Name string
|
||||||
|
Options map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tag) HasOption(name string) bool {
|
||||||
|
_, ok := t.Options[name]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func Parse(s string) *Tag {
|
||||||
|
p := &tagParser{
|
||||||
|
Parser: parser.NewString(s),
|
||||||
|
}
|
||||||
|
p.parseKey()
|
||||||
|
return &p.Tag
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagParser struct {
|
||||||
|
*parser.Parser
|
||||||
|
|
||||||
|
Tag Tag
|
||||||
|
hasName bool
|
||||||
|
key string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tagParser) setTagOption(key, value string) {
|
||||||
|
if !p.hasName {
|
||||||
|
p.hasName = true
|
||||||
|
if key == "" {
|
||||||
|
p.Tag.Name = value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Tag.Options == nil {
|
||||||
|
p.Tag.Options = make(map[string]string)
|
||||||
|
}
|
||||||
|
if key == "" {
|
||||||
|
p.Tag.Options[value] = ""
|
||||||
|
} else {
|
||||||
|
p.Tag.Options[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tagParser) parseKey() {
|
||||||
|
p.key = ""
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
for p.Valid() {
|
||||||
|
c := p.Read()
|
||||||
|
switch c {
|
||||||
|
case ',':
|
||||||
|
p.Skip(' ')
|
||||||
|
p.setTagOption("", string(b))
|
||||||
|
p.parseKey()
|
||||||
|
return
|
||||||
|
case ':':
|
||||||
|
p.key = string(b)
|
||||||
|
p.parseValue()
|
||||||
|
return
|
||||||
|
case '\'':
|
||||||
|
p.parseQuotedValue()
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
b = append(b, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b) > 0 {
|
||||||
|
p.setTagOption("", string(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tagParser) parseValue() {
|
||||||
|
const quote = '\''
|
||||||
|
|
||||||
|
c := p.Peek()
|
||||||
|
if c == quote {
|
||||||
|
p.Skip(quote)
|
||||||
|
p.parseQuotedValue()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
for p.Valid() {
|
||||||
|
c = p.Read()
|
||||||
|
switch c {
|
||||||
|
case '\\':
|
||||||
|
b = append(b, p.Read())
|
||||||
|
case '(':
|
||||||
|
b = append(b, c)
|
||||||
|
b = p.readBrackets(b)
|
||||||
|
case ',':
|
||||||
|
p.Skip(' ')
|
||||||
|
p.setTagOption(p.key, string(b))
|
||||||
|
p.parseKey()
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
b = append(b, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.setTagOption(p.key, string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tagParser) readBrackets(b []byte) []byte {
|
||||||
|
var lvl int
|
||||||
|
loop:
|
||||||
|
for p.Valid() {
|
||||||
|
c := p.Read()
|
||||||
|
switch c {
|
||||||
|
case '\\':
|
||||||
|
b = append(b, p.Read())
|
||||||
|
case '(':
|
||||||
|
b = append(b, c)
|
||||||
|
lvl++
|
||||||
|
case ')':
|
||||||
|
b = append(b, c)
|
||||||
|
lvl--
|
||||||
|
if lvl < 0 {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
b = append(b, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tagParser) parseQuotedValue() {
|
||||||
|
const quote = '\''
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
b = append(b, quote)
|
||||||
|
|
||||||
|
for p.Valid() {
|
||||||
|
bb, ok := p.ReadSep(quote)
|
||||||
|
if !ok {
|
||||||
|
b = append(b, bb...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bb) > 0 && bb[len(bb)-1] == '\\' {
|
||||||
|
b = append(b, bb[:len(bb)-1]...)
|
||||||
|
b = append(b, quote)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, bb...)
|
||||||
|
b = append(b, quote)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
p.setTagOption(p.key, string(b))
|
||||||
|
if p.Skip(',') {
|
||||||
|
p.Skip(' ')
|
||||||
|
}
|
||||||
|
p.parseKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unquote(s string) (string, bool) {
|
||||||
|
const quote = '\''
|
||||||
|
|
||||||
|
if len(s) < 2 {
|
||||||
|
return s, false
|
||||||
|
}
|
||||||
|
if s[0] == quote && s[len(s)-1] == quote {
|
||||||
|
return s[1 : len(s)-1], true
|
||||||
|
}
|
||||||
|
return s, false
|
||||||
|
}
|
|
@ -13,6 +13,14 @@ import (
|
||||||
// if we're converting from a set into a list of the same element type.)
|
// if we're converting from a set into a list of the same element type.)
|
||||||
func conversionCollectionToList(ety cty.Type, conv conversion) conversion {
|
func conversionCollectionToList(ety cty.Type, conv conversion) conversion {
|
||||||
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
return func(val cty.Value, path cty.Path) (cty.Value, error) {
|
||||||
|
if !val.Length().IsKnown() {
|
||||||
|
// If the input collection has an unknown length (which is true
|
||||||
|
// for a set containing unknown values) then our result must be
|
||||||
|
// an unknown list, because we can't predict how many elements
|
||||||
|
// the resulting list should have.
|
||||||
|
return cty.UnknownVal(cty.List(val.Type().ElementType())), nil
|
||||||
|
}
|
||||||
|
|
||||||
elems := make([]cty.Value, 0, val.LengthInt())
|
elems := make([]cty.Value, 0, val.LengthInt())
|
||||||
i := int64(0)
|
i := int64(0)
|
||||||
elemPath := append(path.Copy(), nil)
|
elemPath := append(path.Copy(), nil)
|
||||||
|
@ -458,6 +466,16 @@ func conversionMapToObject(mapType cty.Type, objType cty.Type, unsafe bool) conv
|
||||||
elems[name.AsString()] = val
|
elems[name.AsString()] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for name, aty := range objectAtys {
|
||||||
|
if _, exists := elems[name]; !exists {
|
||||||
|
if optional := objType.AttributeOptional(name); optional {
|
||||||
|
elems[name] = cty.NullVal(aty)
|
||||||
|
} else {
|
||||||
|
return cty.NilVal, path.NewErrorf("map has no element for required attribute %q", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return cty.ObjectVal(elems), nil
|
return cty.ObjectVal(elems), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,17 +11,29 @@ import (
|
||||||
// type, meaning that each attribute of the output type has a corresponding
|
// type, meaning that each attribute of the output type has a corresponding
|
||||||
// attribute in the input type where a recursive conversion is available.
|
// attribute in the input type where a recursive conversion is available.
|
||||||
//
|
//
|
||||||
|
// If the "out" type has any optional attributes, those attributes may be
|
||||||
|
// absent in the "in" type, in which case null values will be used in their
|
||||||
|
// place in the result.
|
||||||
|
//
|
||||||
// Shallow object conversions work the same for both safe and unsafe modes,
|
// Shallow object conversions work the same for both safe and unsafe modes,
|
||||||
// but the safety flag is passed on to recursive conversions and may thus
|
// but the safety flag is passed on to recursive conversions and may thus
|
||||||
// limit the above definition of "subset".
|
// limit the above definition of "subset".
|
||||||
func conversionObjectToObject(in, out cty.Type, unsafe bool) conversion {
|
func conversionObjectToObject(in, out cty.Type, unsafe bool) conversion {
|
||||||
inAtys := in.AttributeTypes()
|
inAtys := in.AttributeTypes()
|
||||||
outAtys := out.AttributeTypes()
|
outAtys := out.AttributeTypes()
|
||||||
|
outOptionals := out.OptionalAttributes()
|
||||||
attrConvs := make(map[string]conversion)
|
attrConvs := make(map[string]conversion)
|
||||||
|
|
||||||
for name, outAty := range outAtys {
|
for name, outAty := range outAtys {
|
||||||
inAty, exists := inAtys[name]
|
inAty, exists := inAtys[name]
|
||||||
if !exists {
|
if !exists {
|
||||||
|
if _, optional := outOptionals[name]; optional {
|
||||||
|
// If it's optional then we'll skip inserting an
|
||||||
|
// attribute conversion and then deal with inserting
|
||||||
|
// the default value in our overall conversion logic
|
||||||
|
// later.
|
||||||
|
continue
|
||||||
|
}
|
||||||
// No conversion is available, then.
|
// No conversion is available, then.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -71,6 +83,13 @@ func conversionObjectToObject(in, out cty.Type, unsafe bool) conversion {
|
||||||
attrVals[name] = val
|
attrVals[name] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for name := range outOptionals {
|
||||||
|
if _, exists := attrVals[name]; !exists {
|
||||||
|
wantTy := outAtys[name]
|
||||||
|
attrVals[name] = cty.NullVal(wantTy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return cty.ObjectVal(attrVals), nil
|
return cty.ObjectVal(attrVals), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,9 @@ func mismatchMessageObjects(got, want cty.Type) string {
|
||||||
for name, wantAty := range wantAtys {
|
for name, wantAty := range wantAtys {
|
||||||
gotAty, exists := gotAtys[name]
|
gotAty, exists := gotAtys[name]
|
||||||
if !exists {
|
if !exists {
|
||||||
missingAttrs = append(missingAttrs, name)
|
if !want.AttributeOptional(name) {
|
||||||
|
missingAttrs = append(missingAttrs, name)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,13 @@ var ElementFunc = function.New(&function.Spec{
|
||||||
},
|
},
|
||||||
Type: func(args []cty.Value) (cty.Type, error) {
|
Type: func(args []cty.Value) (cty.Type, error) {
|
||||||
list := args[0]
|
list := args[0]
|
||||||
|
index := args[1]
|
||||||
|
if index.IsKnown() {
|
||||||
|
if index.LessThan(cty.NumberIntVal(0)).True() {
|
||||||
|
return cty.DynamicPseudoType, fmt.Errorf("cannot use element function with a negative index")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
listTy := list.Type()
|
listTy := list.Type()
|
||||||
switch {
|
switch {
|
||||||
case listTy.IsListType():
|
case listTy.IsListType():
|
||||||
|
@ -173,6 +180,10 @@ var ElementFunc = function.New(&function.Spec{
|
||||||
return cty.DynamicVal, fmt.Errorf("invalid index: %s", err)
|
return cty.DynamicVal, fmt.Errorf("invalid index: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if args[1].LessThan(cty.NumberIntVal(0)).True() {
|
||||||
|
return cty.DynamicVal, fmt.Errorf("cannot use element function with a negative index")
|
||||||
|
}
|
||||||
|
|
||||||
if !args[0].IsKnown() {
|
if !args[0].IsKnown() {
|
||||||
return cty.UnknownVal(retType), nil
|
return cty.UnknownVal(retType), nil
|
||||||
}
|
}
|
||||||
|
@ -492,6 +503,11 @@ var FlattenFunc = function.New(&function.Spec{
|
||||||
// We can flatten lists with unknown values, as long as they are not
|
// We can flatten lists with unknown values, as long as they are not
|
||||||
// lists themselves.
|
// lists themselves.
|
||||||
func flattener(flattenList cty.Value) ([]cty.Value, bool) {
|
func flattener(flattenList cty.Value) ([]cty.Value, bool) {
|
||||||
|
if !flattenList.Length().IsKnown() {
|
||||||
|
// If we don't know the length of what we're flattening then we can't
|
||||||
|
// predict the length of our result yet either.
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
out := make([]cty.Value, 0)
|
out := make([]cty.Value, 0)
|
||||||
for it := flattenList.ElementIterator(); it.Next(); {
|
for it := flattenList.ElementIterator(); it.Next(); {
|
||||||
_, val := it.Element()
|
_, val := it.Element()
|
||||||
|
@ -872,6 +888,10 @@ var SetProductFunc = function.New(&function.Spec{
|
||||||
|
|
||||||
total := 1
|
total := 1
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
|
if !arg.Length().IsKnown() {
|
||||||
|
return cty.UnknownVal(retType), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Because of our type checking function, we are guaranteed that
|
// Because of our type checking function, we are guaranteed that
|
||||||
// all of the arguments are known, non-null values of types that
|
// all of the arguments are known, non-null values of types that
|
||||||
// support LengthInt.
|
// support LengthInt.
|
||||||
|
@ -1019,7 +1039,8 @@ func sliceIndexes(args []cty.Value) (int, int, bool, error) {
|
||||||
var startIndex, endIndex, length int
|
var startIndex, endIndex, length int
|
||||||
var startKnown, endKnown, lengthKnown bool
|
var startKnown, endKnown, lengthKnown bool
|
||||||
|
|
||||||
if args[0].Type().IsTupleType() || args[0].IsKnown() { // if it's a tuple then we always know the length by the type, but lists must be known
|
// If it's a tuple then we always know the length by the type, but collections might be unknown or have unknown length
|
||||||
|
if args[0].Type().IsTupleType() || args[0].Length().IsKnown() {
|
||||||
length = args[0].LengthInt()
|
length = args[0].LengthInt()
|
||||||
lengthKnown = true
|
lengthKnown = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ var FormatListFunc = function.New(&function.Spec{
|
||||||
argTy := arg.Type()
|
argTy := arg.Type()
|
||||||
switch {
|
switch {
|
||||||
case (argTy.IsListType() || argTy.IsSetType() || argTy.IsTupleType()) && !arg.IsNull():
|
case (argTy.IsListType() || argTy.IsSetType() || argTy.IsTupleType()) && !arg.IsNull():
|
||||||
if !argTy.IsTupleType() && !arg.IsKnown() {
|
if !argTy.IsTupleType() && !(arg.IsKnown() && arg.Length().IsKnown()) {
|
||||||
// We can't iterate this one at all yet then, so we can't
|
// We can't iterate this one at all yet then, so we can't
|
||||||
// yet produce a result.
|
// yet produce a result.
|
||||||
unknowns[i] = true
|
unknowns[i] = true
|
||||||
|
|
|
@ -44,7 +44,7 @@ var SetUnionFunc = function.New(&function.Spec{
|
||||||
Type: setOperationReturnType,
|
Type: setOperationReturnType,
|
||||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||||
return s1.Union(s2)
|
return s1.Union(s2)
|
||||||
}),
|
}, true),
|
||||||
})
|
})
|
||||||
|
|
||||||
var SetIntersectionFunc = function.New(&function.Spec{
|
var SetIntersectionFunc = function.New(&function.Spec{
|
||||||
|
@ -63,7 +63,7 @@ var SetIntersectionFunc = function.New(&function.Spec{
|
||||||
Type: setOperationReturnType,
|
Type: setOperationReturnType,
|
||||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||||
return s1.Intersection(s2)
|
return s1.Intersection(s2)
|
||||||
}),
|
}, false),
|
||||||
})
|
})
|
||||||
|
|
||||||
var SetSubtractFunc = function.New(&function.Spec{
|
var SetSubtractFunc = function.New(&function.Spec{
|
||||||
|
@ -82,7 +82,7 @@ var SetSubtractFunc = function.New(&function.Spec{
|
||||||
Type: setOperationReturnType,
|
Type: setOperationReturnType,
|
||||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||||
return s1.Subtract(s2)
|
return s1.Subtract(s2)
|
||||||
}),
|
}, false),
|
||||||
})
|
})
|
||||||
|
|
||||||
var SetSymmetricDifferenceFunc = function.New(&function.Spec{
|
var SetSymmetricDifferenceFunc = function.New(&function.Spec{
|
||||||
|
@ -100,8 +100,8 @@ var SetSymmetricDifferenceFunc = function.New(&function.Spec{
|
||||||
},
|
},
|
||||||
Type: setOperationReturnType,
|
Type: setOperationReturnType,
|
||||||
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
Impl: setOperationImpl(func(s1, s2 cty.ValueSet) cty.ValueSet {
|
||||||
return s1.Subtract(s2)
|
return s1.SymmetricDifference(s2)
|
||||||
}),
|
}, false),
|
||||||
})
|
})
|
||||||
|
|
||||||
// SetHasElement determines whether the given set contains the given value as an
|
// SetHasElement determines whether the given set contains the given value as an
|
||||||
|
@ -187,13 +187,21 @@ func setOperationReturnType(args []cty.Value) (ret cty.Type, err error) {
|
||||||
return cty.Set(newEty), nil
|
return cty.Set(newEty), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setOperationImpl(f func(s1, s2 cty.ValueSet) cty.ValueSet) function.ImplFunc {
|
func setOperationImpl(f func(s1, s2 cty.ValueSet) cty.ValueSet, allowUnknowns bool) function.ImplFunc {
|
||||||
return func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
return func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||||
first := args[0]
|
first := args[0]
|
||||||
first, err = convert.Convert(first, retType)
|
first, err = convert.Convert(first, retType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cty.NilVal, function.NewArgError(0, err)
|
return cty.NilVal, function.NewArgError(0, err)
|
||||||
}
|
}
|
||||||
|
if !allowUnknowns && !first.IsWhollyKnown() {
|
||||||
|
// This set function can produce a correct result only when all
|
||||||
|
// elements are known, because eventually knowing the unknown
|
||||||
|
// values may cause the result to have fewer known elements, or
|
||||||
|
// might cause a result with no unknown elements at all to become
|
||||||
|
// one with a different length.
|
||||||
|
return cty.UnknownVal(retType), nil
|
||||||
|
}
|
||||||
|
|
||||||
set := first.AsValueSet()
|
set := first.AsValueSet()
|
||||||
for i, arg := range args[1:] {
|
for i, arg := range args[1:] {
|
||||||
|
@ -201,6 +209,10 @@ func setOperationImpl(f func(s1, s2 cty.ValueSet) cty.ValueSet) function.ImplFun
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cty.NilVal, function.NewArgError(i+1, err)
|
return cty.NilVal, function.NewArgError(i+1, err)
|
||||||
}
|
}
|
||||||
|
if !allowUnknowns && !arg.IsWhollyKnown() {
|
||||||
|
// (For the same reason as we did this check for "first" above.)
|
||||||
|
return cty.UnknownVal(retType), nil
|
||||||
|
}
|
||||||
|
|
||||||
argSet := arg.AsValueSet()
|
argSet := arg.AsValueSet()
|
||||||
set = f(set, argSet)
|
set = f(set, argSet)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MarshalJSON is an implementation of json.Marshaler that allows Type
|
// MarshalJSON is an implementation of json.Marshaler that allows Type
|
||||||
|
@ -52,6 +53,19 @@ func (t Type) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
buf.WriteString(`["object",`)
|
buf.WriteString(`["object",`)
|
||||||
buf.Write(atysJSON)
|
buf.Write(atysJSON)
|
||||||
|
if optionals := t.OptionalAttributes(); len(optionals) > 0 {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
optionalNames := make([]string, 0, len(optionals))
|
||||||
|
for k := range optionals {
|
||||||
|
optionalNames = append(optionalNames, k)
|
||||||
|
}
|
||||||
|
sort.Strings(optionalNames)
|
||||||
|
optionalsJSON, err := json.Marshal(optionalNames)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf.Write(optionalsJSON)
|
||||||
|
}
|
||||||
buf.WriteRune(']')
|
buf.WriteRune(']')
|
||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
case typeTuple:
|
case typeTuple:
|
||||||
|
@ -148,7 +162,16 @@ func (t *Type) UnmarshalJSON(buf []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*t = Object(atys)
|
if dec.More() {
|
||||||
|
var optionals []string
|
||||||
|
err = dec.Decode(&optionals)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*t = ObjectWithOptionalAttrs(atys, optionals)
|
||||||
|
} else {
|
||||||
|
*t = Object(atys)
|
||||||
|
}
|
||||||
case "tuple":
|
case "tuple":
|
||||||
var etys []Type
|
var etys []Type
|
||||||
err = dec.Decode(&etys)
|
err = dec.Decode(&etys)
|
||||||
|
|
|
@ -3,7 +3,7 @@ package msgpack
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack"
|
"github.com/vmihailenco/msgpack/v4"
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack"
|
"github.com/vmihailenco/msgpack/v4"
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
"github.com/zclconf/go-cty/cty/convert"
|
"github.com/zclconf/go-cty/cty/convert"
|
||||||
)
|
)
|
||||||
|
@ -31,6 +31,7 @@ func Marshal(val cty.Value, ty cty.Type) ([]byte, error) {
|
||||||
var path cty.Path
|
var path cty.Path
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
enc := msgpack.NewEncoder(&buf)
|
enc := msgpack.NewEncoder(&buf)
|
||||||
|
enc.UseCompactEncoding(true)
|
||||||
|
|
||||||
err := marshal(val, ty, path, enc)
|
err := marshal(val, ty, path, enc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -5,8 +5,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack"
|
"github.com/vmihailenco/msgpack/v4"
|
||||||
msgpackcodes "github.com/vmihailenco/msgpack/codes"
|
msgpackcodes "github.com/vmihailenco/msgpack/v4/codes"
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ package msgpack
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack"
|
"github.com/vmihailenco/msgpack/v4"
|
||||||
msgpackCodes "github.com/vmihailenco/msgpack/codes"
|
msgpackCodes "github.com/vmihailenco/msgpack/v4/codes"
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,13 @@ package cty
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
type typeObject struct {
|
type typeObject struct {
|
||||||
typeImplSigil
|
typeImplSigil
|
||||||
AttrTypes map[string]Type
|
AttrTypes map[string]Type
|
||||||
|
AttrOptional map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object creates an object type with the given attribute types.
|
// Object creates an object type with the given attribute types.
|
||||||
|
@ -14,14 +16,52 @@ type typeObject struct {
|
||||||
// After a map is passed to this function the caller must no longer access it,
|
// After a map is passed to this function the caller must no longer access it,
|
||||||
// since ownership is transferred to this library.
|
// since ownership is transferred to this library.
|
||||||
func Object(attrTypes map[string]Type) Type {
|
func Object(attrTypes map[string]Type) Type {
|
||||||
|
return ObjectWithOptionalAttrs(attrTypes, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObjectWithOptionalAttrs creates an object type where some of its attributes
|
||||||
|
// are optional.
|
||||||
|
//
|
||||||
|
// This function is EXPERIMENTAL. The behavior of the function or of any other
|
||||||
|
// functions working either directly or indirectly with a type created by
|
||||||
|
// this function is not currently considered as a compatibility constraint, and
|
||||||
|
// is subject to change even in minor-version releases of this module. Other
|
||||||
|
// modules that work with cty types and values may or may not support object
|
||||||
|
// types with optional attributes; if they do not, their behavior when
|
||||||
|
// receiving one may be non-ideal.
|
||||||
|
//
|
||||||
|
// Optional attributes are significant only when an object type is being used
|
||||||
|
// as a target type for conversion in the "convert" package. A value of an
|
||||||
|
// object type always has a value for each of the attributes in the attribute
|
||||||
|
// types table, with optional values replaced with null during conversion.
|
||||||
|
//
|
||||||
|
// All keys in the optional slice must also exist in the attrTypes map. If not,
|
||||||
|
// this function will panic.
|
||||||
|
//
|
||||||
|
// After a map or array is passed to this function the caller must no longer
|
||||||
|
// access it, since ownership is transferred to this library.
|
||||||
|
func ObjectWithOptionalAttrs(attrTypes map[string]Type, optional []string) Type {
|
||||||
attrTypesNorm := make(map[string]Type, len(attrTypes))
|
attrTypesNorm := make(map[string]Type, len(attrTypes))
|
||||||
for k, v := range attrTypes {
|
for k, v := range attrTypes {
|
||||||
attrTypesNorm[NormalizeString(k)] = v
|
attrTypesNorm[NormalizeString(k)] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var optionalSet map[string]struct{}
|
||||||
|
if len(optional) > 0 {
|
||||||
|
optionalSet = make(map[string]struct{}, len(optional))
|
||||||
|
for _, k := range optional {
|
||||||
|
k = NormalizeString(k)
|
||||||
|
if _, exists := attrTypesNorm[k]; !exists {
|
||||||
|
panic(fmt.Sprintf("optional contains undeclared attribute %q", k))
|
||||||
|
}
|
||||||
|
optionalSet[k] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Type{
|
return Type{
|
||||||
typeObject{
|
typeObject{
|
||||||
AttrTypes: attrTypesNorm,
|
AttrTypes: attrTypesNorm,
|
||||||
|
AttrOptional: optionalSet,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +84,11 @@ func (t typeObject) Equals(other Type) bool {
|
||||||
if !oty.Equals(ty) {
|
if !oty.Equals(ty) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
_, opt := t.AttrOptional[attr]
|
||||||
|
_, oopt := ot.AttrOptional[attr]
|
||||||
|
if opt != oopt {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
@ -66,6 +111,14 @@ func (t typeObject) GoString() string {
|
||||||
if len(t.AttrTypes) == 0 {
|
if len(t.AttrTypes) == 0 {
|
||||||
return "cty.EmptyObject"
|
return "cty.EmptyObject"
|
||||||
}
|
}
|
||||||
|
if len(t.AttrOptional) > 0 {
|
||||||
|
opt := make([]string, len(t.AttrOptional))
|
||||||
|
for k := range t.AttrOptional {
|
||||||
|
opt = append(opt, k)
|
||||||
|
}
|
||||||
|
sort.Strings(opt)
|
||||||
|
return fmt.Sprintf("cty.ObjectWithOptionalAttrs(%#v, %#v)", t.AttrTypes, opt)
|
||||||
|
}
|
||||||
return fmt.Sprintf("cty.Object(%#v)", t.AttrTypes)
|
return fmt.Sprintf("cty.Object(%#v)", t.AttrTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,3 +186,35 @@ func (t Type) AttributeTypes() map[string]Type {
|
||||||
}
|
}
|
||||||
panic("AttributeTypes on non-object Type")
|
panic("AttributeTypes on non-object Type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OptionalAttributes returns a map representing the set of attributes
|
||||||
|
// that are optional. Will panic if the receiver is not an object type
|
||||||
|
// (use IsObjectType to confirm).
|
||||||
|
//
|
||||||
|
// The returned map is part of the internal state of the type, and is provided
|
||||||
|
// for read access only. It is forbidden for any caller to modify the returned
|
||||||
|
// map.
|
||||||
|
func (t Type) OptionalAttributes() map[string]struct{} {
|
||||||
|
if ot, ok := t.typeImpl.(typeObject); ok {
|
||||||
|
return ot.AttrOptional
|
||||||
|
}
|
||||||
|
panic("OptionalAttributes on non-object Type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AttributeOptional returns true if the attribute of the given name is
|
||||||
|
// optional.
|
||||||
|
//
|
||||||
|
// Will panic if the receiver is not an object type (use IsObjectType to
|
||||||
|
// confirm) or if the object type has no such attribute (use HasAttribute to
|
||||||
|
// confirm).
|
||||||
|
func (t Type) AttributeOptional(name string) bool {
|
||||||
|
name = NormalizeString(name)
|
||||||
|
if ot, ok := t.typeImpl.(typeObject); ok {
|
||||||
|
if _, hasAttr := ot.AttrTypes[name]; !hasAttr {
|
||||||
|
panic("no such attribute")
|
||||||
|
}
|
||||||
|
_, exists := ot.AttrOptional[name]
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
panic("AttributeDefaultValue on non-object Type")
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ package cty
|
||||||
type unknownType struct {
|
type unknownType struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown is a special value that can be
|
// unknown is a special value that can be used as the internal value of a
|
||||||
|
// Value to create a placeholder for a value that isn't yet known.
|
||||||
var unknown interface{} = &unknownType{}
|
var unknown interface{} = &unknownType{}
|
||||||
|
|
||||||
// UnknownVal returns an Value that represents an unknown value of the given
|
// UnknownVal returns an Value that represents an unknown value of the given
|
||||||
|
|
|
@ -3,7 +3,6 @@ package cty
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"github.com/zclconf/go-cty/cty/set"
|
"github.com/zclconf/go-cty/cty/set"
|
||||||
)
|
)
|
||||||
|
@ -270,24 +269,26 @@ func (val Value) Equals(other Value) Value {
|
||||||
s2 := other.v.(set.Set)
|
s2 := other.v.(set.Set)
|
||||||
equal := true
|
equal := true
|
||||||
|
|
||||||
// Note that by our definition of sets it's never possible for two
|
// Two sets are equal if all of their values are known and all values
|
||||||
// sets that contain unknown values (directly or indicrectly) to
|
// in one are also in the other.
|
||||||
// ever be equal, even if they are otherwise identical.
|
for it := s1.Iterator(); it.Next(); {
|
||||||
|
rv := it.Value()
|
||||||
// FIXME: iterating both lists and checking each item is not the
|
if rv == unknown { // "unknown" is the internal representation of unknown-ness
|
||||||
// ideal implementation here, but it works with the primitives we
|
return UnknownVal(Bool)
|
||||||
// have in the set implementation. Perhaps the set implementation
|
}
|
||||||
// can provide its own equality test later.
|
if !s2.Has(rv) {
|
||||||
s1.EachValue(func(v interface{}) {
|
|
||||||
if !s2.Has(v) {
|
|
||||||
equal = false
|
equal = false
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
s2.EachValue(func(v interface{}) {
|
for it := s2.Iterator(); it.Next(); {
|
||||||
if !s1.Has(v) {
|
rv := it.Value()
|
||||||
|
if rv == unknown { // "unknown" is the internal representation of unknown-ness
|
||||||
|
return UnknownVal(Bool)
|
||||||
|
}
|
||||||
|
if !s1.Has(rv) {
|
||||||
equal = false
|
equal = false
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
result = equal
|
result = equal
|
||||||
case ty.IsMapType():
|
case ty.IsMapType():
|
||||||
|
@ -462,17 +463,32 @@ func (val Value) RawEquals(other Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
case ty.IsSetType():
|
|
||||||
s1 := val.v.(set.Set)
|
|
||||||
s2 := other.v.(set.Set)
|
|
||||||
|
|
||||||
// Since we're intentionally ignoring our rule that two unknowns
|
case ty.IsSetType():
|
||||||
// are never equal, we can cheat here.
|
// Convert the set values into a slice so that we can compare each
|
||||||
// (This isn't 100% right since e.g. it will fail if the set contains
|
// value. This is safe because the underlying sets are ordered (see
|
||||||
// numbers that are infinite, which DeepEqual can't compare properly.
|
// setRules in set_internals.go), and so the results are guaranteed to
|
||||||
// We're accepting that limitation for simplicity here, since this
|
// be in a consistent order for two equal sets
|
||||||
// function is here primarily for testing.)
|
setList1 := val.AsValueSlice()
|
||||||
return reflect.DeepEqual(s1, s2)
|
setList2 := other.AsValueSlice()
|
||||||
|
|
||||||
|
// If both physical sets have the same length and they have all of their
|
||||||
|
// _known_ values in common, we know that both sets also have the same
|
||||||
|
// number of unknown values.
|
||||||
|
if len(setList1) != len(setList2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range setList1 {
|
||||||
|
eq := setList1[i].RawEquals(setList2[i])
|
||||||
|
if !eq {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here without returning false already then our sets are
|
||||||
|
// equal enough for RawEquals purposes.
|
||||||
|
return true
|
||||||
|
|
||||||
case ty.IsMapType():
|
case ty.IsMapType():
|
||||||
ety := ty.typeImpl.(typeMap).ElementTypeT
|
ety := ty.typeImpl.(typeMap).ElementTypeT
|
||||||
|
@ -955,8 +971,7 @@ func (val Value) HasElement(elem Value) Value {
|
||||||
// If the receiver is null then this function will panic.
|
// If the receiver is null then this function will panic.
|
||||||
//
|
//
|
||||||
// Note that Length is not supported for strings. To determine the length
|
// Note that Length is not supported for strings. To determine the length
|
||||||
// of a string, call AsString and take the length of the native Go string
|
// of a string, use the Length function in funcs/stdlib.
|
||||||
// that is returned.
|
|
||||||
func (val Value) Length() Value {
|
func (val Value) Length() Value {
|
||||||
if val.IsMarked() {
|
if val.IsMarked() {
|
||||||
val, valMarks := val.Unmark()
|
val, valMarks := val.Unmark()
|
||||||
|
@ -971,6 +986,25 @@ func (val Value) Length() Value {
|
||||||
if !val.IsKnown() {
|
if !val.IsKnown() {
|
||||||
return UnknownVal(Number)
|
return UnknownVal(Number)
|
||||||
}
|
}
|
||||||
|
if val.Type().IsSetType() {
|
||||||
|
// The Length rules are a little different for sets because if any
|
||||||
|
// unknown values are present then the values they are standing in for
|
||||||
|
// may or may not be equal to other elements in the set, and thus they
|
||||||
|
// may or may not coalesce with other elements and produce fewer
|
||||||
|
// items in the resulting set.
|
||||||
|
storeLength := int64(val.v.(set.Set).Length())
|
||||||
|
if storeLength == 1 || val.IsWhollyKnown() {
|
||||||
|
// If our set is wholly known then we know its length.
|
||||||
|
//
|
||||||
|
// We also know the length if the physical store has only one
|
||||||
|
// element, even if that element is unknown, because there's
|
||||||
|
// nothing else in the set for it to coalesce with and a single
|
||||||
|
// unknown value cannot represent more than one known value.
|
||||||
|
return NumberIntVal(storeLength)
|
||||||
|
}
|
||||||
|
// Otherwise, we cannot predict the length.
|
||||||
|
return UnknownVal(Number)
|
||||||
|
}
|
||||||
|
|
||||||
return NumberIntVal(int64(val.LengthInt()))
|
return NumberIntVal(int64(val.LengthInt()))
|
||||||
}
|
}
|
||||||
|
@ -980,6 +1014,13 @@ func (val Value) Length() Value {
|
||||||
//
|
//
|
||||||
// This is an integration method provided for the convenience of code bridging
|
// This is an integration method provided for the convenience of code bridging
|
||||||
// into Go's type system.
|
// into Go's type system.
|
||||||
|
//
|
||||||
|
// For backward compatibility with an earlier implementation error, LengthInt's
|
||||||
|
// result can disagree with Length's result for any set containing unknown
|
||||||
|
// values. Length can potentially indicate the set's length is unknown in that
|
||||||
|
// case, whereas LengthInt will return the maximum possible length as if the
|
||||||
|
// unknown values were each a placeholder for a value not equal to any other
|
||||||
|
// value in the set.
|
||||||
func (val Value) LengthInt() int {
|
func (val Value) LengthInt() int {
|
||||||
val.assertUnmarked()
|
val.assertUnmarked()
|
||||||
if val.Type().IsTupleType() {
|
if val.Type().IsTupleType() {
|
||||||
|
@ -1003,6 +1044,15 @@ func (val Value) LengthInt() int {
|
||||||
return len(val.v.([]interface{}))
|
return len(val.v.([]interface{}))
|
||||||
|
|
||||||
case val.ty.IsSetType():
|
case val.ty.IsSetType():
|
||||||
|
// NOTE: This is technically not correct in cases where the set
|
||||||
|
// contains unknown values, because in that case we can't know how
|
||||||
|
// many known values those unknown values are standing in for -- they
|
||||||
|
// might coalesce with other values once known.
|
||||||
|
//
|
||||||
|
// However, this incorrect behavior is preserved for backward
|
||||||
|
// compatibility with callers that were relying on LengthInt rather
|
||||||
|
// than calling Length. Instead of panicking when a set contains an
|
||||||
|
// unknown value, LengthInt returns the largest possible length.
|
||||||
return val.v.(set.Set).Length()
|
return val.v.(set.Set).Length()
|
||||||
|
|
||||||
case val.ty.IsMapType():
|
case val.ty.IsMapType():
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
module google.golang.org/appengine
|
module google.golang.org/appengine
|
||||||
|
|
||||||
|
go 1.11
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang/protobuf v1.3.1
|
github.com/golang/protobuf v1.3.1
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 // indirect
|
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c // indirect
|
|
||||||
golang.org/x/text v0.3.2
|
golang.org/x/text v0.3.2
|
||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b // indirect
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,22 +1,11 @@
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225 h1:kNX+jCowfMYzvlSvJu5pQWEmyWFrBXJ3PBy10xKMXK8=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65 h1:+rhAzEzT3f4JtomfC371qB+0Ola2caSKcY69NUBZrRQ=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65 h1:+rhAzEzT3f4JtomfC371qB+0Ola2caSKcY69NUBZrRQ=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ func limitDial(network, addr string) (net.Conn, error) {
|
||||||
|
|
||||||
// Dial with a timeout in case the API host is MIA.
|
// Dial with a timeout in case the API host is MIA.
|
||||||
// The connection should normally be very fast.
|
// The connection should normally be very fast.
|
||||||
conn, err := net.DialTimeout(network, addr, 500*time.Millisecond)
|
conn, err := net.DialTimeout(network, addr, 10*time.Second)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
limitRelease()
|
limitRelease()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -590,10 +590,13 @@ github.com/ulikunitz/xz
|
||||||
github.com/ulikunitz/xz/internal/hash
|
github.com/ulikunitz/xz/internal/hash
|
||||||
github.com/ulikunitz/xz/internal/xlog
|
github.com/ulikunitz/xz/internal/xlog
|
||||||
github.com/ulikunitz/xz/lzma
|
github.com/ulikunitz/xz/lzma
|
||||||
# github.com/vmihailenco/msgpack v4.0.1+incompatible
|
# github.com/vmihailenco/msgpack/v4 v4.3.12
|
||||||
## explicit
|
github.com/vmihailenco/msgpack/v4
|
||||||
github.com/vmihailenco/msgpack
|
github.com/vmihailenco/msgpack/v4/codes
|
||||||
github.com/vmihailenco/msgpack/codes
|
# github.com/vmihailenco/tagparser v0.1.1
|
||||||
|
github.com/vmihailenco/tagparser
|
||||||
|
github.com/vmihailenco/tagparser/internal
|
||||||
|
github.com/vmihailenco/tagparser/internal/parser
|
||||||
# github.com/xanzy/ssh-agent v0.2.1
|
# github.com/xanzy/ssh-agent v0.2.1
|
||||||
## explicit
|
## explicit
|
||||||
github.com/xanzy/ssh-agent
|
github.com/xanzy/ssh-agent
|
||||||
|
@ -602,7 +605,7 @@ github.com/xanzy/ssh-agent
|
||||||
# github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557
|
# github.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557
|
||||||
## explicit
|
## explicit
|
||||||
github.com/xlab/treeprint
|
github.com/xlab/treeprint
|
||||||
# github.com/zclconf/go-cty v1.5.1
|
# github.com/zclconf/go-cty v1.6.1
|
||||||
## explicit
|
## explicit
|
||||||
github.com/zclconf/go-cty/cty
|
github.com/zclconf/go-cty/cty
|
||||||
github.com/zclconf/go-cty/cty/convert
|
github.com/zclconf/go-cty/cty/convert
|
||||||
|
@ -739,7 +742,7 @@ google.golang.org/api/option
|
||||||
google.golang.org/api/storage/v1
|
google.golang.org/api/storage/v1
|
||||||
google.golang.org/api/transport/http
|
google.golang.org/api/transport/http
|
||||||
google.golang.org/api/transport/http/internal/propagation
|
google.golang.org/api/transport/http/internal/propagation
|
||||||
# google.golang.org/appengine v1.6.1
|
# google.golang.org/appengine v1.6.5
|
||||||
google.golang.org/appengine
|
google.golang.org/appengine
|
||||||
google.golang.org/appengine/datastore
|
google.golang.org/appengine/datastore
|
||||||
google.golang.org/appengine/datastore/internal/cloudkey
|
google.golang.org/appengine/datastore/internal/cloudkey
|
||||||
|
|
Loading…
Reference in New Issue