Merge pull request #4574 from hashicorp/f-base-go-plugin
Base go-plugin client/server
This commit is contained in:
commit
0c77ff37c6
|
@ -0,0 +1,35 @@
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/nomad/plugins/shared/hclspec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BasePlugin is the interface that all Nomad plugins must support.
|
||||||
|
type BasePlugin interface {
|
||||||
|
// PluginInfo describes the type and version of a plugin.
|
||||||
|
PluginInfo() (*PluginInfoResponse, error)
|
||||||
|
|
||||||
|
// ConfigSchema returns the schema for parsing the plugins configuration.
|
||||||
|
ConfigSchema() (*hclspec.Spec, error)
|
||||||
|
|
||||||
|
// SetConfig is used to set the configuration by passing a MessagePack
|
||||||
|
// encoding of it.
|
||||||
|
SetConfig(data []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginInfoResponse returns basic information about the plugin such that Nomad
|
||||||
|
// can decide whether to load the plugin or not.
|
||||||
|
type PluginInfoResponse struct {
|
||||||
|
// Type returns the plugins type
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// PluginApiVersion returns the version of the Nomad plugin API it is built
|
||||||
|
// against.
|
||||||
|
PluginApiVersion string
|
||||||
|
|
||||||
|
// PluginVersion is the version of the plugin.
|
||||||
|
PluginVersion string
|
||||||
|
|
||||||
|
// Name is the plugins name.
|
||||||
|
Name string
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/nomad/plugins/base/proto"
|
||||||
|
"github.com/hashicorp/nomad/plugins/shared/hclspec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// basePluginClient implements the client side of a remote base plugin, using
|
||||||
|
// gRPC to communicate to the remote plugin.
|
||||||
|
type basePluginClient struct {
|
||||||
|
client proto.BasePluginClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basePluginClient) PluginInfo() (*PluginInfoResponse, error) {
|
||||||
|
presp, err := b.client.PluginInfo(context.Background(), &proto.PluginInfoRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptype string
|
||||||
|
switch presp.GetType() {
|
||||||
|
case proto.PluginType_DRIVER:
|
||||||
|
ptype = PluginTypeDriver
|
||||||
|
case proto.PluginType_DEVICE:
|
||||||
|
ptype = PluginTypeDevice
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("plugin is of unknown type: %q", presp.GetType().String())
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &PluginInfoResponse{
|
||||||
|
Type: ptype,
|
||||||
|
PluginApiVersion: presp.GetPluginApiVersion(),
|
||||||
|
PluginVersion: presp.GetPluginVersion(),
|
||||||
|
Name: presp.GetName(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basePluginClient) ConfigSchema() (*hclspec.Spec, error) {
|
||||||
|
presp, err := b.client.ConfigSchema(context.Background(), &proto.ConfigSchemaRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return presp.GetSpec(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basePluginClient) SetConfig(data []byte) error {
|
||||||
|
// Send the config
|
||||||
|
_, err := b.client.SetConfig(context.Background(), &proto.SetConfigRequest{
|
||||||
|
MsgpackConfig: data,
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/nomad/plugins/shared/hclspec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockPlugin is used for testing.
|
||||||
|
// Each function can be set as a closure to make assertions about how data
|
||||||
|
// is passed through the base plugin layer.
|
||||||
|
type MockPlugin struct {
|
||||||
|
PluginInfoF func() (*PluginInfoResponse, error)
|
||||||
|
ConfigSchemaF func() (*hclspec.Spec, error)
|
||||||
|
SetConfigF func([]byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MockPlugin) PluginInfo() (*PluginInfoResponse, error) { return p.PluginInfoF() }
|
||||||
|
func (p *MockPlugin) ConfigSchema() (*hclspec.Spec, error) { return p.ConfigSchemaF() }
|
||||||
|
func (p *MockPlugin) SetConfig(data []byte) error { return p.SetConfigF(data) }
|
|
@ -0,0 +1,45 @@
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
plugin "github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/nomad/plugins/base/proto"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PluginTypeDriver implements the driver plugin interface
|
||||||
|
PluginTypeDriver = "driver"
|
||||||
|
|
||||||
|
// PluginTypeDevice implements the device plugin interface
|
||||||
|
PluginTypeDevice = "device"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Handshake is a common handshake that is shared by all plugins and Nomad.
|
||||||
|
Handshake = plugin.HandshakeConfig{
|
||||||
|
ProtocolVersion: 1,
|
||||||
|
MagicCookieKey: "NOMAD_PLUGIN_MAGIC_COOKIE",
|
||||||
|
MagicCookieValue: "e4327c2e01eabfd75a8a67adb114fb34a757d57eee7728d857a8cec6e91a7255",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// PluginBase is wraps a BasePlugin and implements go-plugins GRPCPlugin
|
||||||
|
// interface to expose the interface over gRPC.
|
||||||
|
type PluginBase struct {
|
||||||
|
plugin.NetRPCUnsupportedPlugin
|
||||||
|
impl BasePlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PluginBase) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
|
||||||
|
proto.RegisterBasePluginServer(s, &basePluginServer{
|
||||||
|
impl: p.impl,
|
||||||
|
broker: broker,
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PluginBase) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
|
||||||
|
return &basePluginClient{client: proto.NewBasePluginClient(c)}, nil
|
||||||
|
}
|
|
@ -0,0 +1,198 @@
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
pb "github.com/golang/protobuf/proto"
|
||||||
|
plugin "github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/nomad/nomad/structs"
|
||||||
|
"github.com/hashicorp/nomad/plugins/shared/hclspec"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
"github.com/zclconf/go-cty/cty/msgpack"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// testSpec is an hcl Spec for testing
|
||||||
|
testSpec = &hclspec.Spec{
|
||||||
|
Block: &hclspec.Spec_Object{
|
||||||
|
Object: &hclspec.Object{
|
||||||
|
Attributes: map[string]*hclspec.Spec{
|
||||||
|
"foo": {
|
||||||
|
Block: &hclspec.Spec_Attr{
|
||||||
|
Attr: &hclspec.Attr{
|
||||||
|
Type: "string",
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"bar": {
|
||||||
|
Block: &hclspec.Spec_Attr{
|
||||||
|
Attr: &hclspec.Attr{
|
||||||
|
Type: "number",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"baz": {
|
||||||
|
Block: &hclspec.Spec_Attr{
|
||||||
|
Attr: &hclspec.Attr{
|
||||||
|
Type: "bool",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// testConfig is used to decode a config from the testSpec
|
||||||
|
type testConfig struct {
|
||||||
|
Foo string `cty:"foo" codec:"foo"`
|
||||||
|
Bar int64 `cty:"bar" codec:"bar"`
|
||||||
|
Baz bool `cty:"baz" codec:"baz"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBasePlugin_PluginInfo_GRPC(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
const (
|
||||||
|
apiVersion = "v0.1.0"
|
||||||
|
pluginVersion = "v0.2.1"
|
||||||
|
pluginName = "mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
knownType := func() (*PluginInfoResponse, error) {
|
||||||
|
info := &PluginInfoResponse{
|
||||||
|
Type: PluginTypeDriver,
|
||||||
|
PluginApiVersion: apiVersion,
|
||||||
|
PluginVersion: pluginVersion,
|
||||||
|
Name: pluginName,
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
unknownType := func() (*PluginInfoResponse, error) {
|
||||||
|
info := &PluginInfoResponse{
|
||||||
|
Type: "bad",
|
||||||
|
PluginApiVersion: apiVersion,
|
||||||
|
PluginVersion: pluginVersion,
|
||||||
|
Name: pluginName,
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mock := &MockPlugin{
|
||||||
|
PluginInfoF: knownType,
|
||||||
|
}
|
||||||
|
|
||||||
|
client, server := plugin.TestPluginGRPCConn(t, map[string]plugin.Plugin{
|
||||||
|
"base": &PluginBase{impl: mock},
|
||||||
|
})
|
||||||
|
defer server.Stop()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
raw, err := client.Dispense("base")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl, ok := raw.(BasePlugin)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("bad: %#v", raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := impl.PluginInfo()
|
||||||
|
require.NoError(err)
|
||||||
|
require.Equal(apiVersion, resp.PluginApiVersion)
|
||||||
|
require.Equal(pluginVersion, resp.PluginVersion)
|
||||||
|
require.Equal(pluginName, resp.Name)
|
||||||
|
require.Equal(PluginTypeDriver, resp.Type)
|
||||||
|
|
||||||
|
// Swap the implementation to return an unknown type
|
||||||
|
mock.PluginInfoF = unknownType
|
||||||
|
_, err = impl.PluginInfo()
|
||||||
|
require.Error(err)
|
||||||
|
require.Contains(err.Error(), "unknown type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBasePlugin_ConfigSchema(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
mock := &MockPlugin{
|
||||||
|
ConfigSchemaF: func() (*hclspec.Spec, error) {
|
||||||
|
return testSpec, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
client, server := plugin.TestPluginGRPCConn(t, map[string]plugin.Plugin{
|
||||||
|
"base": &PluginBase{impl: mock},
|
||||||
|
})
|
||||||
|
defer server.Stop()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
raw, err := client.Dispense("base")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl, ok := raw.(BasePlugin)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("bad: %#v", raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
specOut, err := impl.ConfigSchema()
|
||||||
|
require.NoError(err)
|
||||||
|
require.True(pb.Equal(testSpec, specOut))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBasePlugin_SetConfig(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
var receivedData []byte
|
||||||
|
mock := &MockPlugin{
|
||||||
|
ConfigSchemaF: func() (*hclspec.Spec, error) {
|
||||||
|
return testSpec, nil
|
||||||
|
},
|
||||||
|
SetConfigF: func(data []byte) error {
|
||||||
|
receivedData = data
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
client, server := plugin.TestPluginGRPCConn(t, map[string]plugin.Plugin{
|
||||||
|
"base": &PluginBase{impl: mock},
|
||||||
|
})
|
||||||
|
defer server.Stop()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
raw, err := client.Dispense("base")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl, ok := raw.(BasePlugin)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("bad: %#v", raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.StringVal("v1"),
|
||||||
|
"bar": cty.NumberIntVal(1337),
|
||||||
|
"baz": cty.BoolVal(true),
|
||||||
|
})
|
||||||
|
cdata, err := msgpack.Marshal(config, config.Type())
|
||||||
|
require.NoError(err)
|
||||||
|
require.NoError(impl.SetConfig(cdata))
|
||||||
|
require.Equal(cdata, receivedData)
|
||||||
|
|
||||||
|
// Decode the value back
|
||||||
|
var actual testConfig
|
||||||
|
require.NoError(structs.Decode(receivedData, &actual))
|
||||||
|
require.Equal("v1", actual.Foo)
|
||||||
|
require.EqualValues(1337, actual.Bar)
|
||||||
|
require.True(actual.Baz)
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// source: github.com/hashicorp/nomad/plugins/base/base.proto
|
// source: github.com/hashicorp/nomad/plugins/base/proto/base.proto
|
||||||
|
|
||||||
package base
|
package proto
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
import proto "github.com/golang/protobuf/proto"
|
||||||
import fmt "fmt"
|
import fmt "fmt"
|
||||||
|
@ -29,26 +29,26 @@ type PluginType int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PluginType_UNKNOWN PluginType = 0
|
PluginType_UNKNOWN PluginType = 0
|
||||||
PluginType_DRIVER PluginType = 1
|
PluginType_DRIVER PluginType = 2
|
||||||
PluginType_DEVICE PluginType = 2
|
PluginType_DEVICE PluginType = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
var PluginType_name = map[int32]string{
|
var PluginType_name = map[int32]string{
|
||||||
0: "UNKNOWN",
|
0: "UNKNOWN",
|
||||||
1: "DRIVER",
|
2: "DRIVER",
|
||||||
2: "DEVICE",
|
3: "DEVICE",
|
||||||
}
|
}
|
||||||
var PluginType_value = map[string]int32{
|
var PluginType_value = map[string]int32{
|
||||||
"UNKNOWN": 0,
|
"UNKNOWN": 0,
|
||||||
"DRIVER": 1,
|
"DRIVER": 2,
|
||||||
"DEVICE": 2,
|
"DEVICE": 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x PluginType) String() string {
|
func (x PluginType) String() string {
|
||||||
return proto.EnumName(PluginType_name, int32(x))
|
return proto.EnumName(PluginType_name, int32(x))
|
||||||
}
|
}
|
||||||
func (PluginType) EnumDescriptor() ([]byte, []int) {
|
func (PluginType) EnumDescriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_base_0160727fc8db573d, []int{0}
|
return fileDescriptor_base_9cc78dc32b158b08, []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PluginInfoRequest is used to request the plugins basic information.
|
// PluginInfoRequest is used to request the plugins basic information.
|
||||||
|
@ -62,7 +62,7 @@ func (m *PluginInfoRequest) Reset() { *m = PluginInfoRequest{} }
|
||||||
func (m *PluginInfoRequest) String() string { return proto.CompactTextString(m) }
|
func (m *PluginInfoRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*PluginInfoRequest) ProtoMessage() {}
|
func (*PluginInfoRequest) ProtoMessage() {}
|
||||||
func (*PluginInfoRequest) Descriptor() ([]byte, []int) {
|
func (*PluginInfoRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_base_0160727fc8db573d, []int{0}
|
return fileDescriptor_base_9cc78dc32b158b08, []int{0}
|
||||||
}
|
}
|
||||||
func (m *PluginInfoRequest) XXX_Unmarshal(b []byte) error {
|
func (m *PluginInfoRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_PluginInfoRequest.Unmarshal(m, b)
|
return xxx_messageInfo_PluginInfoRequest.Unmarshal(m, b)
|
||||||
|
@ -86,7 +86,7 @@ var xxx_messageInfo_PluginInfoRequest proto.InternalMessageInfo
|
||||||
// that Nomad can decide whether to load the plugin or not.
|
// that Nomad can decide whether to load the plugin or not.
|
||||||
type PluginInfoResponse struct {
|
type PluginInfoResponse struct {
|
||||||
// type indicates what type of plugin this is.
|
// type indicates what type of plugin this is.
|
||||||
Type PluginType `protobuf:"varint,1,opt,name=type,proto3,enum=hashicorp.nomad.plugins.base.PluginType" json:"type,omitempty"`
|
Type PluginType `protobuf:"varint,1,opt,name=type,proto3,enum=hashicorp.nomad.plugins.base.proto.PluginType" json:"type,omitempty"`
|
||||||
// plugin_api_version indicates the version of the Nomad Plugin API
|
// plugin_api_version indicates the version of the Nomad Plugin API
|
||||||
// this plugin is built against.
|
// this plugin is built against.
|
||||||
PluginApiVersion string `protobuf:"bytes,2,opt,name=plugin_api_version,json=pluginApiVersion,proto3" json:"plugin_api_version,omitempty"`
|
PluginApiVersion string `protobuf:"bytes,2,opt,name=plugin_api_version,json=pluginApiVersion,proto3" json:"plugin_api_version,omitempty"`
|
||||||
|
@ -104,7 +104,7 @@ func (m *PluginInfoResponse) Reset() { *m = PluginInfoResponse{} }
|
||||||
func (m *PluginInfoResponse) String() string { return proto.CompactTextString(m) }
|
func (m *PluginInfoResponse) String() string { return proto.CompactTextString(m) }
|
||||||
func (*PluginInfoResponse) ProtoMessage() {}
|
func (*PluginInfoResponse) ProtoMessage() {}
|
||||||
func (*PluginInfoResponse) Descriptor() ([]byte, []int) {
|
func (*PluginInfoResponse) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_base_0160727fc8db573d, []int{1}
|
return fileDescriptor_base_9cc78dc32b158b08, []int{1}
|
||||||
}
|
}
|
||||||
func (m *PluginInfoResponse) XXX_Unmarshal(b []byte) error {
|
func (m *PluginInfoResponse) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_PluginInfoResponse.Unmarshal(m, b)
|
return xxx_messageInfo_PluginInfoResponse.Unmarshal(m, b)
|
||||||
|
@ -163,7 +163,7 @@ func (m *ConfigSchemaRequest) Reset() { *m = ConfigSchemaRequest{} }
|
||||||
func (m *ConfigSchemaRequest) String() string { return proto.CompactTextString(m) }
|
func (m *ConfigSchemaRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ConfigSchemaRequest) ProtoMessage() {}
|
func (*ConfigSchemaRequest) ProtoMessage() {}
|
||||||
func (*ConfigSchemaRequest) Descriptor() ([]byte, []int) {
|
func (*ConfigSchemaRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_base_0160727fc8db573d, []int{2}
|
return fileDescriptor_base_9cc78dc32b158b08, []int{2}
|
||||||
}
|
}
|
||||||
func (m *ConfigSchemaRequest) XXX_Unmarshal(b []byte) error {
|
func (m *ConfigSchemaRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_ConfigSchemaRequest.Unmarshal(m, b)
|
return xxx_messageInfo_ConfigSchemaRequest.Unmarshal(m, b)
|
||||||
|
@ -196,7 +196,7 @@ func (m *ConfigSchemaResponse) Reset() { *m = ConfigSchemaResponse{} }
|
||||||
func (m *ConfigSchemaResponse) String() string { return proto.CompactTextString(m) }
|
func (m *ConfigSchemaResponse) String() string { return proto.CompactTextString(m) }
|
||||||
func (*ConfigSchemaResponse) ProtoMessage() {}
|
func (*ConfigSchemaResponse) ProtoMessage() {}
|
||||||
func (*ConfigSchemaResponse) Descriptor() ([]byte, []int) {
|
func (*ConfigSchemaResponse) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_base_0160727fc8db573d, []int{3}
|
return fileDescriptor_base_9cc78dc32b158b08, []int{3}
|
||||||
}
|
}
|
||||||
func (m *ConfigSchemaResponse) XXX_Unmarshal(b []byte) error {
|
func (m *ConfigSchemaResponse) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_ConfigSchemaResponse.Unmarshal(m, b)
|
return xxx_messageInfo_ConfigSchemaResponse.Unmarshal(m, b)
|
||||||
|
@ -236,7 +236,7 @@ func (m *SetConfigRequest) Reset() { *m = SetConfigRequest{} }
|
||||||
func (m *SetConfigRequest) String() string { return proto.CompactTextString(m) }
|
func (m *SetConfigRequest) String() string { return proto.CompactTextString(m) }
|
||||||
func (*SetConfigRequest) ProtoMessage() {}
|
func (*SetConfigRequest) ProtoMessage() {}
|
||||||
func (*SetConfigRequest) Descriptor() ([]byte, []int) {
|
func (*SetConfigRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_base_0160727fc8db573d, []int{4}
|
return fileDescriptor_base_9cc78dc32b158b08, []int{4}
|
||||||
}
|
}
|
||||||
func (m *SetConfigRequest) XXX_Unmarshal(b []byte) error {
|
func (m *SetConfigRequest) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_SetConfigRequest.Unmarshal(m, b)
|
return xxx_messageInfo_SetConfigRequest.Unmarshal(m, b)
|
||||||
|
@ -274,7 +274,7 @@ func (m *SetConfigResponse) Reset() { *m = SetConfigResponse{} }
|
||||||
func (m *SetConfigResponse) String() string { return proto.CompactTextString(m) }
|
func (m *SetConfigResponse) String() string { return proto.CompactTextString(m) }
|
||||||
func (*SetConfigResponse) ProtoMessage() {}
|
func (*SetConfigResponse) ProtoMessage() {}
|
||||||
func (*SetConfigResponse) Descriptor() ([]byte, []int) {
|
func (*SetConfigResponse) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_base_0160727fc8db573d, []int{5}
|
return fileDescriptor_base_9cc78dc32b158b08, []int{5}
|
||||||
}
|
}
|
||||||
func (m *SetConfigResponse) XXX_Unmarshal(b []byte) error {
|
func (m *SetConfigResponse) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_SetConfigResponse.Unmarshal(m, b)
|
return xxx_messageInfo_SetConfigResponse.Unmarshal(m, b)
|
||||||
|
@ -295,13 +295,13 @@ func (m *SetConfigResponse) XXX_DiscardUnknown() {
|
||||||
var xxx_messageInfo_SetConfigResponse proto.InternalMessageInfo
|
var xxx_messageInfo_SetConfigResponse proto.InternalMessageInfo
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterType((*PluginInfoRequest)(nil), "hashicorp.nomad.plugins.base.PluginInfoRequest")
|
proto.RegisterType((*PluginInfoRequest)(nil), "hashicorp.nomad.plugins.base.proto.PluginInfoRequest")
|
||||||
proto.RegisterType((*PluginInfoResponse)(nil), "hashicorp.nomad.plugins.base.PluginInfoResponse")
|
proto.RegisterType((*PluginInfoResponse)(nil), "hashicorp.nomad.plugins.base.proto.PluginInfoResponse")
|
||||||
proto.RegisterType((*ConfigSchemaRequest)(nil), "hashicorp.nomad.plugins.base.ConfigSchemaRequest")
|
proto.RegisterType((*ConfigSchemaRequest)(nil), "hashicorp.nomad.plugins.base.proto.ConfigSchemaRequest")
|
||||||
proto.RegisterType((*ConfigSchemaResponse)(nil), "hashicorp.nomad.plugins.base.ConfigSchemaResponse")
|
proto.RegisterType((*ConfigSchemaResponse)(nil), "hashicorp.nomad.plugins.base.proto.ConfigSchemaResponse")
|
||||||
proto.RegisterType((*SetConfigRequest)(nil), "hashicorp.nomad.plugins.base.SetConfigRequest")
|
proto.RegisterType((*SetConfigRequest)(nil), "hashicorp.nomad.plugins.base.proto.SetConfigRequest")
|
||||||
proto.RegisterType((*SetConfigResponse)(nil), "hashicorp.nomad.plugins.base.SetConfigResponse")
|
proto.RegisterType((*SetConfigResponse)(nil), "hashicorp.nomad.plugins.base.proto.SetConfigResponse")
|
||||||
proto.RegisterEnum("hashicorp.nomad.plugins.base.PluginType", PluginType_name, PluginType_value)
|
proto.RegisterEnum("hashicorp.nomad.plugins.base.proto.PluginType", PluginType_name, PluginType_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@ -334,7 +334,7 @@ func NewBasePluginClient(cc *grpc.ClientConn) BasePluginClient {
|
||||||
|
|
||||||
func (c *basePluginClient) PluginInfo(ctx context.Context, in *PluginInfoRequest, opts ...grpc.CallOption) (*PluginInfoResponse, error) {
|
func (c *basePluginClient) PluginInfo(ctx context.Context, in *PluginInfoRequest, opts ...grpc.CallOption) (*PluginInfoResponse, error) {
|
||||||
out := new(PluginInfoResponse)
|
out := new(PluginInfoResponse)
|
||||||
err := c.cc.Invoke(ctx, "/hashicorp.nomad.plugins.base.BasePlugin/PluginInfo", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/hashicorp.nomad.plugins.base.proto.BasePlugin/PluginInfo", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -343,7 +343,7 @@ func (c *basePluginClient) PluginInfo(ctx context.Context, in *PluginInfoRequest
|
||||||
|
|
||||||
func (c *basePluginClient) ConfigSchema(ctx context.Context, in *ConfigSchemaRequest, opts ...grpc.CallOption) (*ConfigSchemaResponse, error) {
|
func (c *basePluginClient) ConfigSchema(ctx context.Context, in *ConfigSchemaRequest, opts ...grpc.CallOption) (*ConfigSchemaResponse, error) {
|
||||||
out := new(ConfigSchemaResponse)
|
out := new(ConfigSchemaResponse)
|
||||||
err := c.cc.Invoke(ctx, "/hashicorp.nomad.plugins.base.BasePlugin/ConfigSchema", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/hashicorp.nomad.plugins.base.proto.BasePlugin/ConfigSchema", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -352,7 +352,7 @@ func (c *basePluginClient) ConfigSchema(ctx context.Context, in *ConfigSchemaReq
|
||||||
|
|
||||||
func (c *basePluginClient) SetConfig(ctx context.Context, in *SetConfigRequest, opts ...grpc.CallOption) (*SetConfigResponse, error) {
|
func (c *basePluginClient) SetConfig(ctx context.Context, in *SetConfigRequest, opts ...grpc.CallOption) (*SetConfigResponse, error) {
|
||||||
out := new(SetConfigResponse)
|
out := new(SetConfigResponse)
|
||||||
err := c.cc.Invoke(ctx, "/hashicorp.nomad.plugins.base.BasePlugin/SetConfig", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/hashicorp.nomad.plugins.base.proto.BasePlugin/SetConfig", in, out, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -383,7 +383,7 @@ func _BasePlugin_PluginInfo_Handler(srv interface{}, ctx context.Context, dec fu
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/hashicorp.nomad.plugins.base.BasePlugin/PluginInfo",
|
FullMethod: "/hashicorp.nomad.plugins.base.proto.BasePlugin/PluginInfo",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(BasePluginServer).PluginInfo(ctx, req.(*PluginInfoRequest))
|
return srv.(BasePluginServer).PluginInfo(ctx, req.(*PluginInfoRequest))
|
||||||
|
@ -401,7 +401,7 @@ func _BasePlugin_ConfigSchema_Handler(srv interface{}, ctx context.Context, dec
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/hashicorp.nomad.plugins.base.BasePlugin/ConfigSchema",
|
FullMethod: "/hashicorp.nomad.plugins.base.proto.BasePlugin/ConfigSchema",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(BasePluginServer).ConfigSchema(ctx, req.(*ConfigSchemaRequest))
|
return srv.(BasePluginServer).ConfigSchema(ctx, req.(*ConfigSchemaRequest))
|
||||||
|
@ -419,7 +419,7 @@ func _BasePlugin_SetConfig_Handler(srv interface{}, ctx context.Context, dec fun
|
||||||
}
|
}
|
||||||
info := &grpc.UnaryServerInfo{
|
info := &grpc.UnaryServerInfo{
|
||||||
Server: srv,
|
Server: srv,
|
||||||
FullMethod: "/hashicorp.nomad.plugins.base.BasePlugin/SetConfig",
|
FullMethod: "/hashicorp.nomad.plugins.base.proto.BasePlugin/SetConfig",
|
||||||
}
|
}
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
return srv.(BasePluginServer).SetConfig(ctx, req.(*SetConfigRequest))
|
return srv.(BasePluginServer).SetConfig(ctx, req.(*SetConfigRequest))
|
||||||
|
@ -428,7 +428,7 @@ func _BasePlugin_SetConfig_Handler(srv interface{}, ctx context.Context, dec fun
|
||||||
}
|
}
|
||||||
|
|
||||||
var _BasePlugin_serviceDesc = grpc.ServiceDesc{
|
var _BasePlugin_serviceDesc = grpc.ServiceDesc{
|
||||||
ServiceName: "hashicorp.nomad.plugins.base.BasePlugin",
|
ServiceName: "hashicorp.nomad.plugins.base.proto.BasePlugin",
|
||||||
HandlerType: (*BasePluginServer)(nil),
|
HandlerType: (*BasePluginServer)(nil),
|
||||||
Methods: []grpc.MethodDesc{
|
Methods: []grpc.MethodDesc{
|
||||||
{
|
{
|
||||||
|
@ -445,40 +445,41 @@ var _BasePlugin_serviceDesc = grpc.ServiceDesc{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Streams: []grpc.StreamDesc{},
|
Streams: []grpc.StreamDesc{},
|
||||||
Metadata: "github.com/hashicorp/nomad/plugins/base/base.proto",
|
Metadata: "github.com/hashicorp/nomad/plugins/base/proto/base.proto",
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterFile("github.com/hashicorp/nomad/plugins/base/base.proto", fileDescriptor_base_0160727fc8db573d)
|
proto.RegisterFile("github.com/hashicorp/nomad/plugins/base/proto/base.proto", fileDescriptor_base_9cc78dc32b158b08)
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileDescriptor_base_0160727fc8db573d = []byte{
|
var fileDescriptor_base_9cc78dc32b158b08 = []byte{
|
||||||
// 430 bytes of a gzipped FileDescriptorProto
|
// 436 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xdf, 0x8b, 0xd3, 0x40,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0xcf, 0x8b, 0xd3, 0x40,
|
||||||
0x10, 0xbe, 0xd4, 0x50, 0xb9, 0xb9, 0x1f, 0xc4, 0x3d, 0x85, 0x12, 0x7c, 0x38, 0x02, 0x42, 0x91,
|
0x14, 0xc7, 0x37, 0xdb, 0xb8, 0xcb, 0xbe, 0xdd, 0x2d, 0x71, 0xaa, 0x50, 0x72, 0x2a, 0x01, 0xa1,
|
||||||
0x63, 0xe3, 0xc5, 0x27, 0xc1, 0x87, 0xb3, 0xb5, 0x0f, 0x45, 0xa8, 0x92, 0x6a, 0x15, 0x5f, 0xc2,
|
0x48, 0x99, 0x60, 0xb5, 0x5a, 0x6f, 0xb5, 0xb5, 0x87, 0x22, 0x54, 0x49, 0xb5, 0x8a, 0x97, 0x30,
|
||||||
0x76, 0xbb, 0x4d, 0x82, 0xcd, 0xee, 0x36, 0x9b, 0x2a, 0xfd, 0xdb, 0xfc, 0x87, 0xfc, 0x33, 0x64,
|
0x9d, 0x4e, 0x93, 0x60, 0x93, 0x19, 0x33, 0xa9, 0x50, 0xc1, 0x93, 0x67, 0xff, 0x28, 0xff, 0x33,
|
||||||
0x77, 0x93, 0x1a, 0x45, 0x4b, 0x7c, 0x49, 0x86, 0x99, 0xef, 0xfb, 0x66, 0xe6, 0x1b, 0x16, 0xa2,
|
0xc9, 0x4c, 0xd2, 0x46, 0x51, 0x4c, 0x4f, 0x79, 0xbc, 0xf7, 0xf9, 0x7e, 0xe7, 0xfd, 0x08, 0x0c,
|
||||||
0x34, 0xaf, 0xb2, 0xdd, 0x12, 0x53, 0x51, 0x84, 0x19, 0x51, 0x59, 0x4e, 0x45, 0x29, 0x43, 0x2e,
|
0x83, 0x28, 0x0b, 0x77, 0x2b, 0x4c, 0x79, 0xec, 0x86, 0x44, 0x86, 0x11, 0xe5, 0xa9, 0x70, 0x13,
|
||||||
0x0a, 0xb2, 0x0a, 0xe5, 0x66, 0x97, 0xe6, 0x5c, 0x85, 0x4b, 0xa2, 0x98, 0xf9, 0x60, 0x59, 0x8a,
|
0x1e, 0x93, 0xb5, 0x2b, 0xb6, 0xbb, 0x20, 0x4a, 0xa4, 0xbb, 0x22, 0x92, 0xb9, 0x22, 0xe5, 0x19,
|
||||||
0x4a, 0xa0, 0xc7, 0x07, 0x20, 0x36, 0x40, 0x5c, 0x03, 0xb1, 0xc6, 0xf8, 0x77, 0x1d, 0x14, 0x55,
|
0x57, 0x21, 0x56, 0x21, 0x72, 0x0e, 0x38, 0x56, 0x38, 0x2e, 0x70, 0x7c, 0x64, 0xec, 0x51, 0x0d,
|
||||||
0x46, 0x4a, 0xb6, 0x0a, 0x33, 0xba, 0x51, 0x92, 0x51, 0xfd, 0x4f, 0x74, 0x60, 0xf5, 0x83, 0x2b,
|
0x77, 0x19, 0x92, 0x94, 0xad, 0xdd, 0x90, 0x6e, 0xa5, 0x60, 0x34, 0xff, 0xfa, 0x79, 0xa0, 0x1d,
|
||||||
0x78, 0xf0, 0xce, 0x00, 0xa7, 0x7c, 0x2d, 0x62, 0xb6, 0xdd, 0x31, 0x55, 0x05, 0xdf, 0x1d, 0x40,
|
0x9c, 0x16, 0xdc, 0x7d, 0xa3, 0xc0, 0x59, 0xb2, 0xe1, 0x1e, 0xfb, 0xbc, 0x63, 0x32, 0x73, 0x7e,
|
||||||
0xed, 0xac, 0x92, 0x82, 0x2b, 0x86, 0x5e, 0x82, 0x5b, 0xed, 0x25, 0x1b, 0x38, 0xd7, 0xce, 0xf0,
|
0x1a, 0x80, 0xaa, 0x59, 0x29, 0x78, 0x22, 0x19, 0x1a, 0x83, 0x99, 0xed, 0x05, 0x6b, 0x1b, 0x1d,
|
||||||
0x32, 0x1a, 0xe2, 0x63, 0xa3, 0x61, 0xcb, 0x7f, 0xbf, 0x97, 0x2c, 0x36, 0x2c, 0x74, 0x03, 0xc8,
|
0xa3, 0xdb, 0xec, 0x63, 0xfc, 0xff, 0x06, 0xb1, 0x76, 0x79, 0xbb, 0x17, 0xcc, 0x53, 0x5a, 0xd4,
|
||||||
0x02, 0x12, 0x22, 0xf3, 0xe4, 0x2b, 0x2b, 0x55, 0x2e, 0xf8, 0xa0, 0x77, 0xed, 0x0c, 0x4f, 0x63,
|
0x03, 0xa4, 0x31, 0x9f, 0x88, 0xc8, 0xff, 0xc2, 0x52, 0x19, 0xf1, 0xa4, 0x7d, 0xde, 0x31, 0xba,
|
||||||
0xcf, 0x56, 0x5e, 0xc9, 0x7c, 0x61, 0xf3, 0xe8, 0x09, 0x5c, 0xd6, 0xe8, 0x06, 0x79, 0xcf, 0x20,
|
0x57, 0x9e, 0xa5, 0x2b, 0x2f, 0x44, 0xb4, 0xd4, 0x79, 0xf4, 0x00, 0x9a, 0x05, 0x5d, 0x92, 0x0d,
|
||||||
0x2f, 0x6c, 0xb6, 0x81, 0x21, 0x70, 0x39, 0x29, 0xd8, 0xc0, 0x35, 0x45, 0x13, 0x07, 0x8f, 0xe0,
|
0x45, 0xde, 0xea, 0x6c, 0x89, 0x21, 0x30, 0x13, 0x12, 0xb3, 0xb6, 0xa9, 0x8a, 0x2a, 0x76, 0xee,
|
||||||
0x6a, 0x2c, 0xf8, 0x3a, 0x4f, 0xe7, 0x34, 0x63, 0x05, 0x69, 0x96, 0xfa, 0x04, 0x0f, 0x7f, 0x4f,
|
0x43, 0x6b, 0xc2, 0x93, 0x4d, 0x14, 0x2c, 0x68, 0xc8, 0x62, 0x52, 0x8e, 0xf6, 0x01, 0xee, 0xfd,
|
||||||
0xd7, 0x5b, 0xdd, 0x81, 0xab, 0xfd, 0x30, 0x5b, 0x9d, 0x45, 0x37, 0xff, 0xdc, 0xca, 0xfa, 0x88,
|
0x9e, 0x2e, 0x66, 0x1b, 0x81, 0x99, 0x6f, 0x45, 0xcd, 0x76, 0xdd, 0xef, 0xfd, 0x73, 0x36, 0xbd,
|
||||||
0x6b, 0x1f, 0xf1, 0x5c, 0x32, 0x1a, 0x1b, 0x66, 0xf0, 0x02, 0xbc, 0x39, 0xab, 0xac, 0x78, 0xdd,
|
0x4d, 0x5c, 0x6c, 0x13, 0x2f, 0x04, 0xa3, 0x9e, 0x52, 0x3a, 0xcf, 0xc1, 0x5a, 0xb0, 0x4c, 0x9b,
|
||||||
0x4d, 0xcf, 0x5f, 0xa8, 0x54, 0x12, 0xfa, 0x25, 0xa1, 0xa6, 0x60, 0xf4, 0xcf, 0xe3, 0x8b, 0x3a,
|
0x17, 0xaf, 0xe5, 0xfd, 0xc7, 0x32, 0x10, 0x84, 0x7e, 0xf2, 0xa9, 0x2a, 0x28, 0xff, 0x1b, 0xef,
|
||||||
0x6b, 0xd1, 0xda, 0xfe, 0x16, 0xd5, 0x4e, 0xf4, 0xf4, 0x16, 0xe0, 0x97, 0x7b, 0xe8, 0x0c, 0xee,
|
0xb6, 0xc8, 0x6a, 0x3a, 0x3f, 0x42, 0x45, 0xaa, 0x3b, 0x7a, 0xf8, 0x08, 0xe0, 0xb8, 0x3d, 0x74,
|
||||||
0x7f, 0x98, 0xbd, 0x99, 0xbd, 0xfd, 0x38, 0xf3, 0x4e, 0x10, 0x40, 0xff, 0x75, 0x3c, 0x5d, 0x4c,
|
0x0d, 0x97, 0xef, 0xe6, 0xaf, 0xe6, 0xaf, 0xdf, 0xcf, 0xad, 0x33, 0x04, 0x70, 0xf1, 0xd2, 0x9b,
|
||||||
0x62, 0xcf, 0x31, 0xf1, 0x64, 0x31, 0x1d, 0x4f, 0xbc, 0x5e, 0xf4, 0xa3, 0x07, 0x30, 0x22, 0x8a,
|
0x2d, 0xa7, 0x9e, 0x75, 0xae, 0xe2, 0xe9, 0x72, 0x36, 0x99, 0x5a, 0x8d, 0xfe, 0x8f, 0x06, 0xc0,
|
||||||
0x59, 0x1e, 0xda, 0x36, 0x0a, 0xfa, 0x7e, 0x28, 0xec, 0x72, 0xa9, 0xd6, 0xfd, 0xfd, 0x67, 0xdd,
|
0x98, 0x48, 0xa6, 0x75, 0xe8, 0x5b, 0xe9, 0x90, 0x5f, 0x11, 0x0d, 0xea, 0xdf, 0xab, 0xf2, 0x2f,
|
||||||
0x09, 0x76, 0xe4, 0xe0, 0x04, 0x7d, 0x83, 0xf3, 0xb6, 0xbd, 0xe8, 0xf6, 0xb8, 0xc6, 0x5f, 0x2e,
|
0xd8, 0x4f, 0x4f, 0x95, 0xe9, 0xf6, 0x9d, 0x33, 0xf4, 0xdd, 0x80, 0x9b, 0xea, 0xae, 0xd1, 0xb3,
|
||||||
0xe4, 0x47, 0xff, 0x43, 0x39, 0x34, 0xe6, 0x70, 0x7a, 0xb0, 0x10, 0xe1, 0xe3, 0x12, 0x7f, 0x9e,
|
0x3a, 0x56, 0x7f, 0x39, 0x9a, 0x3d, 0x3c, 0x5d, 0x78, 0xe8, 0xe2, 0x2b, 0x5c, 0x1d, 0x76, 0x8b,
|
||||||
0xc9, 0x0f, 0x3b, 0xe3, 0x9b, 0x7e, 0xa3, 0xfe, 0x67, 0x57, 0xd7, 0x96, 0x7d, 0xf3, 0x80, 0x9e,
|
0x9e, 0xd4, 0x31, 0xfa, 0xf3, 0x8a, 0xf6, 0xe0, 0x44, 0x55, 0xf9, 0xf6, 0xf8, 0xf2, 0xe3, 0x1d,
|
||||||
0xff, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x66, 0xa2, 0xc0, 0xe2, 0xd6, 0x03, 0x00, 0x00,
|
0x55, 0x5c, 0x5d, 0xa8, 0xcf, 0xe3, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x0b, 0x29, 0x9a, 0xb0,
|
||||||
|
0x0e, 0x04, 0x00, 0x00,
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
package hashicorp.nomad.plugins.base;
|
package hashicorp.nomad.plugins.base.proto;
|
||||||
option go_package = "base";
|
option go_package = "proto";
|
||||||
|
|
||||||
import "github.com/hashicorp/nomad/plugins/shared/hclspec/hcl_spec.proto";
|
import "github.com/hashicorp/nomad/plugins/shared/hclspec/hcl_spec.proto";
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ service BasePlugin {
|
||||||
// PluginType enumerates the type of plugins Nomad supports
|
// PluginType enumerates the type of plugins Nomad supports
|
||||||
enum PluginType {
|
enum PluginType {
|
||||||
UNKNOWN = 0;
|
UNKNOWN = 0;
|
||||||
DRIVER = 1;
|
DRIVER = 2;
|
||||||
DEVICE = 2;
|
DEVICE = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PluginInfoRequest is used to request the plugins basic information.
|
// PluginInfoRequest is used to request the plugins basic information.
|
|
@ -0,0 +1,64 @@
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
plugin "github.com/hashicorp/go-plugin"
|
||||||
|
"github.com/hashicorp/nomad/plugins/base/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
// basePluginServer wraps a base plugin and exposes it via gRPC.
|
||||||
|
type basePluginServer struct {
|
||||||
|
broker *plugin.GRPCBroker
|
||||||
|
impl BasePlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basePluginServer) PluginInfo(context.Context, *proto.PluginInfoRequest) (*proto.PluginInfoResponse, error) {
|
||||||
|
resp, err := b.impl.PluginInfo()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ptype proto.PluginType
|
||||||
|
switch resp.Type {
|
||||||
|
case PluginTypeDriver:
|
||||||
|
ptype = proto.PluginType_DRIVER
|
||||||
|
case PluginTypeDevice:
|
||||||
|
ptype = proto.PluginType_DEVICE
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("plugin is of unknown type: %q", resp.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
presp := &proto.PluginInfoResponse{
|
||||||
|
Type: ptype,
|
||||||
|
PluginApiVersion: resp.PluginApiVersion,
|
||||||
|
PluginVersion: resp.PluginVersion,
|
||||||
|
Name: resp.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
return presp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basePluginServer) ConfigSchema(context.Context, *proto.ConfigSchemaRequest) (*proto.ConfigSchemaResponse, error) {
|
||||||
|
spec, err := b.impl.ConfigSchema()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
presp := &proto.ConfigSchemaResponse{
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
return presp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basePluginServer) SetConfig(ctx context.Context, req *proto.SetConfigRequest) (*proto.SetConfigResponse, error) {
|
||||||
|
// Set the config
|
||||||
|
if err := b.impl.SetConfig(req.GetMsgpackConfig()); err != nil {
|
||||||
|
return nil, fmt.Errorf("SetConfig failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &proto.SetConfigResponse{}, nil
|
||||||
|
}
|
|
@ -82,8 +82,6 @@ func decodeSpecBlock(spec *Spec, impliedName string) (hcldec.Spec, hcl.Diagnosti
|
||||||
})
|
})
|
||||||
return nil, diags
|
return nil, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeObjectSpec(obj *Object) (hcldec.Spec, hcl.Diagnostics) {
|
func decodeObjectSpec(obj *Object) (hcldec.Spec, hcl.Diagnostics) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ func TestDec_Convert_Object(t *testing.T) {
|
||||||
Block: &Spec_Object{
|
Block: &Spec_Object{
|
||||||
&Object{
|
&Object{
|
||||||
Attributes: map[string]*Spec{
|
Attributes: map[string]*Spec{
|
||||||
"foo": &Spec{
|
"foo": {
|
||||||
Block: &Spec_Attr{
|
Block: &Spec_Attr{
|
||||||
&Attr{
|
&Attr{
|
||||||
Type: "string",
|
Type: "string",
|
||||||
|
@ -54,7 +54,7 @@ func TestDec_Convert_Object(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"bar": &Spec{
|
"bar": {
|
||||||
Block: &Spec_Attr{
|
Block: &Spec_Attr{
|
||||||
&Attr{
|
&Attr{
|
||||||
Type: "number",
|
Type: "number",
|
||||||
|
@ -62,7 +62,7 @@ func TestDec_Convert_Object(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"baz": &Spec{
|
"baz": {
|
||||||
Block: &Spec_Attr{
|
Block: &Spec_Attr{
|
||||||
&Attr{
|
&Attr{
|
||||||
Type: "bool",
|
Type: "bool",
|
||||||
|
@ -106,7 +106,7 @@ func TestDec_Convert_Array(t *testing.T) {
|
||||||
Block: &Spec_Array{
|
Block: &Spec_Array{
|
||||||
Array: &Array{
|
Array: &Array{
|
||||||
Values: []*Spec{
|
Values: []*Spec{
|
||||||
&Spec{
|
{
|
||||||
Block: &Spec_Attr{
|
Block: &Spec_Attr{
|
||||||
&Attr{
|
&Attr{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
@ -115,7 +115,7 @@ func TestDec_Convert_Array(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&Spec{
|
{
|
||||||
Block: &Spec_Attr{
|
Block: &Spec_Attr{
|
||||||
&Attr{
|
&Attr{
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
## v3.3
|
||||||
|
|
||||||
|
- `msgpack:",inline"` tag is restored to force inlining structs.
|
||||||
|
|
||||||
|
## v3.2
|
||||||
|
|
||||||
|
- Decoding extension types returns pointer to the value instead of the value. Fixes #153
|
||||||
|
|
||||||
|
## v3
|
||||||
|
|
||||||
|
- gopkg.in is not supported any more. Update import path to github.com/vmihailenco/msgpack.
|
||||||
|
- Msgpack maps are decoded into map[string]interface{} by default.
|
||||||
|
- EncodeSliceLen is removed in favor of EncodeArrayLen. DecodeSliceLen is removed in favor of DecodeArrayLen.
|
||||||
|
- Embedded structs are automatically inlined where possible.
|
||||||
|
- Time is encoded using extension as described in https://github.com/msgpack/msgpack/pull/209. Old format is supported as well.
|
||||||
|
- EncodeInt8/16/32/64 is replaced with EncodeInt. EncodeUint8/16/32/64 is replaced with EncodeUint. There should be no performance differences.
|
||||||
|
- DecodeInterface can now return int8/16/32 and uint8/16/32.
|
||||||
|
- PeekCode returns codes.Code instead of byte.
|
|
@ -0,0 +1,25 @@
|
||||||
|
Copyright (c) 2013 The github.com/vmihailenco/msgpack 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,5 @@
|
||||||
|
all:
|
||||||
|
go test ./...
|
||||||
|
env GOOS=linux GOARCH=386 go test ./...
|
||||||
|
go test ./... -short -race
|
||||||
|
go vet
|
|
@ -0,0 +1,69 @@
|
||||||
|
# MessagePack encoding for Golang
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/vmihailenco/msgpack.svg?branch=v2)](https://travis-ci.org/vmihailenco/msgpack)
|
||||||
|
[![GoDoc](https://godoc.org/github.com/vmihailenco/msgpack?status.svg)](https://godoc.org/github.com/vmihailenco/msgpack)
|
||||||
|
|
||||||
|
Supports:
|
||||||
|
- Primitives, arrays, maps, structs, time.Time and interface{}.
|
||||||
|
- Appengine *datastore.Key and datastore.Cursor.
|
||||||
|
- [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.
|
||||||
|
- Renaming fields via `msgpack:"my_field_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).
|
||||||
|
- [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).
|
||||||
|
- [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).
|
||||||
|
|
||||||
|
API docs: https://godoc.org/github.com/vmihailenco/msgpack.
|
||||||
|
Examples: https://godoc.org/github.com/vmihailenco/msgpack#pkg-examples.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Install:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
go get -u github.com/vmihailenco/msgpack
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ExampleMarshal() {
|
||||||
|
type Item struct {
|
||||||
|
Foo string
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := msgpack.Marshal(&Item{Foo: "bar"})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var item Item
|
||||||
|
err = msgpack.Unmarshal(b, &item)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(item.Foo)
|
||||||
|
// Output: bar
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Benchmark
|
||||||
|
|
||||||
|
```
|
||||||
|
BenchmarkStructVmihailencoMsgpack-4 200000 12814 ns/op 2128 B/op 26 allocs/op
|
||||||
|
BenchmarkStructUgorjiGoMsgpack-4 100000 17678 ns/op 3616 B/op 70 allocs/op
|
||||||
|
BenchmarkStructUgorjiGoCodec-4 100000 19053 ns/op 7346 B/op 23 allocs/op
|
||||||
|
BenchmarkStructJSON-4 20000 69438 ns/op 7864 B/op 26 allocs/op
|
||||||
|
BenchmarkStructGOB-4 10000 104331 ns/op 14664 B/op 278 allocs/op
|
||||||
|
```
|
||||||
|
|
||||||
|
## Howto
|
||||||
|
|
||||||
|
Please go through [examples](https://godoc.org/github.com/vmihailenco/msgpack#pkg-examples) to get an idea how to use this package.
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [Golang PostgreSQL ORM](https://github.com/go-pg/pg)
|
||||||
|
- [Golang message task queue](https://github.com/go-msgqueue/msgqueue)
|
|
@ -0,0 +1,86 @@
|
||||||
|
package codes
|
||||||
|
|
||||||
|
type Code byte
|
||||||
|
|
||||||
|
var (
|
||||||
|
PosFixedNumHigh Code = 0x7f
|
||||||
|
NegFixedNumLow Code = 0xe0
|
||||||
|
|
||||||
|
Nil Code = 0xc0
|
||||||
|
|
||||||
|
False Code = 0xc2
|
||||||
|
True Code = 0xc3
|
||||||
|
|
||||||
|
Float Code = 0xca
|
||||||
|
Double Code = 0xcb
|
||||||
|
|
||||||
|
Uint8 Code = 0xcc
|
||||||
|
Uint16 Code = 0xcd
|
||||||
|
Uint32 Code = 0xce
|
||||||
|
Uint64 Code = 0xcf
|
||||||
|
|
||||||
|
Int8 Code = 0xd0
|
||||||
|
Int16 Code = 0xd1
|
||||||
|
Int32 Code = 0xd2
|
||||||
|
Int64 Code = 0xd3
|
||||||
|
|
||||||
|
FixedStrLow Code = 0xa0
|
||||||
|
FixedStrHigh Code = 0xbf
|
||||||
|
FixedStrMask Code = 0x1f
|
||||||
|
Str8 Code = 0xd9
|
||||||
|
Str16 Code = 0xda
|
||||||
|
Str32 Code = 0xdb
|
||||||
|
|
||||||
|
Bin8 Code = 0xc4
|
||||||
|
Bin16 Code = 0xc5
|
||||||
|
Bin32 Code = 0xc6
|
||||||
|
|
||||||
|
FixedArrayLow Code = 0x90
|
||||||
|
FixedArrayHigh Code = 0x9f
|
||||||
|
FixedArrayMask Code = 0xf
|
||||||
|
Array16 Code = 0xdc
|
||||||
|
Array32 Code = 0xdd
|
||||||
|
|
||||||
|
FixedMapLow Code = 0x80
|
||||||
|
FixedMapHigh Code = 0x8f
|
||||||
|
FixedMapMask Code = 0xf
|
||||||
|
Map16 Code = 0xde
|
||||||
|
Map32 Code = 0xdf
|
||||||
|
|
||||||
|
FixExt1 Code = 0xd4
|
||||||
|
FixExt2 Code = 0xd5
|
||||||
|
FixExt4 Code = 0xd6
|
||||||
|
FixExt8 Code = 0xd7
|
||||||
|
FixExt16 Code = 0xd8
|
||||||
|
Ext8 Code = 0xc7
|
||||||
|
Ext16 Code = 0xc8
|
||||||
|
Ext32 Code = 0xc9
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsFixedNum(c Code) bool {
|
||||||
|
return c <= PosFixedNumHigh || c >= NegFixedNumLow
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsFixedMap(c Code) bool {
|
||||||
|
return c >= FixedMapLow && c <= FixedMapHigh
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsFixedArray(c Code) bool {
|
||||||
|
return c >= FixedArrayLow && c <= FixedArrayHigh
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsFixedString(c Code) bool {
|
||||||
|
return c >= FixedStrLow && c <= FixedStrHigh
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsString(c Code) bool {
|
||||||
|
return IsFixedString(c) || c == Str8 || c == Str16 || c == Str32
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsFixedExt(c Code) bool {
|
||||||
|
return c >= FixExt1 && c <= FixExt16
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsExt(c Code) bool {
|
||||||
|
return IsFixedExt(c) || c == Ext8 || c == Ext16 || c == Ext32
|
||||||
|
}
|
|
@ -0,0 +1,536 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
const bytesAllocLimit = 1024 * 1024 // 1mb
|
||||||
|
|
||||||
|
type bufReader interface {
|
||||||
|
io.Reader
|
||||||
|
io.ByteScanner
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBufReader(r io.Reader) bufReader {
|
||||||
|
if br, ok := r.(bufReader); ok {
|
||||||
|
return br
|
||||||
|
}
|
||||||
|
return bufio.NewReader(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeBuffer() []byte {
|
||||||
|
return make([]byte, 0, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal decodes the MessagePack-encoded data and stores the result
|
||||||
|
// in the value pointed to by v.
|
||||||
|
func Unmarshal(data []byte, v ...interface{}) error {
|
||||||
|
return NewDecoder(bytes.NewReader(data)).Decode(v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Decoder struct {
|
||||||
|
r io.Reader
|
||||||
|
s io.ByteScanner
|
||||||
|
buf []byte
|
||||||
|
|
||||||
|
extLen int
|
||||||
|
rec []byte // accumulates read data if not nil
|
||||||
|
|
||||||
|
useLoose bool
|
||||||
|
useJSONTag bool
|
||||||
|
|
||||||
|
decodeMapFunc func(*Decoder) (interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder returns a new decoder that reads from r.
|
||||||
|
//
|
||||||
|
// The decoder introduces its own buffering and may read data from r
|
||||||
|
// beyond the MessagePack values requested. Buffering can be disabled
|
||||||
|
// by passing a reader that implements io.ByteScanner interface.
|
||||||
|
func NewDecoder(r io.Reader) *Decoder {
|
||||||
|
d := &Decoder{
|
||||||
|
decodeMapFunc: decodeMap,
|
||||||
|
|
||||||
|
buf: makeBuffer(),
|
||||||
|
}
|
||||||
|
d.resetReader(r)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) SetDecodeMapFunc(fn func(*Decoder) (interface{}, error)) {
|
||||||
|
d.decodeMapFunc = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseDecodeInterfaceLoose causes decoder to use DecodeInterfaceLoose
|
||||||
|
// to decode msgpack value into Go interface{}.
|
||||||
|
func (d *Decoder) UseDecodeInterfaceLoose(flag bool) {
|
||||||
|
d.useLoose = flag
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseJSONTag causes the Decoder to use json struct tag as fallback option
|
||||||
|
// if there is no msgpack tag.
|
||||||
|
func (d *Decoder) UseJSONTag(v bool) *Decoder {
|
||||||
|
d.useJSONTag = v
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) Reset(r io.Reader) error {
|
||||||
|
d.resetReader(r)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) resetReader(r io.Reader) {
|
||||||
|
reader := newBufReader(r)
|
||||||
|
d.r = reader
|
||||||
|
d.s = reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) Decode(v ...interface{}) error {
|
||||||
|
for _, vv := range v {
|
||||||
|
if err := d.decode(vv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decode(dst interface{}) error {
|
||||||
|
var err error
|
||||||
|
switch v := dst.(type) {
|
||||||
|
case *string:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeString()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *[]byte:
|
||||||
|
if v != nil {
|
||||||
|
return d.decodeBytesPtr(v)
|
||||||
|
}
|
||||||
|
case *int:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeInt()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *int8:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeInt8()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *int16:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeInt16()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *int32:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeInt32()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *int64:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeInt64()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *uint:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeUint()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *uint8:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeUint8()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *uint16:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeUint16()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *uint32:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeUint32()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *uint64:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeUint64()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *bool:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeBool()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *float32:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeFloat32()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *float64:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeFloat64()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *[]string:
|
||||||
|
return d.decodeStringSlicePtr(v)
|
||||||
|
case *map[string]string:
|
||||||
|
return d.decodeMapStringStringPtr(v)
|
||||||
|
case *map[string]interface{}:
|
||||||
|
return d.decodeMapStringInterfacePtr(v)
|
||||||
|
case *time.Duration:
|
||||||
|
if v != nil {
|
||||||
|
vv, err := d.DecodeInt64()
|
||||||
|
*v = time.Duration(vv)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *time.Time:
|
||||||
|
if v != nil {
|
||||||
|
*v, err = d.DecodeTime()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.ValueOf(dst)
|
||||||
|
if !v.IsValid() {
|
||||||
|
return errors.New("msgpack: Decode(nil)")
|
||||||
|
}
|
||||||
|
if v.Kind() != reflect.Ptr {
|
||||||
|
return fmt.Errorf("msgpack: Decode(nonsettable %T)", dst)
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
if !v.IsValid() {
|
||||||
|
return fmt.Errorf("msgpack: Decode(nonsettable %T)", dst)
|
||||||
|
}
|
||||||
|
return d.DecodeValue(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeInterface() (interface{}, error) {
|
||||||
|
if d.useLoose {
|
||||||
|
return d.DecodeInterfaceLoose()
|
||||||
|
}
|
||||||
|
return d.DecodeInterface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeValue(v reflect.Value) error {
|
||||||
|
decode := getDecoder(v.Type())
|
||||||
|
return decode(d, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeNil() error {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c != codes.Nil {
|
||||||
|
return fmt.Errorf("msgpack: invalid code=%x decoding nil", c)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeNilValue(v reflect.Value) error {
|
||||||
|
err := d.DecodeNil()
|
||||||
|
if v.IsNil() {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
v.Set(reflect.Zero(v.Type()))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeBool() (bool, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return d.bool(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) bool(c codes.Code) (bool, error) {
|
||||||
|
if c == codes.False {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if c == codes.True {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("msgpack: invalid code=%x decoding bool", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeInterface decodes value into interface. It returns following types:
|
||||||
|
// - nil,
|
||||||
|
// - bool,
|
||||||
|
// - int8, int16, int32, int64,
|
||||||
|
// - uint8, uint16, uint32, uint64,
|
||||||
|
// - float32 and float64,
|
||||||
|
// - string,
|
||||||
|
// - []byte,
|
||||||
|
// - slices of any of the above,
|
||||||
|
// - maps of any of the above.
|
||||||
|
//
|
||||||
|
// DecodeInterface should be used only when you don't know the type of value
|
||||||
|
// you are decoding. For example, if you are decoding number it is better to use
|
||||||
|
// DecodeInt64 for negative numbers and DecodeUint64 for positive numbers.
|
||||||
|
func (d *Decoder) DecodeInterface() (interface{}, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if codes.IsFixedNum(c) {
|
||||||
|
return int8(c), nil
|
||||||
|
}
|
||||||
|
if codes.IsFixedMap(c) {
|
||||||
|
_ = d.s.UnreadByte()
|
||||||
|
return d.DecodeMap()
|
||||||
|
}
|
||||||
|
if codes.IsFixedArray(c) {
|
||||||
|
return d.decodeSlice(c)
|
||||||
|
}
|
||||||
|
if codes.IsFixedString(c) {
|
||||||
|
return d.string(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c {
|
||||||
|
case codes.Nil:
|
||||||
|
return nil, nil
|
||||||
|
case codes.False, codes.True:
|
||||||
|
return d.bool(c)
|
||||||
|
case codes.Float:
|
||||||
|
return d.float32(c)
|
||||||
|
case codes.Double:
|
||||||
|
return d.float64(c)
|
||||||
|
case codes.Uint8:
|
||||||
|
return d.uint8()
|
||||||
|
case codes.Uint16:
|
||||||
|
return d.uint16()
|
||||||
|
case codes.Uint32:
|
||||||
|
return d.uint32()
|
||||||
|
case codes.Uint64:
|
||||||
|
return d.uint64()
|
||||||
|
case codes.Int8:
|
||||||
|
return d.int8()
|
||||||
|
case codes.Int16:
|
||||||
|
return d.int16()
|
||||||
|
case codes.Int32:
|
||||||
|
return d.int32()
|
||||||
|
case codes.Int64:
|
||||||
|
return d.int64()
|
||||||
|
case codes.Bin8, codes.Bin16, codes.Bin32:
|
||||||
|
return d.bytes(c, nil)
|
||||||
|
case codes.Str8, codes.Str16, codes.Str32:
|
||||||
|
return d.string(c)
|
||||||
|
case codes.Array16, codes.Array32:
|
||||||
|
return d.decodeSlice(c)
|
||||||
|
case codes.Map16, codes.Map32:
|
||||||
|
d.s.UnreadByte()
|
||||||
|
return d.DecodeMap()
|
||||||
|
case codes.FixExt1, codes.FixExt2, codes.FixExt4, codes.FixExt8, codes.FixExt16,
|
||||||
|
codes.Ext8, codes.Ext16, codes.Ext32:
|
||||||
|
return d.extInterface(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("msgpack: unknown code %x decoding interface{}", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeInterfaceLoose is like DecodeInterface except that:
|
||||||
|
// - int8, int16, and int32 are converted to int64,
|
||||||
|
// - uint8, uint16, and uint32 are converted to uint64,
|
||||||
|
// - float32 is converted to float64.
|
||||||
|
func (d *Decoder) DecodeInterfaceLoose() (interface{}, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if codes.IsFixedNum(c) {
|
||||||
|
return int64(c), nil
|
||||||
|
}
|
||||||
|
if codes.IsFixedMap(c) {
|
||||||
|
d.s.UnreadByte()
|
||||||
|
return d.DecodeMap()
|
||||||
|
}
|
||||||
|
if codes.IsFixedArray(c) {
|
||||||
|
return d.decodeSlice(c)
|
||||||
|
}
|
||||||
|
if codes.IsFixedString(c) {
|
||||||
|
return d.string(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c {
|
||||||
|
case codes.Nil:
|
||||||
|
return nil, nil
|
||||||
|
case codes.False, codes.True:
|
||||||
|
return d.bool(c)
|
||||||
|
case codes.Float, codes.Double:
|
||||||
|
return d.float64(c)
|
||||||
|
case codes.Uint8, codes.Uint16, codes.Uint32, codes.Uint64:
|
||||||
|
return d.uint(c)
|
||||||
|
case codes.Int8, codes.Int16, codes.Int32, codes.Int64:
|
||||||
|
return d.int(c)
|
||||||
|
case codes.Bin8, codes.Bin16, codes.Bin32:
|
||||||
|
return d.bytes(c, nil)
|
||||||
|
case codes.Str8, codes.Str16, codes.Str32:
|
||||||
|
return d.string(c)
|
||||||
|
case codes.Array16, codes.Array32:
|
||||||
|
return d.decodeSlice(c)
|
||||||
|
case codes.Map16, codes.Map32:
|
||||||
|
d.s.UnreadByte()
|
||||||
|
return d.DecodeMap()
|
||||||
|
case codes.FixExt1, codes.FixExt2, codes.FixExt4, codes.FixExt8, codes.FixExt16,
|
||||||
|
codes.Ext8, codes.Ext16, codes.Ext32:
|
||||||
|
return d.extInterface(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("msgpack: unknown code %x decoding interface{}", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip skips next value.
|
||||||
|
func (d *Decoder) Skip() error {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if codes.IsFixedNum(c) {
|
||||||
|
return nil
|
||||||
|
} else if codes.IsFixedMap(c) {
|
||||||
|
return d.skipMap(c)
|
||||||
|
} else if codes.IsFixedArray(c) {
|
||||||
|
return d.skipSlice(c)
|
||||||
|
} else if codes.IsFixedString(c) {
|
||||||
|
return d.skipBytes(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c {
|
||||||
|
case codes.Nil, codes.False, codes.True:
|
||||||
|
return nil
|
||||||
|
case codes.Uint8, codes.Int8:
|
||||||
|
return d.skipN(1)
|
||||||
|
case codes.Uint16, codes.Int16:
|
||||||
|
return d.skipN(2)
|
||||||
|
case codes.Uint32, codes.Int32, codes.Float:
|
||||||
|
return d.skipN(4)
|
||||||
|
case codes.Uint64, codes.Int64, codes.Double:
|
||||||
|
return d.skipN(8)
|
||||||
|
case codes.Bin8, codes.Bin16, codes.Bin32:
|
||||||
|
return d.skipBytes(c)
|
||||||
|
case codes.Str8, codes.Str16, codes.Str32:
|
||||||
|
return d.skipBytes(c)
|
||||||
|
case codes.Array16, codes.Array32:
|
||||||
|
return d.skipSlice(c)
|
||||||
|
case codes.Map16, codes.Map32:
|
||||||
|
return d.skipMap(c)
|
||||||
|
case codes.FixExt1, codes.FixExt2, codes.FixExt4, codes.FixExt8, codes.FixExt16,
|
||||||
|
codes.Ext8, codes.Ext16, codes.Ext32:
|
||||||
|
return d.skipExt(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("msgpack: unknown code %x", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeekCode returns the next MessagePack code without advancing the reader.
|
||||||
|
// Subpackage msgpack/codes contains list of available codes.
|
||||||
|
func (d *Decoder) PeekCode() (codes.Code, error) {
|
||||||
|
c, err := d.s.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return codes.Code(c), d.s.UnreadByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) hasNilCode() bool {
|
||||||
|
code, err := d.PeekCode()
|
||||||
|
return err == nil && code == codes.Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) readCode() (codes.Code, error) {
|
||||||
|
d.extLen = 0
|
||||||
|
c, err := d.s.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if d.rec != nil {
|
||||||
|
d.rec = append(d.rec, c)
|
||||||
|
}
|
||||||
|
return codes.Code(c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) readFull(b []byte) error {
|
||||||
|
_, err := io.ReadFull(d.r, b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if d.rec != nil {
|
||||||
|
d.rec = append(d.rec, b...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) readN(n int) ([]byte, error) {
|
||||||
|
buf, err := readN(d.r, d.buf, n)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
d.buf = buf
|
||||||
|
if d.rec != nil {
|
||||||
|
d.rec = append(d.rec, buf...)
|
||||||
|
}
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readN(r io.Reader, b []byte, n int) ([]byte, error) {
|
||||||
|
if b == nil {
|
||||||
|
if n == 0 {
|
||||||
|
return make([]byte, 0), nil
|
||||||
|
}
|
||||||
|
if n <= bytesAllocLimit {
|
||||||
|
b = make([]byte, n)
|
||||||
|
} else {
|
||||||
|
b = make([]byte, bytesAllocLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if n <= cap(b) {
|
||||||
|
b = b[:n]
|
||||||
|
_, err := io.ReadFull(r, b)
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
b = b[:cap(b)]
|
||||||
|
|
||||||
|
var pos int
|
||||||
|
for {
|
||||||
|
alloc := n - len(b)
|
||||||
|
if alloc > bytesAllocLimit {
|
||||||
|
alloc = bytesAllocLimit
|
||||||
|
}
|
||||||
|
b = append(b, make([]byte, alloc)...)
|
||||||
|
|
||||||
|
_, err := io.ReadFull(r, b[pos:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(b) == n {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
pos = len(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a <= b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
|
@ -0,0 +1,289 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
const mapElemsAllocLimit = 1e4
|
||||||
|
|
||||||
|
var mapStringStringPtrType = reflect.TypeOf((*map[string]string)(nil))
|
||||||
|
var mapStringStringType = mapStringStringPtrType.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 {
|
||||||
|
n, err := d.DecodeMapLen()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := v.Type()
|
||||||
|
if n == -1 {
|
||||||
|
v.Set(reflect.Zero(typ))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.IsNil() {
|
||||||
|
v.Set(reflect.MakeMap(typ))
|
||||||
|
}
|
||||||
|
keyType := typ.Key()
|
||||||
|
valueType := typ.Elem()
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
mk := reflect.New(keyType).Elem()
|
||||||
|
if err := d.DecodeValue(mk); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mv := reflect.New(valueType).Elem()
|
||||||
|
if err := d.DecodeValue(mv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
v.SetMapIndex(mk, mv)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMap(d *Decoder) (interface{}, error) {
|
||||||
|
n, err := d.DecodeMapLen()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m := make(map[string]interface{}, min(n, mapElemsAllocLimit))
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
mk, err := d.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mv, err := d.decodeInterface()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m[mk] = mv
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeMapLen() (int, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if codes.IsExt(c) {
|
||||||
|
if err = d.skipExtHeader(c); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d.mapLen(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) mapLen(c codes.Code) (int, error) {
|
||||||
|
n, err := d._mapLen(c)
|
||||||
|
err = expandInvalidCodeMapLenError(c, err)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) _mapLen(c codes.Code) (int, error) {
|
||||||
|
if c == codes.Nil {
|
||||||
|
return -1, nil
|
||||||
|
}
|
||||||
|
if c >= codes.FixedMapLow && c <= codes.FixedMapHigh {
|
||||||
|
return int(c & codes.FixedMapMask), nil
|
||||||
|
}
|
||||||
|
if c == codes.Map16 {
|
||||||
|
n, err := d.uint16()
|
||||||
|
return int(n), err
|
||||||
|
}
|
||||||
|
if c == codes.Map32 {
|
||||||
|
n, err := d.uint32()
|
||||||
|
return int(n), err
|
||||||
|
}
|
||||||
|
return 0, errInvalidCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandInvalidCodeMapLenError(c codes.Code, err error) error {
|
||||||
|
if err == errInvalidCode {
|
||||||
|
return fmt.Errorf("msgpack: invalid code=%x decoding map length", c)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMapStringStringValue(d *Decoder, v reflect.Value) error {
|
||||||
|
mptr := v.Addr().Convert(mapStringStringPtrType).Interface().(*map[string]string)
|
||||||
|
return d.decodeMapStringStringPtr(mptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeMapStringStringPtr(ptr *map[string]string) error {
|
||||||
|
n, err := d.DecodeMapLen()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
*ptr = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m := *ptr
|
||||||
|
if m == nil {
|
||||||
|
*ptr = make(map[string]string, min(n, mapElemsAllocLimit))
|
||||||
|
m = *ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
mk, err := d.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mv, err := d.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m[mk] = mv
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMapStringInterfaceValue(d *Decoder, v reflect.Value) error {
|
||||||
|
ptr := v.Addr().Convert(mapStringInterfacePtrType).Interface().(*map[string]interface{})
|
||||||
|
return d.decodeMapStringInterfacePtr(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeMapStringInterfacePtr(ptr *map[string]interface{}) error {
|
||||||
|
n, err := d.DecodeMapLen()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
*ptr = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m := *ptr
|
||||||
|
if m == nil {
|
||||||
|
*ptr = make(map[string]interface{}, min(n, mapElemsAllocLimit))
|
||||||
|
m = *ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
mk, err := d.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mv, err := d.decodeInterface()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m[mk] = mv
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeMap() (interface{}, error) {
|
||||||
|
return d.decodeMapFunc(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) skipMap(c codes.Code) error {
|
||||||
|
n, err := d.mapLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if err := d.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := d.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeStructValue(d *Decoder, v reflect.Value) error {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var isArray bool
|
||||||
|
|
||||||
|
n, err := d._mapLen(c)
|
||||||
|
if err != nil {
|
||||||
|
var err2 error
|
||||||
|
n, err2 = d.arrayLen(c)
|
||||||
|
if err2 != nil {
|
||||||
|
return expandInvalidCodeMapLenError(c, err)
|
||||||
|
}
|
||||||
|
isArray = true
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
if err = mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.Set(reflect.Zero(v.Type()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var fields *fields
|
||||||
|
if d.useJSONTag {
|
||||||
|
fields = jsonStructs.Fields(v.Type())
|
||||||
|
} else {
|
||||||
|
fields = structs.Fields(v.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
if isArray {
|
||||||
|
for i, f := range fields.List {
|
||||||
|
if i >= n {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err := f.DecodeValue(d, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Skip extra values.
|
||||||
|
for i := len(fields.List); i < n; i++ {
|
||||||
|
if err := d.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
name, err := d.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f := fields.Table[name]; f != nil {
|
||||||
|
if err := f.DecodeValue(d, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := d.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,307 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Decoder) skipN(n int) error {
|
||||||
|
_, err := d.readN(n)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) uint8() (uint8, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint8(c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) int8() (int8, error) {
|
||||||
|
n, err := d.uint8()
|
||||||
|
return int8(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) uint16() (uint16, error) {
|
||||||
|
b, err := d.readN(2)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return (uint16(b[0]) << 8) | uint16(b[1]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) int16() (int16, error) {
|
||||||
|
n, err := d.uint16()
|
||||||
|
return int16(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) uint32() (uint32, error) {
|
||||||
|
b, err := d.readN(4)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
n := (uint32(b[0]) << 24) |
|
||||||
|
(uint32(b[1]) << 16) |
|
||||||
|
(uint32(b[2]) << 8) |
|
||||||
|
uint32(b[3])
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) int32() (int32, error) {
|
||||||
|
n, err := d.uint32()
|
||||||
|
return int32(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) uint64() (uint64, error) {
|
||||||
|
b, err := d.readN(8)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
n := (uint64(b[0]) << 56) |
|
||||||
|
(uint64(b[1]) << 48) |
|
||||||
|
(uint64(b[2]) << 40) |
|
||||||
|
(uint64(b[3]) << 32) |
|
||||||
|
(uint64(b[4]) << 24) |
|
||||||
|
(uint64(b[5]) << 16) |
|
||||||
|
(uint64(b[6]) << 8) |
|
||||||
|
uint64(b[7])
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) int64() (int64, error) {
|
||||||
|
n, err := d.uint64()
|
||||||
|
return int64(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeUint64 decodes msgpack int8/16/32/64 and uint8/16/32/64
|
||||||
|
// into Go uint64.
|
||||||
|
func (d *Decoder) DecodeUint64() (uint64, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return d.uint(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) uint(c codes.Code) (uint64, error) {
|
||||||
|
if c == codes.Nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
if codes.IsFixedNum(c) {
|
||||||
|
return uint64(int8(c)), nil
|
||||||
|
}
|
||||||
|
switch c {
|
||||||
|
case codes.Uint8:
|
||||||
|
n, err := d.uint8()
|
||||||
|
return uint64(n), err
|
||||||
|
case codes.Int8:
|
||||||
|
n, err := d.int8()
|
||||||
|
return uint64(n), err
|
||||||
|
case codes.Uint16:
|
||||||
|
n, err := d.uint16()
|
||||||
|
return uint64(n), err
|
||||||
|
case codes.Int16:
|
||||||
|
n, err := d.int16()
|
||||||
|
return uint64(n), err
|
||||||
|
case codes.Uint32:
|
||||||
|
n, err := d.uint32()
|
||||||
|
return uint64(n), err
|
||||||
|
case codes.Int32:
|
||||||
|
n, err := d.int32()
|
||||||
|
return uint64(n), err
|
||||||
|
case codes.Uint64, codes.Int64:
|
||||||
|
return d.uint64()
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("msgpack: invalid code=%x decoding uint64", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeInt64 decodes msgpack int8/16/32/64 and uint8/16/32/64
|
||||||
|
// into Go int64.
|
||||||
|
func (d *Decoder) DecodeInt64() (int64, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return d.int(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) int(c codes.Code) (int64, error) {
|
||||||
|
if c == codes.Nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
if codes.IsFixedNum(c) {
|
||||||
|
return int64(int8(c)), nil
|
||||||
|
}
|
||||||
|
switch c {
|
||||||
|
case codes.Uint8:
|
||||||
|
n, err := d.uint8()
|
||||||
|
return int64(n), err
|
||||||
|
case codes.Int8:
|
||||||
|
n, err := d.uint8()
|
||||||
|
return int64(int8(n)), err
|
||||||
|
case codes.Uint16:
|
||||||
|
n, err := d.uint16()
|
||||||
|
return int64(n), err
|
||||||
|
case codes.Int16:
|
||||||
|
n, err := d.uint16()
|
||||||
|
return int64(int16(n)), err
|
||||||
|
case codes.Uint32:
|
||||||
|
n, err := d.uint32()
|
||||||
|
return int64(n), err
|
||||||
|
case codes.Int32:
|
||||||
|
n, err := d.uint32()
|
||||||
|
return int64(int32(n)), err
|
||||||
|
case codes.Uint64, codes.Int64:
|
||||||
|
n, err := d.uint64()
|
||||||
|
return int64(n), err
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("msgpack: invalid code=%x decoding int64", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeFloat32() (float32, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return d.float32(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) float32(c codes.Code) (float32, error) {
|
||||||
|
if c == codes.Float {
|
||||||
|
n, err := d.uint32()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return math.Float32frombits(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := d.int(c)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("msgpack: invalid code=%x decoding float32", c)
|
||||||
|
}
|
||||||
|
return float32(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFloat64 decodes msgpack float32/64 into Go float64.
|
||||||
|
func (d *Decoder) DecodeFloat64() (float64, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return d.float64(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) float64(c codes.Code) (float64, error) {
|
||||||
|
switch c {
|
||||||
|
case codes.Float:
|
||||||
|
n, err := d.float32(c)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return float64(n), nil
|
||||||
|
case codes.Double:
|
||||||
|
n, err := d.uint64()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return math.Float64frombits(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := d.int(c)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("msgpack: invalid code=%x decoding float32", c)
|
||||||
|
}
|
||||||
|
return float64(n), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeUint() (uint, error) {
|
||||||
|
n, err := d.DecodeUint64()
|
||||||
|
return uint(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeUint8() (uint8, error) {
|
||||||
|
n, err := d.DecodeUint64()
|
||||||
|
return uint8(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeUint16() (uint16, error) {
|
||||||
|
n, err := d.DecodeUint64()
|
||||||
|
return uint16(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeUint32() (uint32, error) {
|
||||||
|
n, err := d.DecodeUint64()
|
||||||
|
return uint32(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeInt() (int, error) {
|
||||||
|
n, err := d.DecodeInt64()
|
||||||
|
return int(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeInt8() (int8, error) {
|
||||||
|
n, err := d.DecodeInt64()
|
||||||
|
return int8(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeInt16() (int16, error) {
|
||||||
|
n, err := d.DecodeInt64()
|
||||||
|
return int16(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeInt32() (int32, error) {
|
||||||
|
n, err := d.DecodeInt64()
|
||||||
|
return int32(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeFloat32Value(d *Decoder, v reflect.Value) error {
|
||||||
|
f, err := d.DecodeFloat32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.SetFloat(float64(f))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeFloat64Value(d *Decoder, v reflect.Value) error {
|
||||||
|
f, err := d.DecodeFloat64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.SetFloat(f)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeInt64Value(d *Decoder, v reflect.Value) error {
|
||||||
|
n, err := d.DecodeInt64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.SetInt(n)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeUint64Value(d *Decoder, v reflect.Value) error {
|
||||||
|
n, err := d.DecodeUint64()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.SetUint(n)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type queryResult struct {
|
||||||
|
query string
|
||||||
|
key string
|
||||||
|
hasAsterisk bool
|
||||||
|
|
||||||
|
values []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *queryResult) nextKey() {
|
||||||
|
ind := strings.IndexByte(q.query, '.')
|
||||||
|
if ind == -1 {
|
||||||
|
q.key = q.query
|
||||||
|
q.query = ""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
q.key = q.query[:ind]
|
||||||
|
q.query = q.query[ind+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query extracts data specified by the query from the msgpack stream skipping
|
||||||
|
// any other data. Query consists of map keys and array indexes separated with dot,
|
||||||
|
// e.g. key1.0.key2.
|
||||||
|
func (d *Decoder) Query(query string) ([]interface{}, error) {
|
||||||
|
res := queryResult{
|
||||||
|
query: query,
|
||||||
|
}
|
||||||
|
if err := d.query(&res); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res.values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) query(q *queryResult) error {
|
||||||
|
q.nextKey()
|
||||||
|
if q.key == "" {
|
||||||
|
v, err := d.decodeInterface()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
q.values = append(q.values, v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
code, err := d.PeekCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case code == codes.Map16 || code == codes.Map32 || codes.IsFixedMap(code):
|
||||||
|
err = d.queryMapKey(q)
|
||||||
|
case code == codes.Array16 || code == codes.Array32 || codes.IsFixedArray(code):
|
||||||
|
err = d.queryArrayIndex(q)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("msgpack: unsupported code=%x decoding key=%q", code, q.key)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) queryMapKey(q *queryResult) error {
|
||||||
|
n, err := d.DecodeMapLen()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
k, err := d.bytesNoCopy()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(k) == q.key {
|
||||||
|
if err := d.query(q); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if q.hasAsterisk {
|
||||||
|
return d.skipNext((n - i - 1) * 2)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) queryArrayIndex(q *queryResult) error {
|
||||||
|
n, err := d.DecodeArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if q.key == "*" {
|
||||||
|
q.hasAsterisk = true
|
||||||
|
|
||||||
|
query := q.query
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
q.query = query
|
||||||
|
if err := d.query(q); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
q.hasAsterisk = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ind, err := strconv.Atoi(q.key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if i == ind {
|
||||||
|
if err := d.query(q); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if q.hasAsterisk {
|
||||||
|
return d.skipNext(n - i - 1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) skipNext(n int) error {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if err := d.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
const sliceElemsAllocLimit = 1e4
|
||||||
|
|
||||||
|
var sliceStringPtrType = reflect.TypeOf((*[]string)(nil))
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeArrayLen() (int, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return d.arrayLen(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) arrayLen(c codes.Code) (int, error) {
|
||||||
|
if c == codes.Nil {
|
||||||
|
return -1, nil
|
||||||
|
} else if c >= codes.FixedArrayLow && c <= codes.FixedArrayHigh {
|
||||||
|
return int(c & codes.FixedArrayMask), nil
|
||||||
|
}
|
||||||
|
switch c {
|
||||||
|
case codes.Array16:
|
||||||
|
n, err := d.uint16()
|
||||||
|
return int(n), err
|
||||||
|
case codes.Array32:
|
||||||
|
n, err := d.uint32()
|
||||||
|
return int(n), err
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("msgpack: invalid code=%x decoding array length", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeStringSliceValue(d *Decoder, v reflect.Value) error {
|
||||||
|
ptr := v.Addr().Convert(sliceStringPtrType).Interface().(*[]string)
|
||||||
|
return d.decodeStringSlicePtr(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeStringSlicePtr(ptr *[]string) error {
|
||||||
|
n, err := d.DecodeArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ss := setStringsCap(*ptr, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
s, err := d.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ss = append(ss, s)
|
||||||
|
}
|
||||||
|
*ptr = ss
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setStringsCap(s []string, n int) []string {
|
||||||
|
if n > sliceElemsAllocLimit {
|
||||||
|
n = sliceElemsAllocLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return make([]string, 0, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cap(s) >= n {
|
||||||
|
return s[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
s = s[:cap(s)]
|
||||||
|
s = append(s, make([]string, n-len(s))...)
|
||||||
|
return s[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSliceValue(d *Decoder, v reflect.Value) error {
|
||||||
|
n, err := d.DecodeArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == -1 {
|
||||||
|
v.Set(reflect.Zero(v.Type()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if n == 0 && v.IsNil() {
|
||||||
|
v.Set(reflect.MakeSlice(v.Type(), 0, 0))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.Cap() >= n {
|
||||||
|
v.Set(v.Slice(0, n))
|
||||||
|
} else if v.Len() < v.Cap() {
|
||||||
|
v.Set(v.Slice(0, v.Cap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if i >= v.Len() {
|
||||||
|
v.Set(growSliceValue(v, n))
|
||||||
|
}
|
||||||
|
sv := v.Index(i)
|
||||||
|
if err := d.DecodeValue(sv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func growSliceValue(v reflect.Value, n int) reflect.Value {
|
||||||
|
diff := n - v.Len()
|
||||||
|
if diff > sliceElemsAllocLimit {
|
||||||
|
diff = sliceElemsAllocLimit
|
||||||
|
}
|
||||||
|
v = reflect.AppendSlice(v, reflect.MakeSlice(v.Type(), diff, diff))
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeArrayValue(d *Decoder, v reflect.Value) error {
|
||||||
|
n, err := d.DecodeArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > v.Len() {
|
||||||
|
return fmt.Errorf("%s len is %d, but msgpack has %d elements", v.Type(), v.Len(), n)
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
sv := v.Index(i)
|
||||||
|
if err := d.DecodeValue(sv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeSlice() ([]interface{}, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d.decodeSlice(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeSlice(c codes.Code) ([]interface{}, error) {
|
||||||
|
n, err := d.arrayLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s := make([]interface{}, 0, min(n, sliceElemsAllocLimit))
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
v, err := d.decodeInterface()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s = append(s, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) skipSlice(c codes.Code) error {
|
||||||
|
n, err := d.arrayLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if err := d.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Decoder) bytesLen(c codes.Code) (int, error) {
|
||||||
|
if c == codes.Nil {
|
||||||
|
return -1, nil
|
||||||
|
} else if codes.IsFixedString(c) {
|
||||||
|
return int(c & codes.FixedStrMask), nil
|
||||||
|
}
|
||||||
|
switch c {
|
||||||
|
case codes.Str8, codes.Bin8:
|
||||||
|
n, err := d.uint8()
|
||||||
|
return int(n), err
|
||||||
|
case codes.Str16, codes.Bin16:
|
||||||
|
n, err := d.uint16()
|
||||||
|
return int(n), err
|
||||||
|
case codes.Str32, codes.Bin32:
|
||||||
|
n, err := d.uint32()
|
||||||
|
return int(n), err
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("msgpack: invalid code=%x decoding bytes length", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeString() (string, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return d.string(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) string(c codes.Code) (string, error) {
|
||||||
|
n, err := d.bytesLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
b, err := d.readN(n)
|
||||||
|
return string(b), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeStringValue(d *Decoder, v reflect.Value) error {
|
||||||
|
s, err := d.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.SetString(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeBytesLen() (int, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return d.bytesLen(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeBytes() ([]byte, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d.bytes(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) bytes(c codes.Code, b []byte) ([]byte, error) {
|
||||||
|
n, err := d.bytesLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return readN(d.r, b, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) bytesNoCopy() ([]byte, error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
n, err := d.bytesLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return d.readN(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeBytesPtr(ptr *[]byte) error {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.bytesPtr(c, ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) bytesPtr(c codes.Code, ptr *[]byte) error {
|
||||||
|
n, err := d.bytesLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
*ptr = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
*ptr, err = readN(d.r, *ptr, n)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) skipBytes(c codes.Code) error {
|
||||||
|
n, err := d.bytesLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return d.skipN(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeBytesValue(d *Decoder, v reflect.Value) error {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := d.bytes(c, v.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.SetBytes(b)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeByteArrayValue(d *Decoder, v reflect.Value) error {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := d.bytesLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if n == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if n > v.Len() {
|
||||||
|
return fmt.Errorf("%s len is %d, but msgpack has %d elements", v.Type(), v.Len(), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := v.Slice(0, n).Bytes()
|
||||||
|
return d.readFull(b)
|
||||||
|
}
|
|
@ -0,0 +1,292 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var interfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||||
|
var stringType = reflect.TypeOf((*string)(nil)).Elem()
|
||||||
|
|
||||||
|
var valueDecoders []decoderFunc
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
valueDecoders = []decoderFunc{
|
||||||
|
reflect.Bool: decodeBoolValue,
|
||||||
|
reflect.Int: decodeInt64Value,
|
||||||
|
reflect.Int8: decodeInt64Value,
|
||||||
|
reflect.Int16: decodeInt64Value,
|
||||||
|
reflect.Int32: decodeInt64Value,
|
||||||
|
reflect.Int64: decodeInt64Value,
|
||||||
|
reflect.Uint: decodeUint64Value,
|
||||||
|
reflect.Uint8: decodeUint64Value,
|
||||||
|
reflect.Uint16: decodeUint64Value,
|
||||||
|
reflect.Uint32: decodeUint64Value,
|
||||||
|
reflect.Uint64: decodeUint64Value,
|
||||||
|
reflect.Float32: decodeFloat32Value,
|
||||||
|
reflect.Float64: decodeFloat64Value,
|
||||||
|
reflect.Complex64: decodeUnsupportedValue,
|
||||||
|
reflect.Complex128: decodeUnsupportedValue,
|
||||||
|
reflect.Array: decodeArrayValue,
|
||||||
|
reflect.Chan: decodeUnsupportedValue,
|
||||||
|
reflect.Func: decodeUnsupportedValue,
|
||||||
|
reflect.Interface: decodeInterfaceValue,
|
||||||
|
reflect.Map: decodeMapValue,
|
||||||
|
reflect.Ptr: decodeUnsupportedValue,
|
||||||
|
reflect.Slice: decodeSliceValue,
|
||||||
|
reflect.String: decodeStringValue,
|
||||||
|
reflect.Struct: decodeStructValue,
|
||||||
|
reflect.UnsafePointer: decodeUnsupportedValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustSet(v reflect.Value) error {
|
||||||
|
if !v.CanSet() {
|
||||||
|
return fmt.Errorf("msgpack: Decode(nonsettable %s)", v.Type())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDecoder(typ reflect.Type) decoderFunc {
|
||||||
|
kind := typ.Kind()
|
||||||
|
|
||||||
|
if decoder, ok := typDecMap[typ]; ok {
|
||||||
|
return decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ.Implements(customDecoderType) {
|
||||||
|
return decodeCustomValue
|
||||||
|
}
|
||||||
|
if typ.Implements(unmarshalerType) {
|
||||||
|
return unmarshalValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addressable struct field value.
|
||||||
|
if kind != reflect.Ptr {
|
||||||
|
ptr := reflect.PtrTo(typ)
|
||||||
|
if ptr.Implements(customDecoderType) {
|
||||||
|
return decodeCustomValueAddr
|
||||||
|
}
|
||||||
|
if ptr.Implements(unmarshalerType) {
|
||||||
|
return unmarshalValueAddr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch kind {
|
||||||
|
case reflect.Ptr:
|
||||||
|
return ptrDecoderFunc(typ)
|
||||||
|
case reflect.Slice:
|
||||||
|
elem := typ.Elem()
|
||||||
|
switch elem.Kind() {
|
||||||
|
case reflect.Uint8:
|
||||||
|
return decodeBytesValue
|
||||||
|
}
|
||||||
|
switch elem {
|
||||||
|
case stringType:
|
||||||
|
return decodeStringSliceValue
|
||||||
|
}
|
||||||
|
case reflect.Array:
|
||||||
|
if typ.Elem().Kind() == reflect.Uint8 {
|
||||||
|
return decodeByteArrayValue
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
if typ.Key() == stringType {
|
||||||
|
switch typ.Elem() {
|
||||||
|
case stringType:
|
||||||
|
return decodeMapStringStringValue
|
||||||
|
case interfaceType:
|
||||||
|
return decodeMapStringInterfaceValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return valueDecoders[kind]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptrDecoderFunc(typ reflect.Type) decoderFunc {
|
||||||
|
decoder := getDecoder(typ.Elem())
|
||||||
|
return func(d *Decoder, v reflect.Value) error {
|
||||||
|
if d.hasNilCode() {
|
||||||
|
if err := mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.Set(reflect.Zero(v.Type()))
|
||||||
|
return d.DecodeNil()
|
||||||
|
}
|
||||||
|
if v.IsNil() {
|
||||||
|
if err := mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.Set(reflect.New(v.Type().Elem()))
|
||||||
|
}
|
||||||
|
return decoder(d, v.Elem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeCustomValueAddr(d *Decoder, v reflect.Value) error {
|
||||||
|
if !v.CanAddr() {
|
||||||
|
return fmt.Errorf("msgpack: Decode(nonaddressable %T)", v.Interface())
|
||||||
|
}
|
||||||
|
return decodeCustomValue(d, v.Addr())
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeCustomValue(d *Decoder, v reflect.Value) error {
|
||||||
|
c, err := d.PeekCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if codes.IsExt(c) {
|
||||||
|
c, err = d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
extLen, err := d.parseExtLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = d.PeekCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.extLen = extLen
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == codes.Nil {
|
||||||
|
return d.decodeNilValue(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.IsNil() {
|
||||||
|
v.Set(reflect.New(v.Type().Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder := v.Interface().(CustomDecoder)
|
||||||
|
return decoder.DecodeMsgpack(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalValueAddr(d *Decoder, v reflect.Value) error {
|
||||||
|
if !v.CanAddr() {
|
||||||
|
return fmt.Errorf("msgpack: Decode(nonaddressable %T)", v.Interface())
|
||||||
|
}
|
||||||
|
return unmarshalValue(d, v.Addr())
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalValue(d *Decoder, v reflect.Value) error {
|
||||||
|
c, err := d.PeekCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
extLen := d.extLen
|
||||||
|
d.extLen = 0
|
||||||
|
|
||||||
|
if extLen == 0 && codes.IsExt(c) {
|
||||||
|
c, err = d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
extLen, err = d.parseExtLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = d.PeekCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c == codes.Nil {
|
||||||
|
return d.decodeNilValue(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.IsNil() {
|
||||||
|
v.Set(reflect.New(v.Type().Elem()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if extLen != 0 {
|
||||||
|
b, err := d.readN(extLen)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.rec = b
|
||||||
|
} else {
|
||||||
|
d.rec = makeBuffer()
|
||||||
|
if err := d.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshaler := v.Interface().(Unmarshaler)
|
||||||
|
err = unmarshaler.UnmarshalMsgpack(d.rec)
|
||||||
|
d.rec = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeBoolValue(d *Decoder, v reflect.Value) error {
|
||||||
|
flag, err := d.DecodeBool()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = mustSet(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.SetBool(flag)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeInterfaceValue(d *Decoder, v reflect.Value) error {
|
||||||
|
if v.IsNil() {
|
||||||
|
return d.interfaceValue(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
elem := v.Elem()
|
||||||
|
if !elem.CanAddr() {
|
||||||
|
if d.hasNilCode() {
|
||||||
|
v.Set(reflect.Zero(v.Type()))
|
||||||
|
return d.DecodeNil()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.DecodeValue(elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) interfaceValue(v reflect.Value) error {
|
||||||
|
vv, err := d.decodeInterface()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if vv != nil {
|
||||||
|
if v.Type() == errorType {
|
||||||
|
if vv, ok := vv.(string); ok {
|
||||||
|
v.Set(reflect.ValueOf(errors.New(vv)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Set(reflect.ValueOf(vv))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeUnsupportedValue(d *Decoder, v reflect.Value) error {
|
||||||
|
return fmt.Errorf("msgpack: Decode(unsupported %s)", v.Type())
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(v bool) *Encoder {
|
||||||
|
e.sortMapKeys = v
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// StructAsArray causes the Encoder to encode Go structs as MessagePack arrays.
|
||||||
|
func (e *Encoder) StructAsArray(v bool) *Encoder {
|
||||||
|
e.structAsArray = v
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseJSONTag causes the Encoder to use json struct tag as fallback option
|
||||||
|
// if there is no msgpack tag.
|
||||||
|
func (e *Encoder) UseJSONTag(v bool) *Encoder {
|
||||||
|
e.useJSONTag = v
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) Encode(v ...interface{}) error {
|
||||||
|
for _, vv := range v {
|
||||||
|
if err := e.encode(vv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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.EncodeInt(int64(v))
|
||||||
|
case int64:
|
||||||
|
return e.EncodeInt(v)
|
||||||
|
case uint:
|
||||||
|
return e.EncodeUint(uint64(v))
|
||||||
|
case uint64:
|
||||||
|
return e.EncodeUint(v)
|
||||||
|
case bool:
|
||||||
|
return e.EncodeBool(v)
|
||||||
|
case float32:
|
||||||
|
return e.EncodeFloat32(v)
|
||||||
|
case float64:
|
||||||
|
return e.EncodeFloat64(v)
|
||||||
|
case time.Duration:
|
||||||
|
return e.EncodeInt(int64(v))
|
||||||
|
case time.Time:
|
||||||
|
return e.EncodeTime(v)
|
||||||
|
}
|
||||||
|
return e.EncodeValue(reflect.ValueOf(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeValue(v reflect.Value) error {
|
||||||
|
encode := getEncoder(v.Type())
|
||||||
|
return encode(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
|
||||||
|
}
|
|
@ -0,0 +1,172 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func encodeMapValue(e *Encoder, v reflect.Value) error {
|
||||||
|
if v.IsNil() {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.EncodeMapLen(v.Len()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range v.MapKeys() {
|
||||||
|
if err := e.EncodeValue(key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.EncodeValue(v.MapIndex(key)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeMapStringStringValue(e *Encoder, v reflect.Value) error {
|
||||||
|
if v.IsNil() {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.EncodeMapLen(v.Len()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m := v.Convert(mapStringStringType).Interface().(map[string]string)
|
||||||
|
if e.sortMapKeys {
|
||||||
|
return e.encodeSortedMapStringString(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
for mk, mv := range m {
|
||||||
|
if err := e.EncodeString(mk); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.EncodeString(mv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeMapStringInterfaceValue(e *Encoder, v reflect.Value) error {
|
||||||
|
if v.IsNil() {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.EncodeMapLen(v.Len()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m := v.Convert(mapStringInterfaceType).Interface().(map[string]interface{})
|
||||||
|
if e.sortMapKeys {
|
||||||
|
return e.encodeSortedMapStringInterface(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
for mk, mv := range m {
|
||||||
|
if err := e.EncodeString(mk); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.Encode(mv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) encodeSortedMapStringString(m map[string]string) error {
|
||||||
|
keys := make([]string, 0, len(m))
|
||||||
|
for k, _ := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
for _, k := range keys {
|
||||||
|
err := e.EncodeString(k)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = e.EncodeString(m[k]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) encodeSortedMapStringInterface(m map[string]interface{}) error {
|
||||||
|
keys := make([]string, 0, len(m))
|
||||||
|
for k, _ := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
for _, k := range keys {
|
||||||
|
err := e.EncodeString(k)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = e.Encode(m[k]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeMapLen(l int) error {
|
||||||
|
if l < 16 {
|
||||||
|
return e.writeCode(codes.FixedMapLow | codes.Code(l))
|
||||||
|
}
|
||||||
|
if l < 65536 {
|
||||||
|
return e.write2(codes.Map16, uint64(l))
|
||||||
|
}
|
||||||
|
return e.write4(codes.Map32, uint32(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeStructValue(e *Encoder, strct reflect.Value) error {
|
||||||
|
var structFields *fields
|
||||||
|
if e.useJSONTag {
|
||||||
|
structFields = jsonStructs.Fields(strct.Type())
|
||||||
|
} else {
|
||||||
|
structFields = structs.Fields(strct.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.structAsArray || structFields.AsArray {
|
||||||
|
return encodeStructValueAsArray(e, strct, structFields.List)
|
||||||
|
}
|
||||||
|
fields := structFields.OmitEmpty(strct)
|
||||||
|
|
||||||
|
if err := e.EncodeMapLen(len(fields)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range fields {
|
||||||
|
if err := e.EncodeString(f.name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := f.EncodeValue(e, strct); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeStructValueAsArray(e *Encoder, strct reflect.Value, fields []*field) error {
|
||||||
|
if err := e.EncodeArrayLen(len(fields)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, f := range fields {
|
||||||
|
if err := f.EncodeValue(e, strct); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EncodeUint encodes an uint64 in 1, 2, 3, 5, or 9 bytes.
|
||||||
|
func (e *Encoder) EncodeUint(v uint64) error {
|
||||||
|
if v <= math.MaxInt8 {
|
||||||
|
return e.w.WriteByte(byte(v))
|
||||||
|
}
|
||||||
|
if v <= math.MaxUint8 {
|
||||||
|
return e.write1(codes.Uint8, v)
|
||||||
|
}
|
||||||
|
if v <= math.MaxUint16 {
|
||||||
|
return e.write2(codes.Uint16, v)
|
||||||
|
}
|
||||||
|
if v <= math.MaxUint32 {
|
||||||
|
return e.write4(codes.Uint32, uint32(v))
|
||||||
|
}
|
||||||
|
return e.write8(codes.Uint64, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeInt encodes an int64 in 1, 2, 3, 5, or 9 bytes.
|
||||||
|
func (e *Encoder) EncodeInt(v int64) error {
|
||||||
|
if v >= 0 {
|
||||||
|
return e.EncodeUint(uint64(v))
|
||||||
|
}
|
||||||
|
if v >= int64(int8(codes.NegFixedNumLow)) {
|
||||||
|
return e.w.WriteByte(byte(v))
|
||||||
|
}
|
||||||
|
if v >= math.MinInt8 {
|
||||||
|
return e.write1(codes.Int8, uint64(v))
|
||||||
|
}
|
||||||
|
if v >= math.MinInt16 {
|
||||||
|
return e.write2(codes.Int16, uint64(v))
|
||||||
|
}
|
||||||
|
if v >= math.MinInt32 {
|
||||||
|
return e.write4(codes.Int32, uint32(v))
|
||||||
|
}
|
||||||
|
return e.write8(codes.Int64, uint64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeFloat32(n float32) error {
|
||||||
|
return e.write4(codes.Float, math.Float32bits(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeFloat64(n float64) error {
|
||||||
|
return e.write8(codes.Double, math.Float64bits(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) write1(code codes.Code, n uint64) error {
|
||||||
|
e.buf = e.buf[:2]
|
||||||
|
e.buf[0] = byte(code)
|
||||||
|
e.buf[1] = byte(n)
|
||||||
|
return e.write(e.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) write2(code codes.Code, n uint64) error {
|
||||||
|
e.buf = e.buf[:3]
|
||||||
|
e.buf[0] = byte(code)
|
||||||
|
e.buf[1] = byte(n >> 8)
|
||||||
|
e.buf[2] = byte(n)
|
||||||
|
return e.write(e.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) write4(code codes.Code, n uint32) error {
|
||||||
|
e.buf = e.buf[:5]
|
||||||
|
e.buf[0] = byte(code)
|
||||||
|
e.buf[1] = byte(n >> 24)
|
||||||
|
e.buf[2] = byte(n >> 16)
|
||||||
|
e.buf[3] = byte(n >> 8)
|
||||||
|
e.buf[4] = byte(n)
|
||||||
|
return e.write(e.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) write8(code codes.Code, n uint64) error {
|
||||||
|
e.buf = e.buf[:9]
|
||||||
|
e.buf[0] = byte(code)
|
||||||
|
e.buf[1] = byte(n >> 56)
|
||||||
|
e.buf[2] = byte(n >> 48)
|
||||||
|
e.buf[3] = byte(n >> 40)
|
||||||
|
e.buf[4] = byte(n >> 32)
|
||||||
|
e.buf[5] = byte(n >> 24)
|
||||||
|
e.buf[6] = byte(n >> 16)
|
||||||
|
e.buf[7] = byte(n >> 8)
|
||||||
|
e.buf[8] = byte(n)
|
||||||
|
return e.write(e.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeInt64Value(e *Encoder, v reflect.Value) error {
|
||||||
|
return e.EncodeInt(v.Int())
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeUint64Value(e *Encoder, v reflect.Value) error {
|
||||||
|
return e.EncodeUint(v.Uint())
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeFloat32Value(e *Encoder, v reflect.Value) error {
|
||||||
|
return e.EncodeFloat32(float32(v.Float()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeFloat64Value(e *Encoder, v reflect.Value) error {
|
||||||
|
return e.EncodeFloat64(v.Float())
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func encodeStringValue(e *Encoder, v reflect.Value) error {
|
||||||
|
return e.EncodeString(v.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeByteSliceValue(e *Encoder, v reflect.Value) error {
|
||||||
|
return e.EncodeBytes(v.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeByteArrayValue(e *Encoder, v reflect.Value) error {
|
||||||
|
if err := e.EncodeBytesLen(v.Len()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.CanAddr() {
|
||||||
|
b := v.Slice(0, v.Len()).Bytes()
|
||||||
|
return e.write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.buf = grow(e.buf, v.Len())
|
||||||
|
reflect.Copy(reflect.ValueOf(e.buf), v)
|
||||||
|
return e.write(e.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func grow(b []byte, n int) []byte {
|
||||||
|
if cap(b) >= n {
|
||||||
|
return b[:n]
|
||||||
|
}
|
||||||
|
b = b[:cap(b)]
|
||||||
|
b = append(b, make([]byte, n-len(b))...)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeBytesLen(l int) error {
|
||||||
|
if l < 256 {
|
||||||
|
return e.write1(codes.Bin8, uint64(l))
|
||||||
|
}
|
||||||
|
if l < 65536 {
|
||||||
|
return e.write2(codes.Bin16, uint64(l))
|
||||||
|
}
|
||||||
|
return e.write4(codes.Bin32, uint32(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) encodeStrLen(l int) error {
|
||||||
|
if l < 32 {
|
||||||
|
return e.writeCode(codes.FixedStrLow | codes.Code(l))
|
||||||
|
}
|
||||||
|
if l < 256 {
|
||||||
|
return e.write1(codes.Str8, uint64(l))
|
||||||
|
}
|
||||||
|
if l < 65536 {
|
||||||
|
return e.write2(codes.Str16, uint64(l))
|
||||||
|
}
|
||||||
|
return e.write4(codes.Str32, uint32(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeString(v string) error {
|
||||||
|
if err := e.encodeStrLen(len(v)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return e.writeString(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeBytes(v []byte) error {
|
||||||
|
if v == nil {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
if err := e.EncodeBytesLen(len(v)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return e.write(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeArrayLen(l int) error {
|
||||||
|
if l < 16 {
|
||||||
|
return e.writeCode(codes.FixedArrayLow | codes.Code(l))
|
||||||
|
}
|
||||||
|
if l < 65536 {
|
||||||
|
return e.write2(codes.Array16, uint64(l))
|
||||||
|
}
|
||||||
|
return e.write4(codes.Array32, uint32(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) encodeStringSlice(s []string) error {
|
||||||
|
if s == nil {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
if err := e.EncodeArrayLen(len(s)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, v := range s {
|
||||||
|
if err := e.EncodeString(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeSliceValue(e *Encoder, v reflect.Value) error {
|
||||||
|
if v.IsNil() {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
return encodeArrayValue(e, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeArrayValue(e *Encoder, v reflect.Value) error {
|
||||||
|
l := v.Len()
|
||||||
|
if err := e.EncodeArrayLen(l); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
if err := e.EncodeValue(v.Index(i)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
var valueEncoders []encoderFunc
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
valueEncoders = []encoderFunc{
|
||||||
|
reflect.Bool: encodeBoolValue,
|
||||||
|
reflect.Int: encodeInt64Value,
|
||||||
|
reflect.Int8: encodeInt64Value,
|
||||||
|
reflect.Int16: encodeInt64Value,
|
||||||
|
reflect.Int32: encodeInt64Value,
|
||||||
|
reflect.Int64: encodeInt64Value,
|
||||||
|
reflect.Uint: encodeUint64Value,
|
||||||
|
reflect.Uint8: encodeUint64Value,
|
||||||
|
reflect.Uint16: encodeUint64Value,
|
||||||
|
reflect.Uint32: encodeUint64Value,
|
||||||
|
reflect.Uint64: encodeUint64Value,
|
||||||
|
reflect.Float32: encodeFloat32Value,
|
||||||
|
reflect.Float64: encodeFloat64Value,
|
||||||
|
reflect.Complex64: encodeUnsupportedValue,
|
||||||
|
reflect.Complex128: encodeUnsupportedValue,
|
||||||
|
reflect.Array: encodeArrayValue,
|
||||||
|
reflect.Chan: encodeUnsupportedValue,
|
||||||
|
reflect.Func: encodeUnsupportedValue,
|
||||||
|
reflect.Interface: encodeInterfaceValue,
|
||||||
|
reflect.Map: encodeMapValue,
|
||||||
|
reflect.Ptr: encodeUnsupportedValue,
|
||||||
|
reflect.Slice: encodeSliceValue,
|
||||||
|
reflect.String: encodeStringValue,
|
||||||
|
reflect.Struct: encodeStructValue,
|
||||||
|
reflect.UnsafePointer: encodeUnsupportedValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEncoder(typ reflect.Type) encoderFunc {
|
||||||
|
if encoder, ok := typEncMap[typ]; ok {
|
||||||
|
return encoder
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ.Implements(customEncoderType) {
|
||||||
|
return encodeCustomValue
|
||||||
|
}
|
||||||
|
if typ.Implements(marshalerType) {
|
||||||
|
return marshalValue
|
||||||
|
}
|
||||||
|
|
||||||
|
kind := typ.Kind()
|
||||||
|
|
||||||
|
// Addressable struct field value.
|
||||||
|
if kind != reflect.Ptr {
|
||||||
|
ptr := reflect.PtrTo(typ)
|
||||||
|
if ptr.Implements(customEncoderType) {
|
||||||
|
return encodeCustomValuePtr
|
||||||
|
}
|
||||||
|
if ptr.Implements(marshalerType) {
|
||||||
|
return marshalValuePtr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ == errorType {
|
||||||
|
return encodeErrorValue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch kind {
|
||||||
|
case reflect.Ptr:
|
||||||
|
return ptrEncoderFunc(typ)
|
||||||
|
case reflect.Slice:
|
||||||
|
if typ.Elem().Kind() == reflect.Uint8 {
|
||||||
|
return encodeByteSliceValue
|
||||||
|
}
|
||||||
|
case reflect.Array:
|
||||||
|
if typ.Elem().Kind() == reflect.Uint8 {
|
||||||
|
return encodeByteArrayValue
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
if typ.Key() == stringType {
|
||||||
|
switch typ.Elem() {
|
||||||
|
case stringType:
|
||||||
|
return encodeMapStringStringValue
|
||||||
|
case interfaceType:
|
||||||
|
return encodeMapStringInterfaceValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return valueEncoders[kind]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptrEncoderFunc(typ reflect.Type) encoderFunc {
|
||||||
|
encoder := getEncoder(typ.Elem())
|
||||||
|
return func(e *Encoder, v reflect.Value) error {
|
||||||
|
if v.IsNil() {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
return encoder(e, v.Elem())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeCustomValuePtr(e *Encoder, v reflect.Value) error {
|
||||||
|
if !v.CanAddr() {
|
||||||
|
return fmt.Errorf("msgpack: Encode(non-addressable %T)", v.Interface())
|
||||||
|
}
|
||||||
|
encoder := v.Addr().Interface().(CustomEncoder)
|
||||||
|
return encoder.EncodeMsgpack(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeCustomValue(e *Encoder, v reflect.Value) error {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||||
|
if v.IsNil() {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder := v.Interface().(CustomEncoder)
|
||||||
|
return encoder.EncodeMsgpack(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalValuePtr(e *Encoder, v reflect.Value) error {
|
||||||
|
if !v.CanAddr() {
|
||||||
|
return fmt.Errorf("msgpack: Encode(non-addressable %T)", v.Interface())
|
||||||
|
}
|
||||||
|
return marshalValue(e, v.Addr())
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalValue(e *Encoder, v reflect.Value) error {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||||
|
if v.IsNil() {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
marshaler := v.Interface().(Marshaler)
|
||||||
|
b, err := marshaler.MarshalMsgpack()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = e.w.Write(b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeBoolValue(e *Encoder, v reflect.Value) error {
|
||||||
|
return e.EncodeBool(v.Bool())
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeInterfaceValue(e *Encoder, v reflect.Value) error {
|
||||||
|
if v.IsNil() {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
return e.EncodeValue(v.Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeErrorValue(e *Encoder, v reflect.Value) error {
|
||||||
|
if v.IsNil() {
|
||||||
|
return e.EncodeNil()
|
||||||
|
}
|
||||||
|
return e.EncodeString(v.Interface().(error).Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeUnsupportedValue(e *Encoder, v reflect.Value) error {
|
||||||
|
return fmt.Errorf("msgpack: Encode(unsupported %s)", v.Type())
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var extTypes = make(map[int8]reflect.Type)
|
||||||
|
|
||||||
|
var bufferPool = &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new(bytes.Buffer)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterExt records a type, identified by a value for that type,
|
||||||
|
// under the provided id. That id will identify the concrete type of a value
|
||||||
|
// sent or received as an interface variable. Only types that will be
|
||||||
|
// transferred as implementations of interface values need to be registered.
|
||||||
|
// Expecting to be used only during initialization, it panics if the mapping
|
||||||
|
// between types and ids is not a bijection.
|
||||||
|
func RegisterExt(id int8, value interface{}) {
|
||||||
|
typ := reflect.TypeOf(value)
|
||||||
|
if typ.Kind() == reflect.Ptr {
|
||||||
|
typ = typ.Elem()
|
||||||
|
}
|
||||||
|
ptr := reflect.PtrTo(typ)
|
||||||
|
|
||||||
|
if _, ok := extTypes[id]; ok {
|
||||||
|
panic(fmt.Errorf("msgpack: ext with id=%d is already registered", id))
|
||||||
|
}
|
||||||
|
|
||||||
|
registerExt(id, ptr, getEncoder(ptr), nil)
|
||||||
|
registerExt(id, typ, getEncoder(typ), getDecoder(typ))
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerExt(id int8, typ reflect.Type, enc encoderFunc, dec decoderFunc) {
|
||||||
|
if dec != nil {
|
||||||
|
extTypes[id] = typ
|
||||||
|
}
|
||||||
|
if enc != nil {
|
||||||
|
typEncMap[typ] = makeExtEncoder(id, enc)
|
||||||
|
}
|
||||||
|
if dec != nil {
|
||||||
|
typDecMap[typ] = dec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeExtHeader(typeId int8, length int) error {
|
||||||
|
if err := e.encodeExtLen(length); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.w.WriteByte(byte(typeId)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeExtEncoder(typeId int8, enc encoderFunc) encoderFunc {
|
||||||
|
return func(e *Encoder, v reflect.Value) error {
|
||||||
|
buf := bufferPool.Get().(*bytes.Buffer)
|
||||||
|
defer bufferPool.Put(buf)
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
oldw := e.w
|
||||||
|
e.w = buf
|
||||||
|
err := enc(e, v)
|
||||||
|
e.w = oldw
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := e.EncodeExtHeader(typeId, buf.Len()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return e.write(buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) encodeExtLen(l int) error {
|
||||||
|
switch l {
|
||||||
|
case 1:
|
||||||
|
return e.writeCode(codes.FixExt1)
|
||||||
|
case 2:
|
||||||
|
return e.writeCode(codes.FixExt2)
|
||||||
|
case 4:
|
||||||
|
return e.writeCode(codes.FixExt4)
|
||||||
|
case 8:
|
||||||
|
return e.writeCode(codes.FixExt8)
|
||||||
|
case 16:
|
||||||
|
return e.writeCode(codes.FixExt16)
|
||||||
|
}
|
||||||
|
if l < 256 {
|
||||||
|
return e.write1(codes.Ext8, uint64(l))
|
||||||
|
}
|
||||||
|
if l < 65536 {
|
||||||
|
return e.write2(codes.Ext16, uint64(l))
|
||||||
|
}
|
||||||
|
return e.write4(codes.Ext32, uint32(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) parseExtLen(c codes.Code) (int, error) {
|
||||||
|
switch c {
|
||||||
|
case codes.FixExt1:
|
||||||
|
return 1, nil
|
||||||
|
case codes.FixExt2:
|
||||||
|
return 2, nil
|
||||||
|
case codes.FixExt4:
|
||||||
|
return 4, nil
|
||||||
|
case codes.FixExt8:
|
||||||
|
return 8, nil
|
||||||
|
case codes.FixExt16:
|
||||||
|
return 16, nil
|
||||||
|
case codes.Ext8:
|
||||||
|
n, err := d.uint8()
|
||||||
|
return int(n), err
|
||||||
|
case codes.Ext16:
|
||||||
|
n, err := d.uint16()
|
||||||
|
return int(n), err
|
||||||
|
case codes.Ext32:
|
||||||
|
n, err := d.uint32()
|
||||||
|
return int(n), err
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("msgpack: invalid code=%x decoding ext length", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeExtHeader(c codes.Code) (int8, int, error) {
|
||||||
|
length, err := d.parseExtLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
typeId, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return int8(typeId), length, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeExtHeader() (typeId int8, length int, err error) {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return d.decodeExtHeader(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) extInterface(c codes.Code) (interface{}, error) {
|
||||||
|
extId, extLen, err := d.decodeExtHeader(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
typ, ok := extTypes[extId]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("msgpack: unregistered ext id=%d", extId)
|
||||||
|
}
|
||||||
|
|
||||||
|
v := reflect.New(typ)
|
||||||
|
|
||||||
|
d.extLen = extLen
|
||||||
|
err = d.DecodeValue(v.Elem())
|
||||||
|
d.extLen = 0
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) skipExt(c codes.Code) error {
|
||||||
|
n, err := d.parseExtLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.skipN(n + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) skipExtHeader(c codes.Code) error {
|
||||||
|
// Read ext type.
|
||||||
|
_, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Read ext body len.
|
||||||
|
for i := 0; i < extHeaderLen(c); i++ {
|
||||||
|
_, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extHeaderLen(c codes.Code) int {
|
||||||
|
switch c {
|
||||||
|
case codes.Ext8:
|
||||||
|
return 1
|
||||||
|
case codes.Ext16:
|
||||||
|
return 2
|
||||||
|
case codes.Ext32:
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
type Marshaler interface {
|
||||||
|
MarshalMsgpack() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Unmarshaler interface {
|
||||||
|
UnmarshalMsgpack([]byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomEncoder interface {
|
||||||
|
EncodeMsgpack(*Encoder) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomDecoder interface {
|
||||||
|
DecodeMsgpack(*Decoder) error
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
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,145 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var timeExtId int8 = -1
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
timeType := reflect.TypeOf((*time.Time)(nil)).Elem()
|
||||||
|
registerExt(timeExtId, timeType, encodeTimeValue, decodeTimeValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) EncodeTime(tm time.Time) error {
|
||||||
|
b := e.encodeTime(tm)
|
||||||
|
if err := e.encodeExtLen(len(b)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.w.WriteByte(byte(timeExtId)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return e.write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) encodeTime(tm time.Time) []byte {
|
||||||
|
secs := uint64(tm.Unix())
|
||||||
|
if secs>>34 == 0 {
|
||||||
|
data := uint64(tm.Nanosecond())<<34 | secs
|
||||||
|
if data&0xffffffff00000000 == 0 {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(b, uint32(data))
|
||||||
|
return b
|
||||||
|
} else {
|
||||||
|
b := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(b, data)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]byte, 12)
|
||||||
|
binary.BigEndian.PutUint32(b, uint32(tm.Nanosecond()))
|
||||||
|
binary.BigEndian.PutUint64(b[4:], uint64(secs))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) DecodeTime() (time.Time, error) {
|
||||||
|
tm, err := d.decodeTime()
|
||||||
|
if err != nil {
|
||||||
|
return tm, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if tm.IsZero() {
|
||||||
|
// Assume that zero time does not have timezone information.
|
||||||
|
return tm.UTC(), nil
|
||||||
|
}
|
||||||
|
return tm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Decoder) decodeTime() (time.Time, error) {
|
||||||
|
extLen := d.extLen
|
||||||
|
d.extLen = 0
|
||||||
|
if extLen == 0 {
|
||||||
|
c, err := d.readCode()
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy format.
|
||||||
|
if c == codes.FixedArrayLow|2 {
|
||||||
|
sec, err := d.DecodeInt64()
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nsec, err := d.DecodeInt64()
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Unix(sec, nsec), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if codes.IsString(c) {
|
||||||
|
s, err := d.string(c)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
return time.Parse(time.RFC3339Nano, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
extLen, err = d.parseExtLen(c)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip ext id.
|
||||||
|
_, err = d.s.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := d.readN(extLen)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(b) {
|
||||||
|
case 4:
|
||||||
|
sec := binary.BigEndian.Uint32(b)
|
||||||
|
return time.Unix(int64(sec), 0), nil
|
||||||
|
case 8:
|
||||||
|
sec := binary.BigEndian.Uint64(b)
|
||||||
|
nsec := int64(sec >> 34)
|
||||||
|
sec &= 0x00000003ffffffff
|
||||||
|
return time.Unix(int64(sec), nsec), nil
|
||||||
|
case 12:
|
||||||
|
nsec := binary.BigEndian.Uint32(b)
|
||||||
|
sec := binary.BigEndian.Uint64(b[4:])
|
||||||
|
return time.Unix(int64(sec), int64(nsec)), nil
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("msgpack: invalid ext len=%d decoding time", extLen)
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeTimeValue(e *Encoder, v reflect.Value) error {
|
||||||
|
tm := v.Interface().(time.Time)
|
||||||
|
b := e.encodeTime(tm)
|
||||||
|
return e.write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeTimeValue(d *Decoder, v reflect.Value) error {
|
||||||
|
tm, err := d.DecodeTime()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
v.Set(reflect.ValueOf(tm))
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,307 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||||
|
|
||||||
|
var customEncoderType = reflect.TypeOf((*CustomEncoder)(nil)).Elem()
|
||||||
|
var customDecoderType = reflect.TypeOf((*CustomDecoder)(nil)).Elem()
|
||||||
|
|
||||||
|
var marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||||
|
var unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
||||||
|
|
||||||
|
type encoderFunc func(*Encoder, reflect.Value) error
|
||||||
|
type decoderFunc func(*Decoder, reflect.Value) error
|
||||||
|
|
||||||
|
var typEncMap = make(map[reflect.Type]encoderFunc)
|
||||||
|
var typDecMap = make(map[reflect.Type]decoderFunc)
|
||||||
|
|
||||||
|
// Register registers encoder and decoder functions for a value.
|
||||||
|
// This is low level API and in most cases you should prefer implementing
|
||||||
|
// Marshaler/CustomEncoder and Unmarshaler/CustomDecoder interfaces.
|
||||||
|
func Register(value interface{}, enc encoderFunc, dec decoderFunc) {
|
||||||
|
typ := reflect.TypeOf(value)
|
||||||
|
if enc != nil {
|
||||||
|
typEncMap[typ] = enc
|
||||||
|
}
|
||||||
|
if dec != nil {
|
||||||
|
typDecMap[typ] = dec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
var structs = newStructCache(false)
|
||||||
|
var jsonStructs = newStructCache(true)
|
||||||
|
|
||||||
|
type structCache struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
m map[reflect.Type]*fields
|
||||||
|
|
||||||
|
useJSONTag bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStructCache(useJSONTag bool) *structCache {
|
||||||
|
return &structCache{
|
||||||
|
m: make(map[reflect.Type]*fields),
|
||||||
|
|
||||||
|
useJSONTag: useJSONTag,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *structCache) Fields(typ reflect.Type) *fields {
|
||||||
|
m.mu.RLock()
|
||||||
|
fs, ok := m.m[typ]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
fs, ok = m.m[typ]
|
||||||
|
if !ok {
|
||||||
|
fs = getFields(typ, m.useJSONTag)
|
||||||
|
m.m[typ] = fs
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type field struct {
|
||||||
|
name string
|
||||||
|
index []int
|
||||||
|
omitEmpty bool
|
||||||
|
encoder encoderFunc
|
||||||
|
decoder decoderFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *field) value(v reflect.Value) reflect.Value {
|
||||||
|
return fieldByIndex(v, f.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *field) Omit(strct reflect.Value) bool {
|
||||||
|
return f.omitEmpty && isEmptyValue(f.value(strct))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *field) EncodeValue(e *Encoder, strct reflect.Value) error {
|
||||||
|
return f.encoder(e, f.value(strct))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *field) DecodeValue(d *Decoder, strct reflect.Value) error {
|
||||||
|
return f.decoder(d, f.value(strct))
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type fields struct {
|
||||||
|
Table map[string]*field
|
||||||
|
List []*field
|
||||||
|
AsArray bool
|
||||||
|
|
||||||
|
hasOmitEmpty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFields(numField int) *fields {
|
||||||
|
return &fields{
|
||||||
|
Table: make(map[string]*field, numField),
|
||||||
|
List: make([]*field, 0, numField),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *fields) Add(field *field) {
|
||||||
|
fs.Table[field.name] = field
|
||||||
|
fs.List = append(fs.List, field)
|
||||||
|
if field.omitEmpty {
|
||||||
|
fs.hasOmitEmpty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *fields) OmitEmpty(strct reflect.Value) []*field {
|
||||||
|
if !fs.hasOmitEmpty {
|
||||||
|
return fs.List
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := make([]*field, 0, len(fs.List))
|
||||||
|
for _, f := range fs.List {
|
||||||
|
if !f.Omit(strct) {
|
||||||
|
fields = append(fields, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFields(typ reflect.Type, useJSONTag bool) *fields {
|
||||||
|
numField := typ.NumField()
|
||||||
|
fs := newFields(numField)
|
||||||
|
|
||||||
|
var omitEmpty bool
|
||||||
|
for i := 0; i < numField; i++ {
|
||||||
|
f := typ.Field(i)
|
||||||
|
|
||||||
|
tag := f.Tag.Get("msgpack")
|
||||||
|
if useJSONTag && tag == "" {
|
||||||
|
tag = f.Tag.Get("json")
|
||||||
|
}
|
||||||
|
|
||||||
|
name, opt := parseTag(tag)
|
||||||
|
if name == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Name == "_msgpack" {
|
||||||
|
if opt.Contains("asArray") {
|
||||||
|
fs.AsArray = true
|
||||||
|
}
|
||||||
|
if opt.Contains("omitempty") {
|
||||||
|
omitEmpty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.PkgPath != "" && !f.Anonymous {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
field := &field{
|
||||||
|
name: name,
|
||||||
|
index: f.Index,
|
||||||
|
omitEmpty: omitEmpty || opt.Contains("omitempty"),
|
||||||
|
encoder: getEncoder(f.Type),
|
||||||
|
decoder: getDecoder(f.Type),
|
||||||
|
}
|
||||||
|
|
||||||
|
if field.name == "" {
|
||||||
|
field.name = f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Anonymous {
|
||||||
|
if opt.Contains("inline") {
|
||||||
|
inlineFields(fs, f.Type, field, useJSONTag)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if autoinlineFields(fs, f.Type, field, useJSONTag) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.Add(field)
|
||||||
|
}
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
|
||||||
|
var encodeStructValuePtr uintptr
|
||||||
|
var decodeStructValuePtr uintptr
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
encodeStructValuePtr = reflect.ValueOf(encodeStructValue).Pointer()
|
||||||
|
decodeStructValuePtr = reflect.ValueOf(decodeStructValue).Pointer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func inlineFields(fs *fields, typ reflect.Type, f *field, useJSONTag bool) {
|
||||||
|
inlinedFields := getFields(typ, useJSONTag).List
|
||||||
|
for _, field := range inlinedFields {
|
||||||
|
if _, ok := fs.Table[field.name]; ok {
|
||||||
|
// Don't inline shadowed fields.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
field.index = append(f.index, field.index...)
|
||||||
|
fs.Add(field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func autoinlineFields(fs *fields, typ reflect.Type, f *field, useJSONTag bool) bool {
|
||||||
|
var encoder encoderFunc
|
||||||
|
var decoder decoderFunc
|
||||||
|
|
||||||
|
if typ.Kind() == reflect.Struct {
|
||||||
|
encoder = f.encoder
|
||||||
|
decoder = f.decoder
|
||||||
|
} else {
|
||||||
|
for typ.Kind() == reflect.Ptr {
|
||||||
|
typ = typ.Elem()
|
||||||
|
encoder = getEncoder(typ)
|
||||||
|
decoder = getDecoder(typ)
|
||||||
|
}
|
||||||
|
if typ.Kind() != reflect.Struct {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if reflect.ValueOf(encoder).Pointer() != encodeStructValuePtr {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if reflect.ValueOf(decoder).Pointer() != decodeStructValuePtr {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
inlinedFields := getFields(typ, useJSONTag).List
|
||||||
|
for _, field := range inlinedFields {
|
||||||
|
if _, ok := fs.Table[field.name]; ok {
|
||||||
|
// Don't inline shadowed fields.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, field := range inlinedFields {
|
||||||
|
field.index = append(f.index, field.index...)
|
||||||
|
fs.Add(field)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEmptyValue(v reflect.Value) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
||||||
|
return v.Len() == 0
|
||||||
|
case reflect.Bool:
|
||||||
|
return !v.Bool()
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return v.Int() == 0
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return v.Uint() == 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return v.Float() == 0
|
||||||
|
case reflect.Interface, reflect.Ptr:
|
||||||
|
return v.IsNil()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldByIndex(v reflect.Value, index []int) reflect.Value {
|
||||||
|
if len(index) == 1 {
|
||||||
|
return v.Field(index[0])
|
||||||
|
}
|
||||||
|
for i, x := range index {
|
||||||
|
if i > 0 {
|
||||||
|
var ok bool
|
||||||
|
v, ok = indirectNew(v)
|
||||||
|
if !ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v = v.Field(x)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func indirectNew(v reflect.Value) (reflect.Value, bool) {
|
||||||
|
if v.Kind() == reflect.Ptr {
|
||||||
|
if v.IsNil() {
|
||||||
|
if !v.CanSet() {
|
||||||
|
return v, false
|
||||||
|
}
|
||||||
|
elemType := v.Type().Elem()
|
||||||
|
if elemType.Kind() != reflect.Struct {
|
||||||
|
return v, false
|
||||||
|
}
|
||||||
|
v.Set(reflect.New(elemType))
|
||||||
|
}
|
||||||
|
v = v.Elem()
|
||||||
|
}
|
||||||
|
return v, true
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Package msgpack provides functions for serializing cty values in the
|
||||||
|
// msgpack encoding, and decoding them again.
|
||||||
|
//
|
||||||
|
// If the same type information is provided both at encoding and decoding time
|
||||||
|
// then values can be round-tripped without loss, except for capsule types
|
||||||
|
// which are not currently supported.
|
||||||
|
//
|
||||||
|
// If any unknown values are passed to Marshal then they will be represented
|
||||||
|
// using a msgpack extension with type code zero, which is understood by
|
||||||
|
// the Unmarshal function within this package but will not be understood by
|
||||||
|
// a generic (non-cty-aware) msgpack decoder. Ensure that no unknown values
|
||||||
|
// are used if interoperability with other msgpack implementations is
|
||||||
|
// required.
|
||||||
|
package msgpack
|
|
@ -0,0 +1,31 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dynamicVal struct {
|
||||||
|
Value cty.Value
|
||||||
|
Path cty.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dv *dynamicVal) MarshalMsgpack() ([]byte, error) {
|
||||||
|
// Rather than defining a msgpack-specific serialization of types,
|
||||||
|
// instead we use the existing JSON serialization.
|
||||||
|
typeJSON, err := dv.Value.Type().MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return nil, dv.Path.NewErrorf("failed to serialize type: %s", err)
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := msgpack.NewEncoder(&buf)
|
||||||
|
enc.EncodeArrayLen(2)
|
||||||
|
enc.EncodeBytes(typeJSON)
|
||||||
|
err = marshal(dv.Value, dv.Value.Type(), dv.Path, enc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var negativeInfinity = math.Inf(-1)
|
||||||
|
var positiveInfinity = math.Inf(1)
|
|
@ -0,0 +1,207 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math/big"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
"github.com/zclconf/go-cty/cty/convert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Marshal produces a msgpack serialization of the given value that
|
||||||
|
// can be decoded into the given type later using Unmarshal.
|
||||||
|
//
|
||||||
|
// The given value must conform to the given type, or an error will
|
||||||
|
// be returned.
|
||||||
|
func Marshal(val cty.Value, ty cty.Type) ([]byte, error) {
|
||||||
|
errs := val.Type().TestConformance(ty)
|
||||||
|
if errs != nil {
|
||||||
|
// Attempt a conversion
|
||||||
|
var err error
|
||||||
|
val, err = convert.Convert(val, ty)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// From this point onward, val can be assumed to be conforming to t.
|
||||||
|
|
||||||
|
var path cty.Path
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := msgpack.NewEncoder(&buf)
|
||||||
|
|
||||||
|
err := marshal(val, ty, path, enc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshal(val cty.Value, ty cty.Type, path cty.Path, enc *msgpack.Encoder) error {
|
||||||
|
// If we're going to decode as DynamicPseudoType then we need to save
|
||||||
|
// dynamic type information to recover the real type.
|
||||||
|
if ty == cty.DynamicPseudoType && val.Type() != cty.DynamicPseudoType {
|
||||||
|
return marshalDynamic(val, path, enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !val.IsKnown() {
|
||||||
|
err := enc.Encode(unknownVal)
|
||||||
|
if err != nil {
|
||||||
|
return path.NewError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if val.IsNull() {
|
||||||
|
err := enc.EncodeNil()
|
||||||
|
if err != nil {
|
||||||
|
return path.NewError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The caller should've guaranteed that the given val is conformant with
|
||||||
|
// the given type ty, so we'll proceed under that assumption here.
|
||||||
|
switch {
|
||||||
|
case ty.IsPrimitiveType():
|
||||||
|
switch ty {
|
||||||
|
case cty.String:
|
||||||
|
err := enc.EncodeString(val.AsString())
|
||||||
|
if err != nil {
|
||||||
|
return path.NewError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case cty.Number:
|
||||||
|
var err error
|
||||||
|
switch {
|
||||||
|
case val.RawEquals(cty.PositiveInfinity):
|
||||||
|
err = enc.EncodeFloat64(positiveInfinity)
|
||||||
|
case val.RawEquals(cty.NegativeInfinity):
|
||||||
|
err = enc.EncodeFloat64(negativeInfinity)
|
||||||
|
default:
|
||||||
|
bf := val.AsBigFloat()
|
||||||
|
if iv, acc := bf.Int64(); acc == big.Exact {
|
||||||
|
err = enc.EncodeInt(iv)
|
||||||
|
} else if fv, acc := bf.Float64(); acc == big.Exact {
|
||||||
|
err = enc.EncodeFloat64(fv)
|
||||||
|
} else {
|
||||||
|
err = enc.EncodeString(bf.Text('f', -1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return path.NewError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case cty.Bool:
|
||||||
|
err := enc.EncodeBool(val.True())
|
||||||
|
if err != nil {
|
||||||
|
return path.NewError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
panic("unsupported primitive type")
|
||||||
|
}
|
||||||
|
case ty.IsListType(), ty.IsSetType():
|
||||||
|
enc.EncodeArrayLen(val.LengthInt())
|
||||||
|
ety := ty.ElementType()
|
||||||
|
it := val.ElementIterator()
|
||||||
|
path := append(path, nil) // local override of 'path' with extra element
|
||||||
|
for it.Next() {
|
||||||
|
ek, ev := it.Element()
|
||||||
|
path[len(path)-1] = cty.IndexStep{
|
||||||
|
Key: ek,
|
||||||
|
}
|
||||||
|
err := marshal(ev, ety, path, enc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case ty.IsMapType():
|
||||||
|
enc.EncodeMapLen(val.LengthInt())
|
||||||
|
ety := ty.ElementType()
|
||||||
|
it := val.ElementIterator()
|
||||||
|
path := append(path, nil) // local override of 'path' with extra element
|
||||||
|
for it.Next() {
|
||||||
|
ek, ev := it.Element()
|
||||||
|
path[len(path)-1] = cty.IndexStep{
|
||||||
|
Key: ek,
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
err = marshal(ek, ek.Type(), path, enc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = marshal(ev, ety, path, enc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case ty.IsTupleType():
|
||||||
|
etys := ty.TupleElementTypes()
|
||||||
|
it := val.ElementIterator()
|
||||||
|
path := append(path, nil) // local override of 'path' with extra element
|
||||||
|
i := 0
|
||||||
|
enc.EncodeArrayLen(len(etys))
|
||||||
|
for it.Next() {
|
||||||
|
ety := etys[i]
|
||||||
|
ek, ev := it.Element()
|
||||||
|
path[len(path)-1] = cty.IndexStep{
|
||||||
|
Key: ek,
|
||||||
|
}
|
||||||
|
err := marshal(ev, ety, path, enc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case ty.IsObjectType():
|
||||||
|
atys := ty.AttributeTypes()
|
||||||
|
path := append(path, nil) // local override of 'path' with extra element
|
||||||
|
|
||||||
|
names := make([]string, 0, len(atys))
|
||||||
|
for k := range atys {
|
||||||
|
names = append(names, k)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
|
||||||
|
enc.EncodeMapLen(len(names))
|
||||||
|
|
||||||
|
for _, k := range names {
|
||||||
|
aty := atys[k]
|
||||||
|
av := val.GetAttr(k)
|
||||||
|
path[len(path)-1] = cty.GetAttrStep{
|
||||||
|
Name: k,
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
err = marshal(cty.StringVal(k), cty.String, path, enc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = marshal(av, aty, path, enc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case ty.IsCapsuleType():
|
||||||
|
return path.NewErrorf("capsule types not supported for msgpack encoding")
|
||||||
|
default:
|
||||||
|
// should never happen
|
||||||
|
return path.NewErrorf("cannot msgpack-serialize %s", ty.FriendlyName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshalDynamic adds an extra wrapping object containing dynamic type
|
||||||
|
// information for the given value.
|
||||||
|
func marshalDynamic(val cty.Value, path cty.Path, enc *msgpack.Encoder) error {
|
||||||
|
dv := dynamicVal{
|
||||||
|
Value: val,
|
||||||
|
Path: path,
|
||||||
|
}
|
||||||
|
return enc.Encode(&dv)
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack"
|
||||||
|
msgpackcodes "github.com/vmihailenco/msgpack/codes"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImpliedType returns the cty Type implied by the structure of the given
|
||||||
|
// msgpack-compliant buffer. This function implements the default type mapping
|
||||||
|
// behavior used when decoding arbitrary msgpack without explicit cty Type
|
||||||
|
// information.
|
||||||
|
//
|
||||||
|
// The rules are as follows:
|
||||||
|
//
|
||||||
|
// msgpack strings, numbers and bools map to their equivalent primitive type in
|
||||||
|
// cty.
|
||||||
|
//
|
||||||
|
// msgpack maps become cty object types, with the attributes defined by the
|
||||||
|
// map keys and the types of their values.
|
||||||
|
//
|
||||||
|
// msgpack arrays become cty tuple types, with the elements defined by the
|
||||||
|
// types of the array members.
|
||||||
|
//
|
||||||
|
// Any nulls are typed as DynamicPseudoType, so callers of this function
|
||||||
|
// must be prepared to deal with this. Callers that do not wish to deal with
|
||||||
|
// dynamic typing should not use this function and should instead describe
|
||||||
|
// their required types explicitly with a cty.Type instance when decoding.
|
||||||
|
//
|
||||||
|
// Any unknown values are similarly typed as DynamicPseudoType, because these
|
||||||
|
// do not carry type information on the wire.
|
||||||
|
//
|
||||||
|
// Any parse errors will be returned as an error, and the type will be the
|
||||||
|
// invalid value cty.NilType.
|
||||||
|
func ImpliedType(buf []byte) (cty.Type, error) {
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
|
dec := msgpack.NewDecoder(r)
|
||||||
|
|
||||||
|
ty, err := impliedType(dec)
|
||||||
|
if err != nil {
|
||||||
|
return cty.NilType, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We must now be at the end of the buffer
|
||||||
|
err = dec.Skip()
|
||||||
|
if err != io.EOF {
|
||||||
|
return ty, fmt.Errorf("extra bytes after msgpack value")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func impliedType(dec *msgpack.Decoder) (cty.Type, error) {
|
||||||
|
// If this function returns with a nil error then it must have already
|
||||||
|
// consumed the next value from the decoder, since when called recursively
|
||||||
|
// the caller will be expecting to find a following value here.
|
||||||
|
|
||||||
|
code, err := dec.PeekCode()
|
||||||
|
if err != nil {
|
||||||
|
return cty.NilType, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
|
||||||
|
case code == msgpackcodes.Nil || msgpackcodes.IsExt(code):
|
||||||
|
err := dec.Skip()
|
||||||
|
return cty.DynamicPseudoType, err
|
||||||
|
|
||||||
|
case code == msgpackcodes.True || code == msgpackcodes.False:
|
||||||
|
_, err := dec.DecodeBool()
|
||||||
|
return cty.Bool, err
|
||||||
|
|
||||||
|
case msgpackcodes.IsFixedNum(code):
|
||||||
|
_, err := dec.DecodeInt64()
|
||||||
|
return cty.Number, err
|
||||||
|
|
||||||
|
case code == msgpackcodes.Int8 || code == msgpackcodes.Int16 || code == msgpackcodes.Int32 || code == msgpackcodes.Int64:
|
||||||
|
_, err := dec.DecodeInt64()
|
||||||
|
return cty.Number, err
|
||||||
|
|
||||||
|
case code == msgpackcodes.Uint8 || code == msgpackcodes.Uint16 || code == msgpackcodes.Uint32 || code == msgpackcodes.Uint64:
|
||||||
|
_, err := dec.DecodeUint64()
|
||||||
|
return cty.Number, err
|
||||||
|
|
||||||
|
case code == msgpackcodes.Float || code == msgpackcodes.Double:
|
||||||
|
_, err := dec.DecodeFloat64()
|
||||||
|
return cty.Number, err
|
||||||
|
|
||||||
|
case msgpackcodes.IsString(code):
|
||||||
|
_, err := dec.DecodeString()
|
||||||
|
return cty.String, err
|
||||||
|
|
||||||
|
case msgpackcodes.IsFixedMap(code) || code == msgpackcodes.Map16 || code == msgpackcodes.Map32:
|
||||||
|
return impliedObjectType(dec)
|
||||||
|
|
||||||
|
case msgpackcodes.IsFixedArray(code) || code == msgpackcodes.Array16 || code == msgpackcodes.Array32:
|
||||||
|
return impliedTupleType(dec)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return cty.NilType, fmt.Errorf("unsupported msgpack code %#v", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func impliedObjectType(dec *msgpack.Decoder) (cty.Type, error) {
|
||||||
|
// If we get in here then we've already peeked the next code and know
|
||||||
|
// it's some sort of map.
|
||||||
|
l, err := dec.DecodeMapLen()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicPseudoType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var atys map[string]cty.Type
|
||||||
|
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
// Read the map key first. We require maps to be strings, but msgpack
|
||||||
|
// doesn't so we're prepared to error here if not.
|
||||||
|
k, err := dec.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicPseudoType, err
|
||||||
|
}
|
||||||
|
|
||||||
|
aty, err := impliedType(dec)
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicPseudoType, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if atys == nil {
|
||||||
|
atys = make(map[string]cty.Type)
|
||||||
|
}
|
||||||
|
atys[k] = aty
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(atys) == 0 {
|
||||||
|
return cty.EmptyObject, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.Object(atys), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func impliedTupleType(dec *msgpack.Decoder) (cty.Type, error) {
|
||||||
|
// If we get in here then we've already peeked the next code and know
|
||||||
|
// it's some sort of array.
|
||||||
|
l, err := dec.DecodeArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicPseudoType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if l == 0 {
|
||||||
|
return cty.EmptyTuple, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
etys := make([]cty.Type, l)
|
||||||
|
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
ety, err := impliedType(dec)
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicPseudoType, err
|
||||||
|
}
|
||||||
|
etys[i] = ety
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.Tuple(etys), nil
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
type unknownType struct{}
|
||||||
|
|
||||||
|
var unknownVal = unknownType{}
|
||||||
|
|
||||||
|
// unknownValBytes is the raw bytes of the msgpack fixext1 value we
|
||||||
|
// write to represent an unknown value. It's an extension value of
|
||||||
|
// type zero whose value is irrelevant. Since it's irrelevant, we
|
||||||
|
// set it to a single byte whose value is also zero, since that's
|
||||||
|
// the most compact possible representation.
|
||||||
|
var unknownValBytes = []byte{0xd4, 0, 0}
|
||||||
|
|
||||||
|
func (uv unknownType) MarshalMsgpack() ([]byte, error) {
|
||||||
|
return unknownValBytes, nil
|
||||||
|
}
|
|
@ -0,0 +1,311 @@
|
||||||
|
package msgpack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack"
|
||||||
|
msgpackCodes "github.com/vmihailenco/msgpack/codes"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Unmarshal interprets the given bytes as a msgpack-encoded cty Value of
|
||||||
|
// the given type, returning the result.
|
||||||
|
//
|
||||||
|
// If an error is returned, the error is written with a hypothetical
|
||||||
|
// end-user that wrote the msgpack file as its audience, using cty type
|
||||||
|
// system concepts rather than Go type system concepts.
|
||||||
|
func Unmarshal(b []byte, ty cty.Type) (cty.Value, error) {
|
||||||
|
r := bytes.NewReader(b)
|
||||||
|
dec := msgpack.NewDecoder(r)
|
||||||
|
|
||||||
|
var path cty.Path
|
||||||
|
return unmarshal(dec, ty, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshal(dec *msgpack.Decoder, ty cty.Type, path cty.Path) (cty.Value, error) {
|
||||||
|
peek, err := dec.PeekCode()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewError(err)
|
||||||
|
}
|
||||||
|
if msgpackCodes.IsExt(peek) {
|
||||||
|
// We just assume _all_ extensions are unknown values,
|
||||||
|
// since we don't have any other extensions.
|
||||||
|
return cty.UnknownVal(ty), nil
|
||||||
|
}
|
||||||
|
if ty == cty.DynamicPseudoType {
|
||||||
|
return unmarshalDynamic(dec, path)
|
||||||
|
}
|
||||||
|
if peek == msgpackCodes.Nil {
|
||||||
|
return cty.NullVal(ty), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ty.IsPrimitiveType():
|
||||||
|
val, err := unmarshalPrimitive(dec, ty, path)
|
||||||
|
if err != nil {
|
||||||
|
return cty.NilVal, err
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
case ty.IsListType():
|
||||||
|
return unmarshalList(dec, ty.ElementType(), path)
|
||||||
|
case ty.IsSetType():
|
||||||
|
return unmarshalSet(dec, ty.ElementType(), path)
|
||||||
|
case ty.IsMapType():
|
||||||
|
return unmarshalMap(dec, ty.ElementType(), path)
|
||||||
|
case ty.IsTupleType():
|
||||||
|
return unmarshalTuple(dec, ty.TupleElementTypes(), path)
|
||||||
|
case ty.IsObjectType():
|
||||||
|
return unmarshalObject(dec, ty.AttributeTypes(), path)
|
||||||
|
default:
|
||||||
|
return cty.NilVal, path.NewErrorf("unsupported type %s", ty.FriendlyName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalPrimitive(dec *msgpack.Decoder, ty cty.Type, path cty.Path) (cty.Value, error) {
|
||||||
|
switch ty {
|
||||||
|
case cty.Bool:
|
||||||
|
rv, err := dec.DecodeBool()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("bool is required")
|
||||||
|
}
|
||||||
|
return cty.BoolVal(rv), nil
|
||||||
|
case cty.Number:
|
||||||
|
// Marshal will try int and float first, if the value can be
|
||||||
|
// losslessly represented in these encodings, and then fall
|
||||||
|
// back on a string if the number is too large or too precise.
|
||||||
|
peek, err := dec.PeekCode()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("number is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if msgpackCodes.IsFixedNum(peek) {
|
||||||
|
rv, err := dec.DecodeInt64()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("number is required")
|
||||||
|
}
|
||||||
|
return cty.NumberIntVal(rv), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch peek {
|
||||||
|
case msgpackCodes.Int8, msgpackCodes.Int16, msgpackCodes.Int32, msgpackCodes.Int64:
|
||||||
|
rv, err := dec.DecodeInt64()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("number is required")
|
||||||
|
}
|
||||||
|
return cty.NumberIntVal(rv), nil
|
||||||
|
case msgpackCodes.Uint8, msgpackCodes.Uint16, msgpackCodes.Uint32, msgpackCodes.Uint64:
|
||||||
|
rv, err := dec.DecodeUint64()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("number is required")
|
||||||
|
}
|
||||||
|
return cty.NumberUIntVal(rv), nil
|
||||||
|
case msgpackCodes.Float, msgpackCodes.Double:
|
||||||
|
rv, err := dec.DecodeFloat64()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("number is required")
|
||||||
|
}
|
||||||
|
return cty.NumberFloatVal(rv), nil
|
||||||
|
default:
|
||||||
|
rv, err := dec.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("number is required")
|
||||||
|
}
|
||||||
|
bf := &big.Float{}
|
||||||
|
_, _, err = bf.Parse(rv, 10)
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("number is required")
|
||||||
|
}
|
||||||
|
return cty.NumberVal(bf), nil
|
||||||
|
}
|
||||||
|
case cty.String:
|
||||||
|
rv, err := dec.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("string is required")
|
||||||
|
}
|
||||||
|
return cty.StringVal(rv), nil
|
||||||
|
default:
|
||||||
|
// should never happen
|
||||||
|
panic("unsupported primitive type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalList(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value, error) {
|
||||||
|
length, err := dec.DecodeArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("a list is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
return cty.ListValEmpty(ety), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
vals := make([]cty.Value, 0, length)
|
||||||
|
path = append(path, nil)
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
path[len(path)-1] = cty.IndexStep{
|
||||||
|
Key: cty.NumberIntVal(int64(i)),
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := unmarshal(dec, ety, path)
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vals = append(vals, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.ListVal(vals), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalSet(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value, error) {
|
||||||
|
length, err := dec.DecodeArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("a set is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
return cty.SetValEmpty(ety), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
vals := make([]cty.Value, 0, length)
|
||||||
|
path = append(path, nil)
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
path[len(path)-1] = cty.IndexStep{
|
||||||
|
Key: cty.NumberIntVal(int64(i)),
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := unmarshal(dec, ety, path)
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vals = append(vals, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.SetVal(vals), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalMap(dec *msgpack.Decoder, ety cty.Type, path cty.Path) (cty.Value, error) {
|
||||||
|
length, err := dec.DecodeMapLen()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("a map is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
return cty.MapValEmpty(ety), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
vals := make(map[string]cty.Value, length)
|
||||||
|
path = append(path, nil)
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
key, err := dec.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
path[:len(path)-1].NewErrorf("non-string key in map")
|
||||||
|
}
|
||||||
|
|
||||||
|
path[len(path)-1] = cty.IndexStep{
|
||||||
|
Key: cty.StringVal(key),
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := unmarshal(dec, ety, path)
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vals[key] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.MapVal(vals), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalTuple(dec *msgpack.Decoder, etys []cty.Type, path cty.Path) (cty.Value, error) {
|
||||||
|
length, err := dec.DecodeArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("a tuple is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if length != len(etys) {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("a tuple of length %d is required", len(etys))
|
||||||
|
}
|
||||||
|
|
||||||
|
vals := make([]cty.Value, 0, length)
|
||||||
|
path = append(path, nil)
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
path[len(path)-1] = cty.IndexStep{
|
||||||
|
Key: cty.NumberIntVal(int64(i)),
|
||||||
|
}
|
||||||
|
ety := etys[i]
|
||||||
|
|
||||||
|
val, err := unmarshal(dec, ety, path)
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vals = append(vals, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.TupleVal(vals), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalObject(dec *msgpack.Decoder, atys map[string]cty.Type, path cty.Path) (cty.Value, error) {
|
||||||
|
length, err := dec.DecodeMapLen()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("an object is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if length != len(atys) {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("an object with %d attributes is required", len(atys))
|
||||||
|
}
|
||||||
|
|
||||||
|
vals := make(map[string]cty.Value, length)
|
||||||
|
path = append(path, nil)
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
key, err := dec.DecodeString()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path[:len(path)-1].NewErrorf("all keys must be strings")
|
||||||
|
}
|
||||||
|
|
||||||
|
path[len(path)-1] = cty.IndexStep{
|
||||||
|
Key: cty.StringVal(key),
|
||||||
|
}
|
||||||
|
aty, exists := atys[key]
|
||||||
|
if !exists {
|
||||||
|
return cty.DynamicVal, path.NewErrorf("unsupported attribute")
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := unmarshal(dec, aty, path)
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vals[key] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.ObjectVal(vals), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalDynamic(dec *msgpack.Decoder, path cty.Path) (cty.Value, error) {
|
||||||
|
length, err := dec.DecodeArrayLen()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if length != 2 {
|
||||||
|
return cty.DynamicVal, path.NewErrorf(
|
||||||
|
"dynamic value array must have exactly two elements",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
typeJSON, err := dec.DecodeBytes()
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewError(err)
|
||||||
|
}
|
||||||
|
var ty cty.Type
|
||||||
|
err = (&ty).UnmarshalJSON(typeJSON)
|
||||||
|
if err != nil {
|
||||||
|
return cty.DynamicVal, path.NewError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshal(dec, ty, path)
|
||||||
|
}
|
|
@ -312,12 +312,15 @@
|
||||||
{"path":"github.com/ulikunitz/xz/internal/hash","checksumSHA1":"vjnTkzNrMs5Xj6so/fq0mQ6dT1c=","revision":"0c6b41e72360850ca4f98dc341fd999726ea007f","revisionTime":"2017-06-05T21:53:11Z"},
|
{"path":"github.com/ulikunitz/xz/internal/hash","checksumSHA1":"vjnTkzNrMs5Xj6so/fq0mQ6dT1c=","revision":"0c6b41e72360850ca4f98dc341fd999726ea007f","revisionTime":"2017-06-05T21:53:11Z"},
|
||||||
{"path":"github.com/ulikunitz/xz/internal/xlog","checksumSHA1":"m0pm57ASBK/CTdmC0ppRHO17mBs=","revision":"0c6b41e72360850ca4f98dc341fd999726ea007f","revisionTime":"2017-06-05T21:53:11Z"},
|
{"path":"github.com/ulikunitz/xz/internal/xlog","checksumSHA1":"m0pm57ASBK/CTdmC0ppRHO17mBs=","revision":"0c6b41e72360850ca4f98dc341fd999726ea007f","revisionTime":"2017-06-05T21:53:11Z"},
|
||||||
{"path":"github.com/ulikunitz/xz/lzma","checksumSHA1":"2vZw6zc8xuNlyVz2QKvdlNSZQ1U=","revision":"0c6b41e72360850ca4f98dc341fd999726ea007f","revisionTime":"2017-06-05T21:53:11Z"},
|
{"path":"github.com/ulikunitz/xz/lzma","checksumSHA1":"2vZw6zc8xuNlyVz2QKvdlNSZQ1U=","revision":"0c6b41e72360850ca4f98dc341fd999726ea007f","revisionTime":"2017-06-05T21:53:11Z"},
|
||||||
|
{"path":"github.com/vmihailenco/msgpack","checksumSHA1":"t9A/EE2GhHFPHzK+ksAKgKW9ZC8=","revision":"b5e691b1eb52a28c05e67ab9df303626c095c23b","revisionTime":"2018-06-13T09:15:15Z"},
|
||||||
|
{"path":"github.com/vmihailenco/msgpack/codes","checksumSHA1":"OcTSGT2v7/2saIGq06nDhEZwm8I=","revision":"b5e691b1eb52a28c05e67ab9df303626c095c23b","revisionTime":"2018-06-13T09:15:15Z"},
|
||||||
{"path":"github.com/zclconf/go-cty/cty","checksumSHA1":"Ej+3WWvyjn0xg3aujsyT+yvvmdc=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
{"path":"github.com/zclconf/go-cty/cty","checksumSHA1":"Ej+3WWvyjn0xg3aujsyT+yvvmdc=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
||||||
{"path":"github.com/zclconf/go-cty/cty/convert","checksumSHA1":"1WGUPe776lvMMbaRerAbqOx19nQ=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
{"path":"github.com/zclconf/go-cty/cty/convert","checksumSHA1":"1WGUPe776lvMMbaRerAbqOx19nQ=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
||||||
{"path":"github.com/zclconf/go-cty/cty/function","checksumSHA1":"MyyLCGg3RREMllTJyK6ehZl/dHk=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
{"path":"github.com/zclconf/go-cty/cty/function","checksumSHA1":"MyyLCGg3RREMllTJyK6ehZl/dHk=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
||||||
{"path":"github.com/zclconf/go-cty/cty/function/stdlib","checksumSHA1":"kcTJOuL131/stXJ4U9tC3SASQLs=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
{"path":"github.com/zclconf/go-cty/cty/function/stdlib","checksumSHA1":"kcTJOuL131/stXJ4U9tC3SASQLs=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
||||||
{"path":"github.com/zclconf/go-cty/cty/gocty","checksumSHA1":"tmCzwfNXOEB1sSO7TKVzilb2vjA=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
{"path":"github.com/zclconf/go-cty/cty/gocty","checksumSHA1":"tmCzwfNXOEB1sSO7TKVzilb2vjA=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
||||||
{"path":"github.com/zclconf/go-cty/cty/json","checksumSHA1":"1ApmO+Q33+Oem/3f6BU6sztJWNc=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
{"path":"github.com/zclconf/go-cty/cty/json","checksumSHA1":"1ApmO+Q33+Oem/3f6BU6sztJWNc=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
||||||
|
{"path":"github.com/zclconf/go-cty/cty/msgpack","checksumSHA1":"8H+2pufGi2Eo3d8cXFfJs31wk+I=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
||||||
{"path":"github.com/zclconf/go-cty/cty/set","checksumSHA1":"y5Sk+n6SOspFj8mlyb8swr4DMIs=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
{"path":"github.com/zclconf/go-cty/cty/set","checksumSHA1":"y5Sk+n6SOspFj8mlyb8swr4DMIs=","revision":"02bd58e97b5759d478019c5a6333edbfdfed16a0","revisionTime":"2018-07-18T22:05:26Z"},
|
||||||
{"path":"go4.org/errorutil","checksumSHA1":"PMr/a5kcnC4toJtVwWhlU5E4tJY=","revision":"034d17a462f7b2dcd1a4a73553ec5357ff6e6c6e","revisionTime":"2017-05-24T23:16:39Z"},
|
{"path":"go4.org/errorutil","checksumSHA1":"PMr/a5kcnC4toJtVwWhlU5E4tJY=","revision":"034d17a462f7b2dcd1a4a73553ec5357ff6e6c6e","revisionTime":"2017-05-24T23:16:39Z"},
|
||||||
{"path":"golang.org/x/crypto/blake2b","checksumSHA1":"iBXpCGzDbbbm0sWfaOY3aKDJ31U=","revision":"a49355c7e3f8fe157a85be2f77e6e269a0f89602","revisionTime":"2018-06-20T09:14:27Z"},
|
{"path":"golang.org/x/crypto/blake2b","checksumSHA1":"iBXpCGzDbbbm0sWfaOY3aKDJ31U=","revision":"a49355c7e3f8fe157a85be2f77e6e269a0f89602","revisionTime":"2018-06-20T09:14:27Z"},
|
||||||
|
|
Loading…
Reference in New Issue