* vendor update: go-gitlab to v0.31.0 * migrate client init to v0.31.0 * refactorfor-closed-social
@ -0,0 +1,324 @@ | |||
// Copyright 2019 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package proto | |||
import ( | |||
"errors" | |||
"fmt" | |||
"google.golang.org/protobuf/encoding/prototext" | |||
"google.golang.org/protobuf/encoding/protowire" | |||
"google.golang.org/protobuf/runtime/protoimpl" | |||
) | |||
const ( | |||
WireVarint = 0 | |||
WireFixed32 = 5 | |||
WireFixed64 = 1 | |||
WireBytes = 2 | |||
WireStartGroup = 3 | |||
WireEndGroup = 4 | |||
) | |||
// EncodeVarint returns the varint encoded bytes of v. | |||
func EncodeVarint(v uint64) []byte { | |||
return protowire.AppendVarint(nil, v) | |||
} | |||
// SizeVarint returns the length of the varint encoded bytes of v. | |||
// This is equal to len(EncodeVarint(v)). | |||
func SizeVarint(v uint64) int { | |||
return protowire.SizeVarint(v) | |||
} | |||
// DecodeVarint parses a varint encoded integer from b, returning the | |||
// integer value and the length of the varint. | |||
// It returns (0, 0) if there is a parse error. | |||
func DecodeVarint(b []byte) (uint64, int) { | |||
v, n := protowire.ConsumeVarint(b) | |||
if n < 0 { | |||
return 0, 0 | |||
} | |||
return v, n | |||
} | |||
// Buffer is a buffer for encoding and decoding the protobuf wire format. | |||
// It may be reused between invocations to reduce memory usage. | |||
type Buffer struct { | |||
buf []byte | |||
idx int | |||
deterministic bool | |||
} | |||
// NewBuffer allocates a new Buffer initialized with buf, | |||
// where the contents of buf are considered the unread portion of the buffer. | |||
func NewBuffer(buf []byte) *Buffer { | |||
return &Buffer{buf: buf} | |||
} | |||
// SetDeterministic specifies whether to use deterministic serialization. | |||
// | |||
// Deterministic serialization guarantees that for a given binary, equal | |||
// messages will always be serialized to the same bytes. This implies: | |||
// | |||
// - Repeated serialization of a message will return the same bytes. | |||
// - Different processes of the same binary (which may be executing on | |||
// different machines) will serialize equal messages to the same bytes. | |||
// | |||
// Note that the deterministic serialization is NOT canonical across | |||
// languages. It is not guaranteed to remain stable over time. It is unstable | |||
// across different builds with schema changes due to unknown fields. | |||
// Users who need canonical serialization (e.g., persistent storage in a | |||
// canonical form, fingerprinting, etc.) should define their own | |||
// canonicalization specification and implement their own serializer rather | |||
// than relying on this API. | |||
// | |||
// If deterministic serialization is requested, map entries will be sorted | |||
// by keys in lexographical order. This is an implementation detail and | |||
// subject to change. | |||
func (b *Buffer) SetDeterministic(deterministic bool) { | |||
b.deterministic = deterministic | |||
} | |||
// SetBuf sets buf as the internal buffer, | |||
// where the contents of buf are considered the unread portion of the buffer. | |||
func (b *Buffer) SetBuf(buf []byte) { | |||
b.buf = buf | |||
b.idx = 0 | |||
} | |||
// Reset clears the internal buffer of all written and unread data. | |||
func (b *Buffer) Reset() { | |||
b.buf = b.buf[:0] | |||
b.idx = 0 | |||
} | |||
// Bytes returns the internal buffer. | |||
func (b *Buffer) Bytes() []byte { | |||
return b.buf | |||
} | |||
// Unread returns the unread portion of the buffer. | |||
func (b *Buffer) Unread() []byte { | |||
return b.buf[b.idx:] | |||
} | |||
// Marshal appends the wire-format encoding of m to the buffer. | |||
func (b *Buffer) Marshal(m Message) error { | |||
var err error | |||
b.buf, err = marshalAppend(b.buf, m, b.deterministic) | |||
return err | |||
} | |||
// Unmarshal parses the wire-format message in the buffer and places the decoded results in m. | |||
// | |||
// Unlike proto.Unmarshal, this does not reset the message before starting to unmarshal. | |||
func (b *Buffer) Unmarshal(m Message) error { | |||
err := UnmarshalMerge(b.Unread(), m) | |||
b.idx = len(b.buf) | |||
return err | |||
} | |||
type unknownFields struct{ XXX_unrecognized protoimpl.UnknownFields } | |||
func (m *unknownFields) String() string { panic("not implemented") } | |||
func (m *unknownFields) Reset() { panic("not implemented") } | |||
func (m *unknownFields) ProtoMessage() { panic("not implemented") } | |||
// DebugPrint dumps the encoded bytes of b with a header and footer including s | |||
// to stdout. This is only intended for debugging. | |||
func (*Buffer) DebugPrint(s string, b []byte) { | |||
m := MessageReflect(new(unknownFields)) | |||
m.SetUnknown(b) | |||
b, _ = prototext.MarshalOptions{AllowPartial: true, Indent: "\t"}.Marshal(m.Interface()) | |||
fmt.Printf("==== %s ====\n%s==== %s ====\n", s, b, s) | |||
} | |||
// EncodeVarint appends an unsigned varint encoding to the buffer. | |||
func (b *Buffer) EncodeVarint(v uint64) error { | |||
b.buf = protowire.AppendVarint(b.buf, v) | |||
return nil | |||
} | |||
// EncodeZigzag32 appends a 32-bit zig-zag varint encoding to the buffer. | |||
func (b *Buffer) EncodeZigzag32(v uint64) error { | |||
return b.EncodeVarint(uint64((uint32(v) << 1) ^ uint32((int32(v) >> 31)))) | |||
} | |||
// EncodeZigzag64 appends a 64-bit zig-zag varint encoding to the buffer. | |||
func (b *Buffer) EncodeZigzag64(v uint64) error { | |||
return b.EncodeVarint(uint64((uint64(v) << 1) ^ uint64((int64(v) >> 63)))) | |||
} | |||
// EncodeFixed32 appends a 32-bit little-endian integer to the buffer. | |||
func (b *Buffer) EncodeFixed32(v uint64) error { | |||
b.buf = protowire.AppendFixed32(b.buf, uint32(v)) | |||
return nil | |||
} | |||
// EncodeFixed64 appends a 64-bit little-endian integer to the buffer. | |||
func (b *Buffer) EncodeFixed64(v uint64) error { | |||
b.buf = protowire.AppendFixed64(b.buf, uint64(v)) | |||
return nil | |||
} | |||
// EncodeRawBytes appends a length-prefixed raw bytes to the buffer. | |||
func (b *Buffer) EncodeRawBytes(v []byte) error { | |||
b.buf = protowire.AppendBytes(b.buf, v) | |||
return nil | |||
} | |||
// EncodeStringBytes appends a length-prefixed raw bytes to the buffer. | |||
// It does not validate whether v contains valid UTF-8. | |||
func (b *Buffer) EncodeStringBytes(v string) error { | |||
b.buf = protowire.AppendString(b.buf, v) | |||
return nil | |||
} | |||
// EncodeMessage appends a length-prefixed encoded message to the buffer. | |||
func (b *Buffer) EncodeMessage(m Message) error { | |||
var err error | |||
b.buf = protowire.AppendVarint(b.buf, uint64(Size(m))) | |||
b.buf, err = marshalAppend(b.buf, m, b.deterministic) | |||
return err | |||
} | |||
// DecodeVarint consumes an encoded unsigned varint from the buffer. | |||
func (b *Buffer) DecodeVarint() (uint64, error) { | |||
v, n := protowire.ConsumeVarint(b.buf[b.idx:]) | |||
if n < 0 { | |||
return 0, protowire.ParseError(n) | |||
} | |||
b.idx += n | |||
return uint64(v), nil | |||
} | |||
// DecodeZigzag32 consumes an encoded 32-bit zig-zag varint from the buffer. | |||
func (b *Buffer) DecodeZigzag32() (uint64, error) { | |||
v, err := b.DecodeVarint() | |||
if err != nil { | |||
return 0, err | |||
} | |||
return uint64((uint32(v) >> 1) ^ uint32((int32(v&1)<<31)>>31)), nil | |||
} | |||
// DecodeZigzag64 consumes an encoded 64-bit zig-zag varint from the buffer. | |||
func (b *Buffer) DecodeZigzag64() (uint64, error) { | |||
v, err := b.DecodeVarint() | |||
if err != nil { | |||
return 0, err | |||
} | |||
return uint64((uint64(v) >> 1) ^ uint64((int64(v&1)<<63)>>63)), nil | |||
} | |||
// DecodeFixed32 consumes a 32-bit little-endian integer from the buffer. | |||
func (b *Buffer) DecodeFixed32() (uint64, error) { | |||
v, n := protowire.ConsumeFixed32(b.buf[b.idx:]) | |||
if n < 0 { | |||
return 0, protowire.ParseError(n) | |||
} | |||
b.idx += n | |||
return uint64(v), nil | |||
} | |||
// DecodeFixed64 consumes a 64-bit little-endian integer from the buffer. | |||
func (b *Buffer) DecodeFixed64() (uint64, error) { | |||
v, n := protowire.ConsumeFixed64(b.buf[b.idx:]) | |||
if n < 0 { | |||
return 0, protowire.ParseError(n) | |||
} | |||
b.idx += n | |||
return uint64(v), nil | |||
} | |||
// DecodeRawBytes consumes a length-prefixed raw bytes from the buffer. | |||
// If alloc is specified, it returns a copy the raw bytes | |||
// rather than a sub-slice of the buffer. | |||
func (b *Buffer) DecodeRawBytes(alloc bool) ([]byte, error) { | |||
v, n := protowire.ConsumeBytes(b.buf[b.idx:]) | |||
if n < 0 { | |||
return nil, protowire.ParseError(n) | |||
} | |||
b.idx += n | |||
if alloc { | |||
v = append([]byte(nil), v...) | |||
} | |||
return v, nil | |||
} | |||
// DecodeStringBytes consumes a length-prefixed raw bytes from the buffer. | |||
// It does not validate whether the raw bytes contain valid UTF-8. | |||
func (b *Buffer) DecodeStringBytes() (string, error) { | |||
v, n := protowire.ConsumeString(b.buf[b.idx:]) | |||
if n < 0 { | |||
return "", protowire.ParseError(n) | |||
} | |||
b.idx += n | |||
return v, nil | |||
} | |||
// DecodeMessage consumes a length-prefixed message from the buffer. | |||
// It does not reset m. | |||
func (b *Buffer) DecodeMessage(m Message) error { | |||
v, err := b.DecodeRawBytes(false) | |||
if err != nil { | |||
return err | |||
} | |||
return UnmarshalMerge(v, m) | |||
} | |||
// DecodeGroup consumes a message group from the buffer. | |||
// It assumes that the start group marker has already been consumed and | |||
// consumes all bytes until (and including the end group marker). | |||
// It does not reset m. | |||
func (b *Buffer) DecodeGroup(m Message) error { | |||
v, n, err := consumeGroup(b.buf[b.idx:]) | |||
if err != nil { | |||
return err | |||
} | |||
b.idx += n | |||
return UnmarshalMerge(v, m) | |||
} | |||
// consumeGroup parses b until it finds an end group marker, returning | |||
// the raw bytes of the message (excluding the end group marker) and the | |||
// the total length of the message (including the end group marker). | |||
func consumeGroup(b []byte) ([]byte, int, error) { | |||
b0 := b | |||
depth := 1 // assume this follows a start group marker | |||
for { | |||
_, wtyp, tagLen := protowire.ConsumeTag(b) | |||
if tagLen < 0 { | |||
return nil, 0, protowire.ParseError(tagLen) | |||
} | |||
b = b[tagLen:] | |||
var valLen int | |||
switch wtyp { | |||
case protowire.VarintType: | |||
_, valLen = protowire.ConsumeVarint(b) | |||
case protowire.Fixed32Type: | |||
_, valLen = protowire.ConsumeFixed32(b) | |||
case protowire.Fixed64Type: | |||
_, valLen = protowire.ConsumeFixed64(b) | |||
case protowire.BytesType: | |||
_, valLen = protowire.ConsumeBytes(b) | |||
case protowire.StartGroupType: | |||
depth++ | |||
case protowire.EndGroupType: | |||
depth-- | |||
default: | |||
return nil, 0, errors.New("proto: cannot parse reserved wire type") | |||
} | |||
if valLen < 0 { | |||
return nil, 0, protowire.ParseError(valLen) | |||
} | |||
b = b[valLen:] | |||
if depth == 0 { | |||
return b0[:len(b0)-len(b)-tagLen], len(b0) - len(b), nil | |||
} | |||
} | |||
} |
@ -1,253 +0,0 @@ | |||
// Go support for Protocol Buffers - Google's data interchange format | |||
// | |||
// Copyright 2011 The Go Authors. All rights reserved. | |||
// https://github.com/golang/protobuf | |||
// | |||
// 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. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// 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. | |||
// Protocol buffer deep copy and merge. | |||
// TODO: RawMessage. | |||
package proto | |||
import ( | |||
"fmt" | |||
"log" | |||
"reflect" | |||
"strings" | |||
) | |||
// Clone returns a deep copy of a protocol buffer. | |||
func Clone(src Message) Message { | |||
in := reflect.ValueOf(src) | |||
if in.IsNil() { | |||
return src | |||
} | |||
out := reflect.New(in.Type().Elem()) | |||
dst := out.Interface().(Message) | |||
Merge(dst, src) | |||
return dst | |||
} | |||
// Merger is the interface representing objects that can merge messages of the same type. | |||
type Merger interface { | |||
// Merge merges src into this message. | |||
// Required and optional fields that are set in src will be set to that value in dst. | |||
// Elements of repeated fields will be appended. | |||
// | |||
// Merge may panic if called with a different argument type than the receiver. | |||
Merge(src Message) | |||
} | |||
// generatedMerger is the custom merge method that generated protos will have. | |||
// We must add this method since a generate Merge method will conflict with | |||
// many existing protos that have a Merge data field already defined. | |||
type generatedMerger interface { | |||
XXX_Merge(src Message) | |||
} | |||
// Merge merges src into dst. | |||
// Required and optional fields that are set in src will be set to that value in dst. | |||
// Elements of repeated fields will be appended. | |||
// Merge panics if src and dst are not the same type, or if dst is nil. | |||
func Merge(dst, src Message) { | |||
if m, ok := dst.(Merger); ok { | |||
m.Merge(src) | |||
return | |||
} | |||
in := reflect.ValueOf(src) | |||
out := reflect.ValueOf(dst) | |||
if out.IsNil() { | |||
panic("proto: nil destination") | |||
} | |||
if in.Type() != out.Type() { | |||
panic(fmt.Sprintf("proto.Merge(%T, %T) type mismatch", dst, src)) | |||
} | |||
if in.IsNil() { | |||
return // Merge from nil src is a noop | |||
} | |||
if m, ok := dst.(generatedMerger); ok { | |||
m.XXX_Merge(src) | |||
return | |||
} | |||
mergeStruct(out.Elem(), in.Elem()) | |||
} | |||
func mergeStruct(out, in reflect.Value) { | |||
sprop := GetProperties(in.Type()) | |||
for i := 0; i < in.NumField(); i++ { | |||
f := in.Type().Field(i) | |||
if strings.HasPrefix(f.Name, "XXX_") { | |||
continue | |||
} | |||
mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i]) | |||
} | |||
if emIn, err := extendable(in.Addr().Interface()); err == nil { | |||
emOut, _ := extendable(out.Addr().Interface()) | |||
mIn, muIn := emIn.extensionsRead() | |||
if mIn != nil { | |||
mOut := emOut.extensionsWrite() | |||
muIn.Lock() | |||
mergeExtension(mOut, mIn) | |||
muIn.Unlock() | |||
} | |||
} | |||
uf := in.FieldByName("XXX_unrecognized") | |||
if !uf.IsValid() { | |||
return | |||
} | |||
uin := uf.Bytes() | |||
if len(uin) > 0 { | |||
out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...)) | |||
} | |||
} | |||
// mergeAny performs a merge between two values of the same type. | |||
// viaPtr indicates whether the values were indirected through a pointer (implying proto2). | |||
// prop is set if this is a struct field (it may be nil). | |||
func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) { | |||
if in.Type() == protoMessageType { | |||
if !in.IsNil() { | |||
if out.IsNil() { | |||
out.Set(reflect.ValueOf(Clone(in.Interface().(Message)))) | |||
} else { | |||
Merge(out.Interface().(Message), in.Interface().(Message)) | |||
} | |||
} | |||
return | |||
} | |||
switch in.Kind() { | |||
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, | |||
reflect.String, reflect.Uint32, reflect.Uint64: | |||
if !viaPtr && isProto3Zero(in) { | |||
return | |||
} | |||
out.Set(in) | |||
case reflect.Interface: | |||
// Probably a oneof field; copy non-nil values. | |||
if in.IsNil() { | |||
return | |||
} | |||
// Allocate destination if it is not set, or set to a different type. | |||
// Otherwise we will merge as normal. | |||
if out.IsNil() || out.Elem().Type() != in.Elem().Type() { | |||
out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T) | |||
} | |||
mergeAny(out.Elem(), in.Elem(), false, nil) | |||
case reflect.Map: | |||
if in.Len() == 0 { | |||
return | |||
} | |||
if out.IsNil() { | |||
out.Set(reflect.MakeMap(in.Type())) | |||
} | |||
// For maps with value types of *T or []byte we need to deep copy each value. | |||
elemKind := in.Type().Elem().Kind() | |||
for _, key := range in.MapKeys() { | |||
var val reflect.Value | |||
switch elemKind { | |||
case reflect.Ptr: | |||
val = reflect.New(in.Type().Elem().Elem()) | |||
mergeAny(val, in.MapIndex(key), false, nil) | |||
case reflect.Slice: | |||
val = in.MapIndex(key) | |||
val = reflect.ValueOf(append([]byte{}, val.Bytes()...)) | |||
default: | |||
val = in.MapIndex(key) | |||
} | |||
out.SetMapIndex(key, val) | |||
} | |||
case reflect.Ptr: | |||
if in.IsNil() { | |||
return | |||
} | |||
if out.IsNil() { | |||
out.Set(reflect.New(in.Elem().Type())) | |||
} | |||
mergeAny(out.Elem(), in.Elem(), true, nil) | |||
case reflect.Slice: | |||
if in.IsNil() { | |||
return | |||
} | |||
if in.Type().Elem().Kind() == reflect.Uint8 { | |||
// []byte is a scalar bytes field, not a repeated field. | |||
// Edge case: if this is in a proto3 message, a zero length | |||
// bytes field is considered the zero value, and should not | |||
// be merged. | |||
if prop != nil && prop.proto3 && in.Len() == 0 { | |||
return | |||
} | |||
// Make a deep copy. | |||
// Append to []byte{} instead of []byte(nil) so that we never end up | |||
// with a nil result. | |||
out.SetBytes(append([]byte{}, in.Bytes()...)) | |||
return | |||
} | |||
n := in.Len() | |||
if out.IsNil() { | |||
out.Set(reflect.MakeSlice(in.Type(), 0, n)) | |||
} | |||
switch in.Type().Elem().Kind() { | |||
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, | |||
reflect.String, reflect.Uint32, reflect.Uint64: | |||
out.Set(reflect.AppendSlice(out, in)) | |||
default: | |||
for i := 0; i < n; i++ { | |||
x := reflect.Indirect(reflect.New(in.Type().Elem())) | |||
mergeAny(x, in.Index(i), false, nil) | |||
out.Set(reflect.Append(out, x)) | |||
} | |||
} | |||
case reflect.Struct: | |||
mergeStruct(out, in) | |||
default: | |||
// unknown type, so not a protocol buffer | |||
log.Printf("proto: don't know how to copy %v", in) | |||
} | |||
} | |||
func mergeExtension(out, in map[int32]Extension) { | |||
for extNum, eIn := range in { | |||
eOut := Extension{desc: eIn.desc} | |||
if eIn.value != nil { | |||
v := reflect.New(reflect.TypeOf(eIn.value)).Elem() | |||
mergeAny(v, reflect.ValueOf(eIn.value), false, nil) | |||
eOut.value = v.Interface() | |||
} | |||
if eIn.enc != nil { | |||
eOut.enc = make([]byte, len(eIn.enc)) | |||
copy(eOut.enc, eIn.enc) | |||
} | |||
out[extNum] = eOut | |||
} | |||
} |
@ -1,427 +0,0 @@ | |||
// Go support for Protocol Buffers - Google's data interchange format | |||
// | |||
// Copyright 2010 The Go Authors. All rights reserved. | |||
// https://github.com/golang/protobuf | |||
// | |||
// 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. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// 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. | |||
package proto | |||
/* | |||
* Routines for decoding protocol buffer data to construct in-memory representations. | |||
*/ | |||
import ( | |||
"errors" | |||
"fmt" | |||
"io" | |||
) | |||
// errOverflow is returned when an integer is too large to be represented. | |||
var errOverflow = errors.New("proto: integer overflow") | |||
// ErrInternalBadWireType is returned by generated code when an incorrect | |||
// wire type is encountered. It does not get returned to user code. | |||
var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof") | |||
// DecodeVarint reads a varint-encoded integer from the slice. | |||
// It returns the integer and the number of bytes consumed, or | |||
// zero if there is not enough. | |||
// This is the format for the | |||
// int32, int64, uint32, uint64, bool, and enum | |||
// protocol buffer types. | |||
func DecodeVarint(buf []byte) (x uint64, n int) { | |||
for shift := uint(0); shift < 64; shift += 7 { | |||
if n >= len(buf) { | |||
return 0, 0 | |||
} | |||
b := uint64(buf[n]) | |||
n++ | |||
x |= (b & 0x7F) << shift | |||
if (b & 0x80) == 0 { | |||
return x, n | |||
} | |||
} | |||
// The number is too large to represent in a 64-bit value. | |||
return 0, 0 | |||
} | |||
func (p *Buffer) decodeVarintSlow() (x uint64, err error) { | |||
i := p.index | |||
l := len(p.buf) | |||
for shift := uint(0); shift < 64; shift += 7 { | |||
if i >= l { | |||
err = io.ErrUnexpectedEOF | |||
return | |||
} | |||
b := p.buf[i] | |||
i++ | |||
x |= (uint64(b) & 0x7F) << shift | |||
if b < 0x80 { | |||
p.index = i | |||
return | |||
} | |||
} | |||
// The number is too large to represent in a 64-bit value. | |||
err = errOverflow | |||
return | |||
} | |||
// DecodeVarint reads a varint-encoded integer from the Buffer. | |||
// This is the format for the | |||
// int32, int64, uint32, uint64, bool, and enum | |||
// protocol buffer types. | |||
func (p *Buffer) DecodeVarint() (x uint64, err error) { | |||
i := p.index | |||
buf := p.buf | |||
if i >= len(buf) { | |||
return 0, io.ErrUnexpectedEOF | |||
} else if buf[i] < 0x80 { | |||
p.index++ | |||
return uint64(buf[i]), nil | |||
} else if len(buf)-i < 10 { | |||
return p.decodeVarintSlow() | |||
} | |||
var b uint64 | |||
// we already checked the first byte | |||
x = uint64(buf[i]) - 0x80 | |||
i++ | |||
b = uint64(buf[i]) | |||
i++ | |||
x += b << 7 | |||
if b&0x80 == 0 { | |||
goto done | |||
} | |||
x -= 0x80 << 7 | |||
b = uint64(buf[i]) | |||
i++ | |||
x += b << 14 | |||
if b&0x80 == 0 { | |||
goto done | |||
} | |||
x -= 0x80 << 14 | |||
b = uint64(buf[i]) | |||
i++ | |||
x += b << 21 | |||
if b&0x80 == 0 { | |||
goto done | |||
} | |||
x -= 0x80 << 21 | |||
b = uint64(buf[i]) | |||
i++ | |||
x += b << 28 | |||
if b&0x80 == 0 { | |||
goto done | |||
} | |||
x -= 0x80 << 28 | |||
b = uint64(buf[i]) | |||
i++ | |||
x += b << 35 | |||
if b&0x80 == 0 { | |||
goto done | |||
} | |||
x -= 0x80 << 35 | |||
b = uint64(buf[i]) | |||
i++ | |||
x += b << 42 | |||
if b&0x80 == 0 { | |||
goto done | |||
} | |||
x -= 0x80 << 42 | |||
b = uint64(buf[i]) | |||
i++ | |||
x += b << 49 | |||
if b&0x80 == 0 { | |||
goto done | |||
} | |||
x -= 0x80 << 49 | |||
b = uint64(buf[i]) | |||
i++ | |||
x += b << 56 | |||
if b&0x80 == 0 { | |||
goto done | |||
} | |||
x -= 0x80 << 56 | |||
b = uint64(buf[i]) | |||
i++ | |||
x += b << 63 | |||
if b&0x80 == 0 { | |||
goto done | |||
} | |||
return 0, errOverflow | |||
done: | |||
p.index = i | |||
return x, nil | |||
} | |||
// DecodeFixed64 reads a 64-bit integer from the Buffer. | |||
// This is the format for the | |||
// fixed64, sfixed64, and double protocol buffer types. | |||
func (p *Buffer) DecodeFixed64() (x uint64, err error) { | |||
// x, err already 0 | |||
i := p.index + 8 | |||
if i < 0 || i > len(p.buf) { | |||
err = io.ErrUnexpectedEOF | |||
return | |||
} | |||
p.index = i | |||
x = uint64(p.buf[i-8]) | |||
x |= uint64(p.buf[i-7]) << 8 | |||
x |= uint64(p.buf[i-6]) << 16 | |||
x |= uint64(p.buf[i-5]) << 24 | |||
x |= uint64(p.buf[i-4]) << 32 | |||
x |= uint64(p.buf[i-3]) << 40 | |||
x |= uint64(p.buf[i-2]) << 48 | |||
x |= uint64(p.buf[i-1]) << 56 | |||
return | |||
} | |||
// DecodeFixed32 reads a 32-bit integer from the Buffer. | |||
// This is the format for the | |||
// fixed32, sfixed32, and float protocol buffer types. | |||
func (p *Buffer) DecodeFixed32() (x uint64, err error) { | |||
// x, err already 0 | |||
i := p.index + 4 | |||
if i < 0 || i > len(p.buf) { | |||
err = io.ErrUnexpectedEOF | |||
return | |||
} | |||
p.index = i | |||
x = uint64(p.buf[i-4]) | |||
x |= uint64(p.buf[i-3]) << 8 | |||
x |= uint64(p.buf[i-2]) << 16 | |||
x |= uint64(p.buf[i-1]) << 24 | |||
return | |||
} | |||
// DecodeZigzag64 reads a zigzag-encoded 64-bit integer | |||
// from the Buffer. | |||
// This is the format used for the sint64 protocol buffer type. | |||
func (p *Buffer) DecodeZigzag64() (x uint64, err error) { | |||
x, err = p.DecodeVarint() | |||
if err != nil { | |||
return | |||
} | |||
x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63) | |||
return | |||
} | |||
// DecodeZigzag32 reads a zigzag-encoded 32-bit integer | |||
// from the Buffer. | |||
// This is the format used for the sint32 protocol buffer type. | |||
func (p *Buffer) DecodeZigzag32() (x uint64, err error) { | |||
x, err = p.DecodeVarint() | |||
if err != nil { | |||
return | |||
} | |||
x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31)) | |||
return | |||
} | |||
// DecodeRawBytes reads a count-delimited byte buffer from the Buffer. | |||
// This is the format used for the bytes protocol buffer | |||
// type and for embedded messages. | |||
func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) { | |||
n, err := p.DecodeVarint() | |||
if err != nil { | |||
return nil, err | |||
} | |||
nb := int(n) | |||
if nb < 0 { | |||
return nil, fmt.Errorf("proto: bad byte length %d", nb) | |||
} | |||
end := p.index + nb | |||
if end < p.index || end > len(p.buf) { | |||
return nil, io.ErrUnexpectedEOF | |||
} | |||
if !alloc { | |||
// todo: check if can get more uses of alloc=false | |||
buf = p.buf[p.index:end] | |||
p.index += nb | |||
return | |||
} | |||
buf = make([]byte, nb) | |||
copy(buf, p.buf[p.index:]) | |||
p.index += nb | |||
return | |||
} | |||
// DecodeStringBytes reads an encoded string from the Buffer. | |||
// This is the format used for the proto2 string type. | |||
func (p *Buffer) DecodeStringBytes() (s string, err error) { | |||
buf, err := p.DecodeRawBytes(false) | |||
if err != nil { | |||
return | |||
} | |||
return string(buf), nil | |||
} | |||
// Unmarshaler is the interface representing objects that can | |||
// unmarshal themselves. The argument points to data that may be | |||
// overwritten, so implementations should not keep references to the | |||
// buffer. | |||
// Unmarshal implementations should not clear the receiver. | |||
// Any unmarshaled data should be merged into the receiver. | |||
// Callers of Unmarshal that do not want to retain existing data | |||
// should Reset the receiver before calling Unmarshal. | |||
type Unmarshaler interface { | |||
Unmarshal([]byte) error | |||
} | |||
// newUnmarshaler is the interface representing objects that can | |||
// unmarshal themselves. The semantics are identical to Unmarshaler. | |||
// | |||
// This exists to support protoc-gen-go generated messages. | |||
// The proto package will stop type-asserting to this interface in the future. | |||
// | |||
// DO NOT DEPEND ON THIS. | |||
type newUnmarshaler interface { | |||
XXX_Unmarshal([]byte) error | |||
} | |||
// Unmarshal parses the protocol buffer representation in buf and places the | |||
// decoded result in pb. If the struct underlying pb does not match | |||
// the data in buf, the results can be unpredictable. | |||
// | |||
// Unmarshal resets pb before starting to unmarshal, so any | |||
// existing data in pb is always removed. Use UnmarshalMerge | |||
// to preserve and append to existing data. | |||
func Unmarshal(buf []byte, pb Message) error { | |||
pb.Reset() | |||
if u, ok := pb.(newUnmarshaler); ok { | |||
return u.XXX_Unmarshal(buf) | |||
} | |||
if u, ok := pb.(Unmarshaler); ok { | |||
return u.Unmarshal(buf) | |||
} | |||
return NewBuffer(buf).Unmarshal(pb) | |||
} | |||
// UnmarshalMerge parses the protocol buffer representation in buf and | |||
// writes the decoded result to pb. If the struct underlying pb does not match | |||
// the data in buf, the results can be unpredictable. | |||
// | |||
// UnmarshalMerge merges into existing data in pb. | |||
// Most code should use Unmarshal instead. | |||
func UnmarshalMerge(buf []byte, pb Message) error { | |||
if u, ok := pb.(newUnmarshaler); ok { | |||
return u.XXX_Unmarshal(buf) | |||
} | |||
if u, ok := pb.(Unmarshaler); ok { | |||
// NOTE: The history of proto have unfortunately been inconsistent | |||
// whether Unmarshaler should or should not implicitly clear itself. | |||
// Some implementations do, most do not. | |||
// Thus, calling this here may or may not do what people want. | |||
// | |||
// See https://github.com/golang/protobuf/issues/424 | |||
return u.Unmarshal(buf) | |||
} | |||
return NewBuffer(buf).Unmarshal(pb) | |||
} | |||
// DecodeMessage reads a count-delimited message from the Buffer. | |||
func (p *Buffer) DecodeMessage(pb Message) error { | |||
enc, err := p.DecodeRawBytes(false) | |||
if err != nil { | |||
return err | |||
} | |||
return NewBuffer(enc).Unmarshal(pb) | |||
} | |||
// DecodeGroup reads a tag-delimited group from the Buffer. | |||
// StartGroup tag is already consumed. This function consumes | |||
// EndGroup tag. | |||
func (p *Buffer) DecodeGroup(pb Message) error { | |||
b := p.buf[p.index:] | |||
x, y := findEndGroup(b) | |||
if x < 0 { | |||
return io.ErrUnexpectedEOF | |||
} | |||
err := Unmarshal(b[:x], pb) | |||
p.index += y | |||
return err | |||
} | |||
// Unmarshal parses the protocol buffer representation in the | |||
// Buffer and places the decoded result in pb. If the struct | |||
// underlying pb does not match the data in the buffer, the results can be | |||
// unpredictable. | |||
// | |||
// Unlike proto.Unmarshal, this does not reset pb before starting to unmarshal. | |||
func (p *Buffer) Unmarshal(pb Message) error { | |||
// If the object can unmarshal itself, let it. | |||
if u, ok := pb.(newUnmarshaler); ok { | |||
err := u.XXX_Unmarshal(p.buf[p.index:]) | |||
p.index = len(p.buf) | |||
return err | |||
} | |||
if u, ok := pb.(Unmarshaler); ok { | |||
// NOTE: The history of proto have unfortunately been inconsistent | |||
// whether Unmarshaler should or should not implicitly clear itself. | |||
// Some implementations do, most do not. | |||
// Thus, calling this here may or may not do what people want. | |||
// | |||
// See https://github.com/golang/protobuf/issues/424 | |||
err := u.Unmarshal(p.buf[p.index:]) | |||
p.index = len(p.buf) | |||
return err | |||
} | |||
// Slow workaround for messages that aren't Unmarshalers. | |||
// This includes some hand-coded .pb.go files and | |||
// bootstrap protos. | |||
// TODO: fix all of those and then add Unmarshal to | |||
// the Message interface. Then: | |||
// The cast above and code below can be deleted. | |||
// The old unmarshaler can be deleted. | |||
// Clients can call Unmarshal directly (can already do that, actually). | |||
var info InternalMessageInfo | |||
err := info.Unmarshal(pb, p.buf[p.index:]) | |||
p.index = len(p.buf) | |||
return err | |||
} |
@ -0,0 +1,63 @@ | |||
// Copyright 2019 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package proto | |||
import ( | |||
"google.golang.org/protobuf/reflect/protoreflect" | |||
) | |||
// SetDefaults sets unpopulated scalar fields to their default values. | |||
// Fields within a oneof are not set even if they have a default value. | |||
// SetDefaults is recursively called upon any populated message fields. | |||
func SetDefaults(m Message) { | |||
if m != nil { | |||
setDefaults(MessageReflect(m)) | |||
} | |||
} | |||
func setDefaults(m protoreflect.Message) { | |||
fds := m.Descriptor().Fields() | |||
for i := 0; i < fds.Len(); i++ { | |||
fd := fds.Get(i) | |||
if !m.Has(fd) { | |||
if fd.HasDefault() && fd.ContainingOneof() == nil { | |||
v := fd.Default() | |||
if fd.Kind() == protoreflect.BytesKind { | |||
v = protoreflect.ValueOf(append([]byte(nil), v.Bytes()...)) // copy the default bytes | |||
} | |||
m.Set(fd, v) | |||
} | |||
continue | |||
} | |||
} | |||
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { | |||
switch { | |||
// Handle singular message. | |||
case fd.Cardinality() != protoreflect.Repeated: | |||
if fd.Message() != nil { | |||
setDefaults(m.Get(fd).Message()) | |||
} | |||
// Handle list of messages. | |||
case fd.IsList(): | |||
if fd.Message() != nil { | |||
ls := m.Get(fd).List() | |||
for i := 0; i < ls.Len(); i++ { | |||
setDefaults(ls.Get(i).Message()) | |||
} | |||
} | |||
// Handle map of messages. | |||
case fd.IsMap(): | |||
if fd.MapValue().Message() != nil { | |||
ms := m.Get(fd).Map() | |||
ms.Range(func(_ protoreflect.MapKey, v protoreflect.Value) bool { | |||
setDefaults(v.Message()) | |||
return true | |||
}) | |||
} | |||
} | |||
return true | |||
}) | |||
} |
@ -1,63 +1,92 @@ | |||
// Go support for Protocol Buffers - Google's data interchange format | |||
// | |||
// Copyright 2018 The Go Authors. All rights reserved. | |||
// https://github.com/golang/protobuf | |||
// | |||
// 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. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// 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. | |||
// Copyright 2018 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package proto | |||
import "errors" | |||
import ( | |||
"encoding/json" | |||
"errors" | |||
"fmt" | |||
"strconv" | |||
) | |||
// Deprecated: do not use. | |||
var ( | |||
// Deprecated: No longer returned. | |||
ErrNil = errors.New("proto: Marshal called with nil") | |||
// Deprecated: No longer returned. | |||
ErrTooLarge = errors.New("proto: message encodes to over 2 GB") | |||
// Deprecated: No longer returned. | |||
ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof") | |||
) | |||
// Deprecated: Do not use. | |||
type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 } | |||
// Deprecated: do not use. | |||
// Deprecated: Do not use. | |||
func GetStats() Stats { return Stats{} } | |||
// Deprecated: do not use. | |||
// Deprecated: Do not use. | |||
func MarshalMessageSet(interface{}) ([]byte, error) { | |||
return nil, errors.New("proto: not implemented") | |||
} | |||
// Deprecated: do not use. | |||
// Deprecated: Do not use. | |||
func UnmarshalMessageSet([]byte, interface{}) error { | |||
return errors.New("proto: not implemented") | |||
} | |||
// Deprecated: do not use. | |||
// Deprecated: Do not use. | |||
func MarshalMessageSetJSON(interface{}) ([]byte, error) { | |||
return nil, errors.New("proto: not implemented") | |||
} | |||
// Deprecated: do not use. | |||
// Deprecated: Do not use. | |||
func UnmarshalMessageSetJSON([]byte, interface{}) error { | |||
return errors.New("proto: not implemented") | |||
} | |||
// Deprecated: do not use. | |||
// Deprecated: Do not use. | |||
func RegisterMessageSetType(Message, int32, string) {} | |||
// Deprecated: Do not use. | |||
func EnumName(m map[int32]string, v int32) string { | |||
s, ok := m[v] | |||
if ok { | |||
return s | |||
} | |||
return strconv.Itoa(int(v)) | |||
} | |||
// Deprecated: Do not use. | |||
func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) { | |||
if data[0] == '"' { | |||
// New style: enums are strings. | |||
var repr string | |||
if err := json.Unmarshal(data, &repr); err != nil { | |||
return -1, err | |||
} | |||
val, ok := m[repr] | |||
if !ok { | |||
return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr) | |||
} | |||
return val, nil | |||
} | |||
// Old style: enums are ints. | |||
var val int32 | |||
if err := json.Unmarshal(data, &val); err != nil { | |||
return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName) | |||
} | |||
return val, nil | |||
} | |||
// Deprecated: Do not use. | |||
type InternalMessageInfo struct{} | |||
func (*InternalMessageInfo) DiscardUnknown(Message) { panic("not implemented") } | |||
func (*InternalMessageInfo) Marshal([]byte, Message, bool) ([]byte, error) { panic("not implemented") } | |||
func (*InternalMessageInfo) Merge(Message, Message) { panic("not implemented") } | |||
func (*InternalMessageInfo) Size(Message) int { panic("not implemented") } | |||
func (*InternalMessageInfo) Unmarshal(Message, []byte) error { panic("not implemented") } |
@ -1,203 +0,0 @@ | |||
// Go support for Protocol Buffers - Google's data interchange format | |||
// | |||
// Copyright 2010 The Go Authors. All rights reserved. | |||
// https://github.com/golang/protobuf | |||
// | |||
// 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. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// 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. | |||
package proto | |||
/* | |||
* Routines for encoding data into the wire format for protocol buffers. | |||
*/ | |||
import ( | |||
"errors" | |||
"reflect" | |||
) | |||
var ( | |||
// errRepeatedHasNil is the error returned if Marshal is called with | |||
// a struct with a repeated field containing a nil element. | |||
errRepeatedHasNil = errors.New("proto: repeated field has nil element") | |||
// errOneofHasNil is the error returned if Marshal is called with | |||
// a struct with a oneof field containing a nil element. | |||
errOneofHasNil = errors.New("proto: oneof field has nil value") | |||
// ErrNil is the error returned if Marshal is called with nil. | |||
ErrNil = errors.New("proto: Marshal called with nil") | |||
// ErrTooLarge is the error returned if Marshal is called with a | |||
// message that encodes to >2GB. | |||
ErrTooLarge = errors.New("proto: message encodes to over 2 GB") | |||
) | |||
// The fundamental encoders that put bytes on the wire. | |||
// Those that take integer types all accept uint64 and are | |||
// therefore of type valueEncoder. | |||
const maxVarintBytes = 10 // maximum length of a varint | |||
// EncodeVarint returns the varint encoding of x. | |||
// This is the format for the | |||
// int32, int64, uint32, uint64, bool, and enum | |||
// protocol buffer types. | |||
// Not used by the package itself, but helpful to clients | |||
// wishing to use the same encoding. | |||
func EncodeVarint(x uint64) []byte { | |||
var buf [maxVarintBytes]byte | |||
var n int | |||
for n = 0; x > 127; n++ { | |||
buf[n] = 0x80 | uint8(x&0x7F) | |||
x >>= 7 | |||
} | |||
buf[n] = uint8(x) | |||
n++ | |||
return buf[0:n] | |||
} | |||
// EncodeVarint writes a varint-encoded integer to the Buffer. | |||
// This is the format for the | |||
// int32, int64, uint32, uint64, bool, and enum | |||
// protocol buffer types. | |||
func (p *Buffer) EncodeVarint(x uint64) error { | |||
for x >= 1<<7 { | |||
p.buf = append(p.buf, uint8(x&0x7f|0x80)) | |||
x >>= 7 | |||
} | |||
p.buf = append(p.buf, uint8(x)) | |||
return nil | |||
} | |||
// SizeVarint returns the varint encoding size of an integer. | |||
func SizeVarint(x uint64) int { | |||
switch { | |||
case x < 1<<7: | |||
return 1 | |||
case x < 1<<14: | |||
return 2 | |||
case x < 1<<21: | |||
return 3 | |||
case x < 1<<28: | |||
return 4 | |||
case x < 1<<35: | |||
return 5 | |||
case x < 1<<42: | |||
return 6 | |||
case x < 1<<49: | |||
return 7 | |||
case x < 1<<56: | |||
return 8 | |||
case x < 1<<63: | |||
return 9 | |||
} | |||
return 10 | |||
} | |||
// EncodeFixed64 writes a 64-bit integer to the Buffer. | |||
// This is the format for the | |||
// fixed64, sfixed64, and double protocol buffer types. | |||
func (p *Buffer) EncodeFixed64(x uint64) error { | |||
p.buf = append(p.buf, | |||
uint8(x), | |||
uint8(x>>8), | |||
uint8(x>>16), | |||
uint8(x>>24), | |||
uint8(x>>32), | |||
uint8(x>>40), | |||
uint8(x>>48), | |||
uint8(x>>56)) | |||
return nil | |||
} | |||
// EncodeFixed32 writes a 32-bit integer to the Buffer. | |||
// This is the format for the | |||
// fixed32, sfixed32, and float protocol buffer types. | |||
func (p *Buffer) EncodeFixed32(x uint64) error { | |||
p.buf = append(p.buf, | |||
uint8(x), | |||
uint8(x>>8), | |||
uint8(x>>16), | |||
uint8(x>>24)) | |||
return nil | |||
} | |||
// EncodeZigzag64 writes a zigzag-encoded 64-bit integer | |||
// to the Buffer. | |||
// This is the format used for the sint64 protocol buffer type. | |||
func (p *Buffer) EncodeZigzag64(x uint64) error { | |||
// use signed number to get arithmetic right shift. | |||
return p.EncodeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) | |||
} | |||
// EncodeZigzag32 writes a zigzag-encoded 32-bit integer | |||
// to the Buffer. | |||
// This is the format used for the sint32 protocol buffer type. | |||
func (p *Buffer) EncodeZigzag32(x uint64) error { | |||
// use signed number to get arithmetic right shift. | |||
return p.EncodeVarint(uint64((uint32(x) << 1) ^ uint32((int32(x) >> 31)))) | |||
} | |||
// EncodeRawBytes writes a count-delimited byte buffer to the Buffer. | |||
// This is the format used for the bytes protocol buffer | |||
// type and for embedded messages. | |||
func (p *Buffer) EncodeRawBytes(b []byte) error { | |||
p.EncodeVarint(uint64(len(b))) | |||
p.buf = append(p.buf, b...) | |||
return nil | |||
} | |||
// EncodeStringBytes writes an encoded string to the Buffer. | |||
// This is the format used for the proto2 string type. | |||
func (p *Buffer) EncodeStringBytes(s string) error { | |||
p.EncodeVarint(uint64(len(s))) | |||
p.buf = append(p.buf, s...) | |||
return nil | |||
} | |||
// Marshaler is the interface representing objects that can marshal themselves. | |||
type Marshaler interface { | |||
Marshal() ([]byte, error) | |||
} | |||
// EncodeMessage writes the protocol buffer to the Buffer, | |||
// prefixed by a varint-encoded length. | |||
func (p *Buffer) EncodeMessage(pb Message) error { | |||
siz := Size(pb) | |||
p.EncodeVarint(uint64(siz)) | |||
return p.Marshal(pb) | |||
} | |||
// All protocol buffer fields are nillable, but be careful. | |||
func isNil(v reflect.Value) bool { | |||
switch v.Kind() { | |||
case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: | |||
return v.IsNil() | |||
} | |||
return false | |||
} |
@ -1,301 +0,0 @@ | |||
// Go support for Protocol Buffers - Google's data interchange format | |||
// | |||
// Copyright 2011 The Go Authors. All rights reserved. | |||
// https://github.com/golang/protobuf | |||
// | |||
// 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. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// 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. | |||
// Protocol buffer comparison. | |||
package proto | |||
import ( | |||
"bytes" | |||
"log" | |||
"reflect" | |||
"strings" | |||
) | |||
/* | |||
Equal returns true iff protocol buffers a and b are equal. | |||
The arguments must both be pointers to protocol buffer structs. | |||
Equality is defined in this way: | |||
- Two messages are equal iff they are the same type, | |||
corresponding fields are equal, unknown field sets | |||
are equal, and extensions sets are equal. | |||
- Two set scalar fields are equal iff their values are equal. | |||
If the fields are of a floating-point type, remember that | |||
NaN != x for all x, including NaN. If the message is defined | |||
in a proto3 .proto file, fields are not "set"; specifically, | |||
zero length proto3 "bytes" fields are equal (nil == {}). | |||
- Two repeated fields are equal iff their lengths are the same, | |||
and their corresponding elements are equal. Note a "bytes" field, | |||
although represented by []byte, is not a repeated field and the | |||
rule for the scalar fields described above applies. | |||
- Two unset fields are equal. | |||
- Two unknown field sets are equal if their current | |||
encoded state is equal. | |||
- Two extension sets are equal iff they have corresponding | |||
elements that are pairwise equal. | |||
- Two map fields are equal iff their lengths are the same, | |||
and they contain the same set of elements. Zero-length map | |||
fields are equal. | |||
- Every other combination of things are not equal. | |||
The return value is undefined if a and b are not protocol buffers. | |||
*/ | |||
func Equal(a, b Message) bool { | |||
if a == nil || b == nil { | |||
return a == b | |||
} | |||
v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b) | |||
if v1.Type() != v2.Type() { | |||
return false | |||
} | |||
if v1.Kind() == reflect.Ptr { | |||
if v1.IsNil() { | |||
return v2.IsNil() | |||
} | |||
if v2.IsNil() { | |||
return false | |||
} | |||
v1, v2 = v1.Elem(), v2.Elem() | |||
} | |||
if v1.Kind() != reflect.Struct { | |||
return false | |||
} | |||
return equalStruct(v1, v2) | |||
} | |||
// v1 and v2 are known to have the same type. | |||
func equalStruct(v1, v2 reflect.Value) bool { | |||
sprop := GetProperties(v1.Type()) | |||
for i := 0; i < v1.NumField(); i++ { | |||
f := v1.Type().Field(i) | |||
if strings.HasPrefix(f.Name, "XXX_") { | |||
continue | |||
} | |||
f1, f2 := v1.Field(i), v2.Field(i) | |||
if f.Type.Kind() == reflect.Ptr { | |||
if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 { | |||
// both unset | |||
continue | |||
} else if n1 != n2 { | |||
// set/unset mismatch | |||
return false | |||
} | |||
f1, f2 = f1.Elem(), f2.Elem() | |||
} | |||
if !equalAny(f1, f2, sprop.Prop[i]) { | |||
return false | |||
} | |||
} | |||
if em1 := v1.FieldByName("XXX_InternalExtensions"); em1.IsValid() { | |||
em2 := v2.FieldByName("XXX_InternalExtensions") | |||
if !equalExtensions(v1.Type(), em1.Interface().(XXX_InternalExtensions), em2.Interface().(XXX_InternalExtensions)) { | |||
return false | |||
} | |||
} | |||
if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() { | |||
em2 := v2.FieldByName("XXX_extensions") | |||
if !equalExtMap(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) { | |||
return false | |||
} | |||
} | |||
uf := v1.FieldByName("XXX_unrecognized") | |||
if !uf.IsValid() { | |||
return true | |||
} | |||
u1 := uf.Bytes() | |||
u2 := v2.FieldByName("XXX_unrecognized").Bytes() | |||
return bytes.Equal(u1, u2) | |||
} | |||
// v1 and v2 are known to have the same type. | |||
// prop may be nil. | |||
func equalAny(v1, v2 reflect.Value, prop *Properties) bool { | |||
if v1.Type() == protoMessageType { | |||
m1, _ := v1.Interface().(Message) | |||
m2, _ := v2.Interface().(Message) | |||
return Equal(m1, m2) | |||
} | |||
switch v1.Kind() { | |||
case reflect.Bool: | |||
return v1.Bool() == v2.Bool() | |||
case reflect.Float32, reflect.Float64: | |||
return v1.Float() == v2.Float() | |||
case reflect.Int32, reflect.Int64: | |||
return v1.Int() == v2.Int() | |||
case reflect.Interface: | |||
// Probably a oneof field; compare the inner values. | |||
n1, n2 := v1.IsNil(), v2.IsNil() | |||
if n1 || n2 { | |||
return n1 == n2 | |||
} | |||
e1, e2 := v1.Elem(), v2.Elem() | |||
if e1.Type() != e2.Type() { | |||
return false | |||
} | |||
return equalAny(e1, e2, nil) | |||
case reflect.Map: | |||
if v1.Len() != v2.Len() { | |||
return false | |||
} | |||
for _, key := range v1.MapKeys() { | |||
val2 := v2.MapIndex(key) | |||
if !val2.IsValid() { | |||
// This key was not found in the second map. | |||
return false | |||
} | |||
if !equalAny(v1.MapIndex(key), val2, nil) { | |||
return false | |||
} | |||
} | |||
return true | |||
case reflect.Ptr: | |||
// Maps may have nil values in them, so check for nil. | |||
if v1.IsNil() && v2.IsNil() { | |||
return true | |||
} | |||
if v1.IsNil() != v2.IsNil() { | |||
return false | |||
} | |||
return equalAny(v1.Elem(), v2.Elem(), prop) | |||
case reflect.Slice: | |||
if v1.Type().Elem().Kind() == reflect.Uint8 { | |||
// short circuit: []byte | |||
// Edge case: if this is in a proto3 message, a zero length | |||
// bytes field is considered the zero value. | |||
if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 { | |||
return true | |||
} | |||
if v1.IsNil() != v2.IsNil() { | |||
return false | |||
} | |||
return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte)) | |||
} | |||
if v1.Len() != v2.Len() { | |||
return false | |||
} | |||
for i := 0; i < v1.Len(); i++ { | |||
if !equalAny(v1.Index(i), v2.Index(i), prop) { | |||
return false | |||
} | |||
} | |||
return true | |||
case reflect.String: | |||
return v1.Interface().(string) == v2.Interface().(string) | |||
case reflect.Struct: | |||
return equalStruct(v1, v2) | |||
case reflect.Uint32, reflect.Uint64: | |||
return v1.Uint() == v2.Uint() | |||
} | |||
// unknown type, so not a protocol buffer | |||
log.Printf("proto: don't know how to compare %v", v1) | |||
return false | |||
} | |||
// base is the struct type that the extensions are based on. | |||
// x1 and x2 are InternalExtensions. | |||
func equalExtensions(base reflect.Type, x1, x2 XXX_InternalExtensions) bool { | |||
em1, _ := x1.extensionsRead() | |||
em2, _ := x2.extensionsRead() | |||
return equalExtMap(base, em1, em2) | |||
} | |||
func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool { | |||
if len(em1) != len(em2) { | |||
return false | |||
} | |||
for extNum, e1 := range em1 { | |||
e2, ok := em2[extNum] | |||
if !ok { | |||
return false | |||
} | |||
m1 := extensionAsLegacyType(e1.value) | |||
m2 := extensionAsLegacyType(e2.value) | |||
if m1 == nil && m2 == nil { | |||
// Both have only encoded form. | |||
if bytes.Equal(e1.enc, e2.enc) { | |||
continue | |||
} | |||
// The bytes are different, but the extensions might still be | |||
// equal. We need to decode them to compare. | |||
} | |||
if m1 != nil && m2 != nil { | |||
// Both are unencoded. | |||
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { | |||
return false | |||
} | |||
continue | |||
} | |||
// At least one is encoded. To do a semantically correct comparison | |||
// we need to unmarshal them first. | |||
var desc *ExtensionDesc | |||
if m := extensionMaps[base]; m != nil { | |||
desc = m[extNum] | |||
} | |||
if desc == nil { | |||
// If both have only encoded form and the bytes are the same, | |||
// it is handled above. We get here when the bytes are different. | |||
// We don't know how to decode it, so just compare them as byte | |||
// slices. | |||
log.Printf("proto: don't know how to compare extension %d of %v", extNum, base) | |||
return false | |||
} | |||
var err error | |||
if m1 == nil { | |||
m1, err = decodeExtension(e1.enc, desc) | |||
} | |||
if m2 == nil && err == nil { | |||
m2, err = decodeExtension(e2.enc, desc) | |||
} | |||
if err != nil { | |||
// The encoded form is invalid. | |||
log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err) | |||
return false | |||
} | |||
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { | |||
return false | |||
} | |||
} | |||
return true | |||
} |
@ -1,965 +0,0 @@ | |||
// Go support for Protocol Buffers - Google's data interchange format | |||
// | |||
// Copyright 2010 The Go Authors. All rights reserved. | |||
// https://github.com/golang/protobuf | |||
// | |||
// 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. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// 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. | |||
/* | |||
Package proto converts data structures to and from the wire format of | |||
protocol buffers. It works in concert with the Go source code generated | |||
for .proto files by the protocol compiler. | |||
A summary of the properties of the protocol buffer interface | |||
for a protocol buffer variable v: | |||
- Names are turned from camel_case to CamelCase for export. | |||
- There are no methods on v to set fields; just treat | |||
them as structure fields. | |||
- There are getters that return a field's value if set, | |||
and return the field's default value if unset. | |||
The getters work even if the receiver is a nil message. | |||
- The zero value for a struct is its correct initialization state. | |||
All desired fields must be set before marshaling. | |||
- A Reset() method will restore a protobuf struct to its zero state. | |||
- Non-repeated fields are pointers to the values; nil means unset. | |||
That is, optional or required field int32 f becomes F *int32. | |||
- Repeated fields are slices. | |||
- Helper functions are available to aid the setting of fields. | |||
msg.Foo = proto.String("hello") // set field | |||
- Constants are defined to hold the default values of all fields that | |||
have them. They have the form Default_StructName_FieldName. | |||
Because the getter methods handle defaulted values, | |||
direct use of these constants should be rare. | |||
- Enums are given type names and maps from names to values. | |||
Enum values are prefixed by the enclosing message's name, or by the | |||
enum's type name if it is a top-level enum. Enum types have a String | |||
method, and a Enum method to assist in message construction. | |||
- Nested messages, groups and enums have type names prefixed with the name of | |||
the surrounding message type. | |||
- Extensions are given descriptor names that start with E_, | |||
followed by an underscore-delimited list of the nested messages | |||
that contain it (if any) followed by the CamelCased name of the | |||
extension field itself. HasExtension, ClearExtension, GetExtension | |||
and SetExtension are functions for manipulating extensions. | |||
- Oneof field sets are given a single field in their message, | |||
with distinguished wrapper types for each possible field value. | |||
- Marshal and Unmarshal are functions to encode and decode the wire format. | |||
When the .proto file specifies `syntax="proto3"`, there are some differences: | |||
- Non-repeated fields of non-message type are values instead of pointers. | |||
- Enum types do not get an Enum method. | |||
The simplest way to describe this is to see an example. | |||
Given file test.proto, containing | |||
package example; | |||
enum FOO { X = 17; } | |||
message Test { | |||
required string label = 1; | |||
optional int32 type = 2 [default=77]; | |||
repeated int64 reps = 3; | |||
optional group OptionalGroup = 4 { | |||
required string RequiredField = 5; | |||
} | |||
oneof union { | |||
int32 number = 6; | |||
string name = 7; | |||
} | |||
} | |||
The resulting file, test.pb.go, is: | |||
package example | |||
import proto "github.com/golang/protobuf/proto" | |||
import math "math" | |||
type FOO int32 | |||
const ( | |||
FOO_X FOO = 17 | |||
) | |||
var FOO_name = map[int32]string{ | |||
17: "X", | |||
} | |||
var FOO_value = map[string]int32{ | |||
"X": 17, | |||
} | |||
func (x FOO) Enum() *FOO { | |||
p := new(FOO) | |||
*p = x | |||
return p | |||
} | |||
func (x FOO) String() string { | |||
return proto.EnumName(FOO_name, int32(x)) | |||
} | |||
func (x *FOO) UnmarshalJSON(data []byte) error { | |||
value, err := proto.UnmarshalJSONEnum(FOO_value, data) | |||
if err != nil { | |||
return err | |||
} | |||
*x = FOO(value) | |||
return nil | |||
} | |||
type Test struct { | |||
Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"` | |||
Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"` | |||
Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"` | |||
Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"` | |||
// Types that are valid to be assigned to Union: | |||
// *Test_Number | |||
// *Test_Name | |||
Union isTest_Union `protobuf_oneof:"union"` | |||
XXX_unrecognized []byte `json:"-"` | |||
} | |||
func (m *Test) Reset() { *m = Test{} } | |||
func (m *Test) String() string { return proto.CompactTextString(m) } | |||
func (*Test) ProtoMessage() {} | |||
type isTest_Union interface { | |||
isTest_Union() | |||
} | |||
type Test_Number struct { | |||
Number int32 `protobuf:"varint,6,opt,name=number"` | |||
} | |||
type Test_Name struct { | |||
Name string `protobuf:"bytes,7,opt,name=name"` | |||
} | |||
func (*Test_Number) isTest_Union() {} | |||
func (*Test_Name) isTest_Union() {} | |||
func (m *Test) GetUnion() isTest_Union { | |||
if m != nil { | |||
return m.Union | |||
} | |||
return nil | |||
} | |||
const Default_Test_Type int32 = 77 | |||
func (m *Test) GetLabel() string { | |||
if m != nil && m.Label != nil { | |||
return *m.Label | |||
} | |||
return "" | |||
} | |||
func (m *Test) GetType() int32 { | |||
if m != nil && m.Type != nil { | |||
return *m.Type | |||
} | |||
return Default_Test_Type | |||
} | |||
func (m *Test) GetOptionalgroup() *Test_OptionalGroup { | |||
if m != nil { | |||
return m.Optionalgroup | |||
} | |||
return nil | |||
} | |||
type Test_OptionalGroup struct { | |||
RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"` | |||
} | |||
func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} } | |||
func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) } | |||
func (m *Test_OptionalGroup) GetRequiredField() string { | |||
if m != nil && m.RequiredField != nil { | |||
return *m.RequiredField | |||
} | |||
return "" | |||
} | |||
func (m *Test) GetNumber() int32 { | |||
if x, ok := m.GetUnion().(*Test_Number); ok { | |||
return x.Number | |||
} | |||
return 0 | |||
} | |||
func (m *Test) GetName() string { | |||
if x, ok := m.GetUnion().(*Test_Name); ok { | |||
return x.Name | |||
} | |||
return "" | |||
} | |||
func init() { | |||
proto.RegisterEnum("example.FOO", FOO_name, FOO_value) | |||
} | |||
To create and play with a Test object: | |||
package main | |||
import ( | |||
"log" | |||
"github.com/golang/protobuf/proto" | |||
pb "./example.pb" | |||
) | |||
func main() { | |||
test := &pb.Test{ | |||
Label: proto.String("hello"), | |||
Type: proto.Int32(17), | |||
Reps: []int64{1, 2, 3}, | |||
Optionalgroup: &pb.Test_OptionalGroup{ | |||
RequiredField: proto.String("good bye"), | |||
}, | |||
Union: &pb.Test_Name{"fred"}, | |||
} | |||
data, err := proto.Marshal(test) | |||
if err != nil { | |||
log.Fatal("marshaling error: ", err) | |||
} | |||
newTest := &pb.Test{} | |||
err = proto.Unmarshal(data, newTest) | |||
if err != nil { | |||
log.Fatal("unmarshaling error: ", err) | |||
} | |||
// Now test and newTest contain the same data. | |||
if test.GetLabel() != newTest.GetLabel() { | |||
log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel()) | |||
} | |||
// Use a type switch to determine which oneof was set. | |||
switch u := test.Union.(type) { | |||
case *pb.Test_Number: // u.Number contains the number. | |||
case *pb.Test_Name: // u.Name contains the string. | |||
} | |||
// etc. | |||
} | |||
*/ | |||
package proto | |||
import ( | |||
"encoding/json" | |||
"fmt" | |||
"log" | |||
"reflect" | |||
"sort" | |||
"strconv" | |||
"sync" | |||
) | |||
// RequiredNotSetError is an error type returned by either Marshal or Unmarshal. | |||
// Marshal reports this when a required field is not initialized. | |||
// Unmarshal reports this when a required field is missing from the wire data. | |||
type RequiredNotSetError struct{ field string } | |||
func (e *RequiredNotSetError) Error() string { | |||
if e.field == "" { | |||
return fmt.Sprintf("proto: required field not set") | |||
} | |||
return fmt.Sprintf("proto: required field %q not set", e.field) | |||
} | |||
func (e *RequiredNotSetError) RequiredNotSet() bool { | |||
return true | |||
} | |||
type invalidUTF8Error struct{ field string } | |||
func (e *invalidUTF8Error) Error() string { | |||
if e.field == "" { | |||
return "proto: invalid UTF-8 detected" | |||
} | |||
return fmt.Sprintf("proto: field %q contains invalid UTF-8", e.field) | |||
} | |||
func (e *invalidUTF8Error) InvalidUTF8() bool { | |||
return true | |||
} | |||
// errInvalidUTF8 is a sentinel error to identify fields with invalid UTF-8. | |||
// This error should not be exposed to the external API as such errors should | |||
// be recreated with the field information. | |||
var errInvalidUTF8 = &invalidUTF8Error{} | |||
// isNonFatal reports whether the error is either a RequiredNotSet error | |||
// or a InvalidUTF8 error. | |||
func isNonFatal(err error) bool { | |||
if re, ok := err.(interface{ RequiredNotSet() bool }); ok && re.RequiredNotSet() { | |||
return true | |||
} | |||
if re, ok := err.(interface{ InvalidUTF8() bool }); ok && re.InvalidUTF8() { | |||
return true | |||
} | |||
return false | |||
} | |||
type nonFatal struct{ E error } | |||
// Merge merges err into nf and reports whether it was successful. | |||
// Otherwise it returns false for any fatal non-nil errors. | |||
func (nf *nonFatal) Merge(err error) (ok bool) { | |||
if err == nil { | |||
return true // not an error | |||
} | |||
if !isNonFatal(err) { | |||
return false // fatal error | |||
} | |||
if nf.E == nil { | |||
nf.E = err // store first instance of non-fatal error | |||
} | |||
return true | |||
} | |||
// Message is implemented by generated protocol buffer messages. | |||
type Message interface { | |||
Reset() | |||
String() string | |||
ProtoMessage() | |||
} | |||
// A Buffer is a buffer manager for marshaling and unmarshaling | |||
// protocol buffers. It may be reused between invocations to | |||
// reduce memory usage. It is not necessary to use a Buffer; | |||
// the global functions Marshal and Unmarshal create a | |||
// temporary Buffer and are fine for most applications. | |||
type Buffer struct { | |||
buf []byte // encode/decode byte stream | |||
index int // read point | |||
deterministic bool | |||
} | |||
// NewBuffer allocates a new Buffer and initializes its internal data to | |||
// the contents of the argument slice. | |||
func NewBuffer(e []byte) *Buffer { | |||
return &Buffer{buf: e} | |||
} | |||
// Reset resets the Buffer, ready for marshaling a new protocol buffer. | |||
func (p *Buffer) Reset() { | |||
p.buf = p.buf[0:0] // for reading/writing | |||
p.index = 0 // for reading | |||
} | |||
// SetBuf replaces the internal buffer with the slice, | |||
// ready for unmarshaling the contents of the slice. | |||
func (p *Buffer) SetBuf(s []byte) { | |||
p.buf = s | |||
p.index = 0 | |||
} | |||
// Bytes returns the contents of the Buffer. | |||
func (p *Buffer) Bytes() []byte { return p.buf } | |||
// SetDeterministic sets whether to use deterministic serialization. | |||
// | |||
// Deterministic serialization guarantees that for a given binary, equal | |||
// messages will always be serialized to the same bytes. This implies: | |||
// | |||
// - Repeated serialization of a message will return the same bytes. | |||
// - Different processes of the same binary (which may be executing on | |||
// different machines) will serialize equal messages to the same bytes. | |||
// | |||
// Note that the deterministic serialization is NOT canonical across | |||
// languages. It is not guaranteed to remain stable over time. It is unstable | |||
// across different builds with schema changes due to unknown fields. | |||
// Users who need canonical serialization (e.g., persistent storage in a | |||
// canonical form, fingerprinting, etc.) should define their own | |||
// canonicalization specification and implement their own serializer rather | |||
// than relying on this API. | |||
// | |||
// If deterministic serialization is requested, map entries will be sorted | |||
// by keys in lexicographical order. This is an implementation detail and | |||
// subject to change. | |||
func (p *Buffer) SetDeterministic(deterministic bool) { | |||
p.deterministic = deterministic | |||
} | |||
/* | |||
* Helper routines for simplifying the creation of optional fields of basic type. | |||
*/ | |||
// Bool is a helper routine that allocates a new bool value | |||
// to store v and returns a pointer to it. | |||
func Bool(v bool) *bool { | |||
return &v | |||
} | |||
// Int32 is a helper routine that allocates a new int32 value | |||
// to store v and returns a pointer to it. | |||
func Int32(v int32) *int32 { | |||
return &v | |||
} | |||
// Int is a helper routine that allocates a new int32 value | |||
// to store v and returns a pointer to it, but unlike Int32 | |||
// its argument value is an int. | |||
func Int(v int) *int32 { | |||
p := new(int32) | |||
*p = int32(v) | |||
return p | |||
} | |||
// Int64 is a helper routine that allocates a new int64 value | |||
// to store v and returns a pointer to it. | |||
func Int64(v int64) *int64 { | |||
return &v | |||
} | |||
// Float32 is a helper routine that allocates a new float32 value | |||
// to store v and returns a pointer to it. | |||
func Float32(v float32) *float32 { | |||
return &v | |||
} | |||
// Float64 is a helper routine that allocates a new float64 value | |||
// to store v and returns a pointer to it. | |||
func Float64(v float64) *float64 { | |||
return &v | |||
} | |||
// Uint32 is a helper routine that allocates a new uint32 value | |||
// to store v and returns a pointer to it. | |||
func Uint32(v uint32) *uint32 { | |||
return &v | |||
} | |||
// Uint64 is a helper routine that allocates a new uint64 value | |||
// to store v and returns a pointer to it. | |||
func Uint64(v uint64) *uint64 { | |||
return &v | |||
} | |||
// String is a helper routine that allocates a new string value | |||
// to store v and returns a pointer to it. | |||
func String(v string) *string { | |||
return &v | |||
} | |||
// EnumName is a helper function to simplify printing protocol buffer enums | |||
// by name. Given an enum map and a value, it returns a useful string. | |||
func EnumName(m map[int32]string, v int32) string { | |||
s, ok := m[v] | |||
if ok { | |||
return s | |||
} | |||
return strconv.Itoa(int(v)) | |||
} | |||
// UnmarshalJSONEnum is a helper function to simplify recovering enum int values | |||
// from their JSON-encoded representation. Given a map from the enum's symbolic | |||
// names to its int values, and a byte buffer containing the JSON-encoded | |||
// value, it returns an int32 that can be cast to the enum type by the caller. | |||
// | |||
// The function can deal with both JSON representations, numeric and symbolic. | |||
func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) { | |||
if data[0] == '"' { | |||
// New style: enums are strings. | |||
var repr string | |||
if err := json.Unmarshal(data, &repr); err != nil { | |||
return -1, err | |||
} | |||
val, ok := m[repr] | |||
if !ok { | |||
return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr) | |||
} | |||
return val, nil | |||
} | |||
// Old style: enums are ints. | |||
var val int32 | |||
if err := json.Unmarshal(data, &val); err != nil { | |||
return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName) | |||
} | |||
return val, nil | |||
} | |||
// DebugPrint dumps the encoded data in b in a debugging format with a header | |||
// including the string s. Used in testing but made available for general debugging. | |||
func (p *Buffer) DebugPrint(s string, b []byte) { | |||
var u uint64 | |||
obuf := p.buf | |||
index := p.index | |||
p.buf = b | |||
p.index = 0 | |||
depth := 0 | |||
fmt.Printf("\n--- %s ---\n", s) | |||
out: | |||
for { | |||
for i := 0; i < depth; i++ { | |||
fmt.Print(" ") | |||
} | |||
index := p.index | |||
if index == len(p.buf) { | |||
break | |||
} | |||
op, err := p.DecodeVarint() | |||
if err != nil { | |||
fmt.Printf("%3d: fetching op err %v\n", index, err) | |||
break out | |||
} | |||
tag := op >> 3 | |||
wire := op & 7 | |||
switch wire { | |||
default: | |||
fmt.Printf("%3d: t=%3d unknown wire=%d\n", | |||
index, tag, wire) | |||
break out | |||
case WireBytes: | |||
var r []byte | |||
r, err = p.DecodeRawBytes(false) | |||
if err != nil { | |||
break out | |||
} | |||
fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r)) | |||
if len(r) <= 6 { | |||
for i := 0; i < len(r); i++ { | |||
fmt.Printf(" %.2x", r[i]) | |||
} | |||
} else { | |||
for i := 0; i < 3; i++ { | |||
fmt.Printf(" %.2x", r[i]) | |||
} | |||
fmt.Printf(" ..") | |||
for i := len(r) - 3; i < len(r); i++ { | |||
fmt.Printf(" %.2x", r[i]) | |||
} | |||
} | |||
fmt.Printf("\n") | |||
case WireFixed32: | |||
u, err = p.DecodeFixed32() | |||
if err != nil { | |||
fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err) | |||
break out | |||
} | |||
fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u) | |||
case WireFixed64: | |||
u, err = p.DecodeFixed64() | |||
if err != nil { | |||
fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err) | |||
break out | |||
} | |||
fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u) | |||
case WireVarint: | |||
u, err = p.DecodeVarint() | |||
if err != nil { | |||
fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err) | |||
break out | |||
} | |||
fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u) | |||
case WireStartGroup: | |||
fmt.Printf("%3d: t=%3d start\n", index, tag) | |||
depth++ | |||
case WireEndGroup: | |||
depth-- | |||
fmt.Printf("%3d: t=%3d end\n", index, tag) | |||
} | |||
} | |||
if depth != 0 { | |||
fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth) | |||
} | |||
fmt.Printf("\n") | |||
p.buf = obuf | |||
p.index = index | |||
} | |||
// SetDefaults sets unset protocol buffer fields to their default values. | |||
// It only modifies fields that are both unset and have defined defaults. | |||
// It recursively sets default values in any non-nil sub-messages. | |||
func SetDefaults(pb Message) { | |||
setDefaults(reflect.ValueOf(pb), true, false) | |||
} | |||
// v is a pointer to a struct. | |||
func setDefaults(v reflect.Value, recur, zeros bool) { | |||
v = v.Elem() | |||
defaultMu.RLock() | |||
dm, ok := defaults[v.Type()] | |||
defaultMu.RUnlock() | |||
if !ok { | |||
dm = buildDefaultMessage(v.Type()) | |||
defaultMu.Lock() | |||
defaults[v.Type()] = dm | |||
defaultMu.Unlock() | |||
} | |||
for _, sf := range dm.scalars { | |||
f := v.Field(sf.index) | |||
if !f.IsNil() { | |||
// field already set | |||
continue | |||
} | |||
dv := sf.value | |||
if dv == nil && !zeros { | |||
// no explicit default, and don't want to set zeros | |||
continue | |||
} | |||
fptr := f.Addr().Interface() // **T | |||
// TODO: Consider batching the allocations we do here. | |||
switch sf.kind { | |||
case reflect.Bool: | |||
b := new(bool) | |||
if dv != nil { | |||
*b = dv.(bool) | |||
} | |||
*(fptr.(**bool)) = b | |||
case reflect.Float32: | |||
f := new(float32) | |||
if dv != nil { | |||
*f = dv.(float32) | |||
} | |||
*(fptr.(**float32)) = f | |||
case reflect.Float64: | |||
f := new(float64) | |||
if dv != nil { | |||
*f = dv.(float64) | |||
} | |||
*(fptr.(**float64)) = f | |||
case reflect.Int32: | |||
// might be an enum | |||
if ft := f.Type(); ft != int32PtrType { | |||
// enum | |||
f.Set(reflect.New(ft.Elem())) | |||
if dv != nil { | |||
f.Elem().SetInt(int64(dv.(int32))) | |||
} | |||
} else { | |||
// int32 field | |||
i := new(int32) | |||
if dv != nil { | |||
*i = dv.(int32) | |||
} | |||
*(fptr.(**int32)) = i | |||
} | |||
case reflect.Int64: | |||
i := new(int64) | |||
if dv != nil { | |||
*i = dv.(int64) | |||
} | |||
*(fptr.(**int64)) = i | |||
case reflect.String: | |||
s := new(string) | |||
if dv != nil { | |||
*s = dv.(string) | |||
} | |||
*(fptr.(**string)) = s | |||
case reflect.Uint8: | |||
// exceptional case: []byte | |||
var b []byte | |||
if dv != nil { | |||
db := dv.([]byte) | |||
b = make([]byte, len(db)) | |||
copy(b, db) | |||
} else { | |||
b = []byte{} | |||
} | |||
*(fptr.(*[]byte)) = b | |||
case reflect.Uint32: | |||
u := new(uint32) | |||
if dv != nil { | |||
*u = dv.(uint32) | |||
} | |||
*(fptr.(**uint32)) = u | |||
case reflect.Uint64: | |||
u := new(uint64) | |||
if dv != nil { | |||
*u = dv.(uint64) | |||
} | |||
*(fptr.(**uint64)) = u | |||
default: | |||
log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind) | |||
} | |||
} | |||
for _, ni := range dm.nested { | |||
f := v.Field(ni) | |||
// f is *T or []*T or map[T]*T | |||
switch f.Kind() { | |||
case reflect.Ptr: | |||
if f.IsNil() { | |||
continue | |||
} | |||
setDefaults(f, recur, zeros) | |||
case reflect.Slice: | |||
for i := 0; i < f.Len(); i++ { | |||
e := f.Index(i) | |||
if e.IsNil() { | |||
continue | |||
} | |||
setDefaults(e, recur, zeros) | |||
} | |||
case reflect.Map: | |||
for _, k := range f.MapKeys() { | |||
e := f.MapIndex(k) | |||
if e.IsNil() { | |||
continue | |||
} | |||
setDefaults(e, recur, zeros) | |||
} | |||
} | |||
} | |||
} | |||
var ( | |||
// defaults maps a protocol buffer struct type to a slice of the fields, | |||
// with its scalar fields set to their proto-declared non-zero default values. | |||
defaultMu sync.RWMutex | |||
defaults = make(map[reflect.Type]defaultMessage) | |||
int32PtrType = reflect.TypeOf((*int32)(nil)) | |||
) | |||
// defaultMessage represents information about the default values of a message. | |||
type defaultMessage struct { | |||
scalars []scalarField | |||
nested []int // struct field index of nested messages | |||
} | |||
type scalarField struct { | |||
index int // struct field index | |||
kind reflect.Kind // element type (the T in *T or []T) | |||
value interface{} // the proto-declared default value, or nil | |||
} | |||
// t is a struct type. | |||
func buildDefaultMessage(t reflect.Type) (dm defaultMessage) { | |||
sprop := GetProperties(t) | |||
for _, prop := range sprop.Prop { | |||
fi, ok := sprop.decoderTags.get(prop.Tag) | |||
if !ok { | |||
// XXX_unrecognized | |||
continue | |||
} | |||
ft := t.Field(fi).Type | |||
sf, nested, err := fieldDefault(ft, prop) | |||
switch { | |||
case err != nil: | |||
log.Print(err) | |||
case nested: | |||
dm.nested = append(dm.nested, fi) | |||
case sf != nil: | |||
sf.index = fi | |||
dm.scalars = append(dm.scalars, *sf) | |||
} | |||
} | |||
return dm | |||
} | |||
// fieldDefault returns the scalarField for field type ft. | |||
// sf will be nil if the field can not have a default. | |||
// nestedMessage will be true if this is a nested message. | |||
// Note that sf.index is not set on return. | |||
func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) { | |||
var canHaveDefault bool | |||
switch ft.Kind() { | |||
case reflect.Ptr: | |||
if ft.Elem().Kind() == reflect.Struct { | |||
nestedMessage = true | |||
} else { | |||
canHaveDefault = true // proto2 scalar field | |||
} | |||
case reflect.Slice: | |||
switch ft.Elem().Kind() { | |||
case reflect.Ptr: | |||
nestedMessage = true // repeated message | |||
case reflect.Uint8: | |||
canHaveDefault = true // bytes field | |||
} | |||
case reflect.Map: | |||
if ft.Elem().Kind() == reflect.Ptr { | |||
nestedMessage = true // map with message values | |||
} | |||
} | |||
if !canHaveDefault { | |||
if nestedMessage { | |||
return nil, true, nil | |||
} | |||
return nil, false, nil | |||
} | |||
// We now know that ft is a pointer or slice. | |||
sf = &scalarField{kind: ft.Elem().Kind()} | |||
// scalar fields without defaults | |||
if !prop.HasDefault { | |||
return sf, false, nil | |||
} | |||
// a scalar field: either *T or []byte | |||
switch ft.Elem().Kind() { | |||
case reflect.Bool: | |||
x, err := strconv.ParseBool(prop.Default) | |||
if err != nil { | |||
return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err) | |||
} | |||
sf.value = x | |||
case reflect.Float32: | |||
x, err := strconv.ParseFloat(prop.Default, 32) | |||
if err != nil { | |||
return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err) | |||
} | |||
sf.value = float32(x) | |||
case reflect.Float64: | |||
x, err := strconv.ParseFloat(prop.Default, 64) | |||
if err != nil { | |||
return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err) | |||
} | |||
sf.value = x | |||
case reflect.Int32: | |||
x, err := strconv.ParseInt(prop.Default, 10, 32) | |||
if err != nil { | |||
return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err) | |||
} | |||
sf.value = int32(x) | |||
case reflect.Int64: | |||
x, err := strconv.ParseInt(prop.Default, 10, 64) | |||
if err != nil { | |||
return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err) | |||
} | |||
sf.value = x | |||
case reflect.String: | |||
sf.value = prop.Default | |||
case reflect.Uint8: | |||
// []byte (not *uint8) | |||
sf.value = []byte(prop.Default) | |||
case reflect.Uint32: | |||
x, err := strconv.ParseUint(prop.Default, 10, 32) | |||
if err != nil { | |||
return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err) | |||
} | |||
sf.value = uint32(x) | |||
case reflect.Uint64: | |||
x, err := strconv.ParseUint(prop.Default, 10, 64) | |||
if err != nil { | |||
return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err) | |||
} | |||
sf.value = x | |||
default: | |||
return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind()) | |||
} | |||
return sf, false, nil | |||
} | |||
// mapKeys returns a sort.Interface to be used for sorting the map keys. | |||
// Map fields may have key types of non-float scalars, strings and enums. | |||
func mapKeys(vs []reflect.Value) sort.Interface { | |||
s := mapKeySorter{vs: vs} | |||
// Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps. | |||
if len(vs) == 0 { | |||
return s | |||
} | |||
switch vs[0].Kind() { | |||
case reflect.Int32, reflect.Int64: | |||
s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() } | |||
case reflect.Uint32, reflect.Uint64: | |||
s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() } | |||
case reflect.Bool: | |||
s.less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() } // false < true | |||
case reflect.String: | |||
s.less = func(a, b reflect.Value) bool { return a.String() < b.String() } | |||
default: | |||
panic(fmt.Sprintf("unsupported map key type: %v", vs[0].Kind())) | |||
} | |||
return s | |||
} | |||
type mapKeySorter struct { | |||
vs []reflect.Value | |||
less func(a, b reflect.Value) bool | |||
} | |||
func (s mapKeySorter) Len() int { return len(s.vs) } | |||
func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] } | |||
func (s mapKeySorter) Less(i, j int) bool { | |||
return s.less(s.vs[i], s.vs[j]) | |||
} | |||
// isProto3Zero reports whether v is a zero proto3 value. | |||
func isProto3Zero(v reflect.Value) bool { | |||
switch v.Kind() { | |||
case reflect.Bool: | |||
return !v.Bool() | |||
case reflect.Int32, reflect.Int64: | |||
return v.Int() == 0 | |||
case reflect.Uint32, reflect.Uint64: | |||
return v.Uint() == 0 | |||
case reflect.Float32, reflect.Float64: | |||
return v.Float() == 0 | |||
case reflect.String: | |||
return v.String() == "" | |||
} | |||
return false | |||
} | |||
const ( | |||
// ProtoPackageIsVersion3 is referenced from generated protocol buffer files | |||
// to assert that that code is compatible with this version of the proto package. | |||
ProtoPackageIsVersion3 = true | |||
// ProtoPackageIsVersion2 is referenced from generated protocol buffer files | |||
// to assert that that code is compatible with this version of the proto package. | |||
ProtoPackageIsVersion2 = true | |||
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files | |||
// to assert that that code is compatible with this version of the proto package. | |||
ProtoPackageIsVersion1 = true | |||
) | |||
// InternalMessageInfo is a type used internally by generated .pb.go files. | |||
// This type is not intended to be used by non-generated code. | |||
// This type is not subject to any compatibility guarantee. | |||
type InternalMessageInfo struct { | |||
marshal *marshalInfo | |||
unmarshal *unmarshalInfo | |||
merge *mergeInfo | |||
discard *discardInfo | |||
} |
@ -1,181 +0,0 @@ | |||
// Go support for Protocol Buffers - Google's data interchange format | |||
// | |||
// Copyright 2010 The Go Authors. All rights reserved. | |||
// https://github.com/golang/protobuf | |||
// | |||
// 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. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// 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. | |||
package proto | |||
/* | |||
* Support for message sets. | |||
*/ | |||
import ( | |||
"errors" | |||
) | |||
// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID. | |||
// A message type ID is required for storing a protocol buffer in a message set. | |||
var errNoMessageTypeID = errors.New("proto does not have a message type ID") | |||
// The first two types (_MessageSet_Item and messageSet) | |||
// model what the protocol compiler produces for the following protocol message: | |||
// message MessageSet { | |||
// repeated group Item = 1 { | |||
// required int32 type_id = 2; | |||
// required string message = 3; | |||
// }; | |||
// } | |||
// That is the MessageSet wire format. We can't use a proto to generate these | |||
// because that would introduce a circular dependency between it and this package. | |||
type _MessageSet_Item struct { | |||
TypeId *int32 `protobuf:"varint,2,req,name=type_id"` | |||
Message []byte `protobuf:"bytes,3,req,name=message"` | |||
} | |||
type messageSet struct { | |||
Item []*_MessageSet_Item `protobuf:"group,1,rep"` | |||
XXX_unrecognized []byte | |||
// TODO: caching? | |||
} | |||
// Make sure messageSet is a Message. | |||
var _ Message = (*messageSet)(nil) | |||
// messageTypeIder is an interface satisfied by a protocol buffer type | |||
// that may be stored in a MessageSet. | |||
type messageTypeIder interface { | |||
MessageTypeId() int32 | |||
} | |||
func (ms *messageSet) find(pb Message) *_MessageSet_Item { | |||
mti, ok := pb.(messageTypeIder) | |||
if !ok { | |||
return nil | |||
} | |||
id := mti.MessageTypeId() | |||
for _, item := range ms.Item { | |||
if *item.TypeId == id { | |||
return item | |||
} | |||
} | |||
return nil | |||
} | |||
func (ms *messageSet) Has(pb Message) bool { | |||
return ms.find(pb) != nil | |||
} | |||
func (ms *messageSet) Unmarshal(pb Message) error { | |||
if item := ms.find(pb); item != nil { | |||
return Unmarshal(item.Message, pb) | |||
} | |||
if _, ok := pb.(messageTypeIder); !ok { | |||
return errNoMessageTypeID | |||
} | |||
return nil // TODO: return error instead? | |||
} | |||
func (ms *messageSet) Marshal(pb Message) error { | |||
msg, err := Marshal(pb) | |||
if err != nil { | |||
return err | |||
} | |||
if item := ms.find(pb); item != nil { | |||
// reuse existing item | |||
item.Message = msg | |||
return nil | |||
} | |||
mti, ok := pb.(messageTypeIder) | |||
if !ok { | |||
return errNoMessageTypeID | |||
} | |||
mtid := mti.MessageTypeId() | |||
ms.Item = append(ms.Item, &_MessageSet_Item{ | |||
TypeId: &mtid, | |||
Message: msg, | |||
}) | |||
return nil | |||
} | |||
func (ms *messageSet) Reset() { *ms = messageSet{} } | |||
func (ms *messageSet) String() string { return CompactTextString(ms) } | |||
func (*messageSet) ProtoMessage() {} | |||
// Support for the message_set_wire_format message option. | |||
func skipVarint(buf []byte) []byte { | |||
i := 0 | |||
for ; buf[i]&0x80 != 0; i++ { | |||
} | |||
return buf[i+1:] | |||
} | |||
// unmarshalMessageSet decodes the extension map encoded in buf in the message set wire format. | |||
// It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option. | |||
func unmarshalMessageSet(buf []byte, exts interface{}) error { | |||
var m map[int32]Extension | |||
switch exts := exts.(type) { | |||
case *XXX_InternalExtensions: | |||
m = exts.extensionsWrite() | |||
case map[int32]Extension: | |||
m = exts | |||
default: | |||
return errors.New("proto: not an extension map") | |||
} | |||
ms := new(messageSet) | |||
if err := Unmarshal(buf, ms); err != nil { | |||
return err | |||
} | |||
for _, item := range ms.Item { | |||
id := *item.TypeId | |||
msg := item.Message | |||
// Restore wire type and field number varint, plus length varint. | |||
// Be careful to preserve duplicate items. | |||
b := EncodeVarint(uint64(id)<<3 | WireBytes) | |||
if ext, ok := m[id]; ok { | |||
// Existing data; rip off the tag and length varint | |||
// so we join the new data correctly. | |||
// We can assume that ext.enc is set because we are unmarshaling. | |||
o := ext.enc[len(b):] // skip wire type and field number | |||
_, n := DecodeVarint(o) // calculate length of length varint | |||
o = o[n:] // skip length varint | |||
msg = append(o, msg...) // join old data and new data | |||
} | |||
b = append(b, EncodeVarint(uint64(len(msg)))...) | |||
b = append(b, msg...) | |||
m[id] = Extension{enc: b} | |||
} | |||
return nil | |||
} |
@ -1,360 +0,0 @@ | |||
// Go support for Protocol Buffers - Google's data interchange format | |||
// | |||
// Copyright 2012 The Go Authors. All rights reserved. | |||
// https://github.com/golang/protobuf | |||
// | |||
// 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. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// 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. | |||
// +build purego appengine js | |||
// This file contains an implementation of proto field accesses using package reflect. | |||
// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can | |||
// be used on App Engine. | |||
package proto | |||
import ( | |||
"reflect" | |||
"sync" | |||
) | |||
const unsafeAllowed = false | |||
// A field identifies a field in a struct, accessible from a pointer. | |||
// In this implementation, a field is identified by the sequence of field indices | |||
// passed to reflect's FieldByIndex. | |||
type field []int | |||
// toField returns a field equivalent to the given reflect field. | |||
func toField(f *reflect.StructField) field { | |||
return f.Index | |||
} | |||
// invalidField is an invalid field identifier. | |||
var invalidField = field(nil) | |||
// zeroField is a noop when calling pointer.offset. | |||
var zeroField = field([]int{}) | |||
// IsValid reports whether the field identifier is valid. | |||
func (f field) IsValid() bool { return f != nil } | |||
// The pointer type is for the table-driven decoder. | |||
// The implementation here uses a reflect.Value of pointer type to | |||
// create a generic pointer. In pointer_unsafe.go we use unsafe | |||
// instead of reflect to implement the same (but faster) interface. | |||
type pointer struct { | |||
v reflect.Value | |||
} | |||
// toPointer converts an interface of pointer type to a pointer | |||
// that points to the same target. | |||
func toPointer(i *Message) pointer { | |||
return pointer{v: reflect.ValueOf(*i)} | |||
} | |||
// toAddrPointer converts an interface to a pointer that points to | |||
// the interface data. | |||
func toAddrPointer(i *interface{}, isptr, deref bool) pointer { | |||
v := reflect.ValueOf(*i) | |||
u := reflect.New(v.Type()) | |||
u.Elem().Set(v) | |||
if deref { | |||
u = u.Elem() | |||
} | |||
return pointer{v: u} | |||
} | |||
// valToPointer converts v to a pointer. v must be of pointer type. | |||
func valToPointer(v reflect.Value) pointer { | |||
return pointer{v: v} | |||
} | |||
// offset converts from a pointer to a structure to a pointer to | |||
// one of its fields. | |||
func (p pointer) offset(f field) pointer { | |||
return pointer{v: p.v.Elem().FieldByIndex(f).Addr()} | |||
} | |||
func (p pointer) isNil() bool { | |||
return p.v.IsNil() | |||
} | |||
// grow updates the slice s in place to make it one element longer. | |||
// s must be addressable. | |||
// Returns the (addressable) new element. | |||
func grow(s reflect.Value) reflect.Value { | |||
n, m := s.Len(), s.Cap() | |||
if n < m { | |||
s.SetLen(n + 1) | |||
} else { | |||
s.Set(reflect.Append(s, reflect.Zero(s.Type().Elem()))) | |||
} | |||
return s.Index(n) | |||
} | |||
func (p pointer) toInt64() *int64 { | |||
return p.v.Interface().(*int64) | |||
} | |||
func (p pointer) toInt64Ptr() **int64 { | |||
return p.v.Interface().(**int64) | |||
} | |||
func (p pointer) toInt64Slice() *[]int64 { | |||
return p.v.Interface().(*[]int64) | |||
} | |||
var int32ptr = reflect.TypeOf((*int32)(nil)) | |||
func (p pointer) toInt32() *int32 { | |||
return p.v.Convert(int32ptr).Interface().(*int32) | |||
} | |||
// The toInt32Ptr/Slice methods don't work because of enums. | |||
// Instead, we must use set/get methods for the int32ptr/slice case. | |||
/* | |||
func (p pointer) toInt32Ptr() **int32 { | |||
return p.v.Interface().(**int32) | |||
} | |||
func (p pointer) toInt32Slice() *[]int32 { | |||
return p.v.Interface().(*[]int32) | |||
} | |||
*/ | |||
func (p pointer) getInt32Ptr() *int32 { | |||
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { | |||
// raw int32 type | |||
return p.v.Elem().Interface().(*int32) | |||
} | |||
// an enum | |||
return p.v.Elem().Convert(int32PtrType).Interface().(*int32) | |||
} | |||
func (p pointer) setInt32Ptr(v int32) { | |||
// Allocate value in a *int32. Possibly convert that to a *enum. | |||
// Then assign it to a **int32 or **enum. | |||
// Note: we can convert *int32 to *enum, but we can't convert | |||
// **int32 to **enum! | |||
p.v.Elem().Set(reflect.ValueOf(&v).Convert(p.v.Type().Elem())) | |||
} | |||
// getInt32Slice copies []int32 from p as a new slice. | |||
// This behavior differs from the implementation in pointer_unsafe.go. | |||
func (p pointer) getInt32Slice() []int32 { | |||
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { | |||
// raw int32 type | |||
return p.v.Elem().Interface().([]int32) | |||
} | |||
// an enum | |||
// Allocate a []int32, then assign []enum's values into it. | |||
// Note: we can't convert []enum to []int32. | |||
slice := p.v.Elem() | |||
s := make([]int32, slice.Len()) | |||
for i := 0; i < slice.Len(); i++ { | |||
s[i] = int32(slice.Index(i).Int()) | |||
} | |||
return s | |||
} | |||
// setInt32Slice copies []int32 into p as a new slice. | |||
// This behavior differs from the implementation in pointer_unsafe.go. | |||
func (p pointer) setInt32Slice(v []int32) { | |||
if p.v.Type().Elem().Elem() == reflect.TypeOf(int32(0)) { | |||
// raw int32 type | |||
p.v.Elem().Set(reflect.ValueOf(v)) | |||
return | |||
} | |||
// an enum | |||
// Allocate a []enum, then assign []int32's values into it. | |||
// Note: we can't convert []enum to []int32. | |||
slice := reflect.MakeSlice(p.v.Type().Elem(), len(v), cap(v)) | |||
for i, x := range v { | |||
slice.Index(i).SetInt(int64(x)) | |||
} | |||
p.v.Elem().Set(slice) | |||
} | |||
func (p pointer) appendInt32Slice(v int32) { | |||
grow(p.v.Elem()).SetInt(int64(v)) | |||
} | |||
func (p pointer) toUint64() *uint64 { | |||
return p.v.Interface().(*uint64) | |||
} | |||
func (p pointer) toUint64Ptr() **uint64 { | |||
return p.v.Interface().(**uint64) | |||
} | |||
func (p pointer) toUint64Slice() *[]uint64 { | |||
return p.v.Interface().(*[]uint64) | |||
} | |||
func (p pointer) toUint32() *uint32 { | |||
return p.v.Interface().(*uint32) | |||
} | |||
func (p pointer) toUint32Ptr() **uint32 { | |||
return p.v.Interface().(**uint32) | |||
} | |||
func (p pointer) toUint32Slice() *[]uint32 { | |||
return p.v.Interface().(*[]uint32) | |||
} | |||
func (p pointer) toBool() *bool { | |||
return p.v.Interface().(*bool) | |||
} | |||
func (p pointer) toBoolPtr() **bool { | |||
return p.v.Interface().(**bool) | |||
} | |||
func (p pointer) toBoolSlice() *[]bool { | |||
return p.v.Interface().(*[]bool) | |||
} | |||
func (p pointer) toFloat64() *float64 { | |||
return p.v.Interface().(*float64) | |||
} | |||
func (p pointer) toFloat64Ptr() **float64 { | |||
return p.v.Interface().(**float64) | |||
} | |||
func (p pointer) toFloat64Slice() *[]float64 { | |||
return p.v.Interface().(*[]float64) | |||
} | |||
func (p pointer) toFloat32() *float32 { | |||
return p.v.Interface().(*float32) | |||
} | |||
func (p pointer) toFloat32Ptr() **float32 { | |||
return p.v.Interface().(**float32) | |||
} | |||
func (p pointer) toFloat32Slice() *[]float32 { | |||
return p.v.Interface().(*[]float32) | |||
} | |||
func (p pointer) toString() *string { | |||
return p.v.Interface().(*string) | |||
} | |||
func (p pointer) toStringPtr() **string { | |||
return p.v.Interface().(**string) | |||
} | |||
func (p pointer) toStringSlice() *[]string { | |||
return p.v.Interface().(*[]string) | |||
} | |||
func (p pointer) toBytes() *[]byte { | |||
return p.v.Interface().(*[]byte) | |||
} | |||
func (p pointer) toBytesSlice() *[][]byte { | |||
return p.v.Interface().(*[][]byte) | |||
} | |||
func (p pointer) toExtensions() *XXX_InternalExtensions { | |||
return p.v.Interface().(*XXX_InternalExtensions) | |||
} | |||
func (p pointer) toOldExtensions() *map[int32]Extension { | |||
return p.v.Interface().(*map[int32]Extension) | |||
} | |||
func (p pointer) getPointer() pointer { | |||
return pointer{v: p.v.Elem()} | |||
} | |||
func (p pointer) setPointer(q pointer) { | |||
p.v.Elem().Set(q.v) | |||
} | |||
func (p pointer) appendPointer(q pointer) { | |||
grow(p.v.Elem()).Set(q.v) | |||
} | |||
// getPointerSlice copies []*T from p as a new []pointer. | |||
// This behavior differs from the implementation in pointer_unsafe.go. | |||
func (p pointer) getPointerSlice() []pointer { | |||
if p.v.IsNil() { | |||
return nil | |||
} | |||
n := p.v.Elem().Len() | |||
s := make([]pointer, n) | |||
for i := 0; i < n; i++ { | |||
s[i] = pointer{v: p.v.Elem().Index(i)} | |||
} | |||
return s | |||
} | |||
// setPointerSlice copies []pointer into p as a new []*T. | |||
// This behavior differs from the implementation in pointer_unsafe.go. | |||
func (p pointer) setPointerSlice(v []pointer) { | |||
if v == nil { | |||
p.v.Elem().Set(reflect.New(p.v.Elem().Type()).Elem()) | |||
return | |||
} | |||
s := reflect.MakeSlice(p.v.Elem().Type(), 0, len(v)) | |||
for _, p := range v { | |||
s = reflect.Append(s, p.v) | |||
} | |||
p.v.Elem().Set(s) | |||
} | |||
// getInterfacePointer returns a pointer that points to the | |||
// interface data of the interface pointed by p. | |||
func (p pointer) getInterfacePointer() pointer { | |||
if p.v.Elem().IsNil() { | |||
return pointer{v: p.v.Elem()} | |||
} | |||
return pointer{v: p.v.Elem().Elem().Elem().Field(0).Addr()} // *interface -> interface -> *struct -> struct | |||
} | |||
func (p pointer) asPointerTo(t reflect.Type) reflect.Value { | |||
// TODO: check that p.v.Type().Elem() == t? | |||
return p.v | |||
} | |||
func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo { | |||
atomicLock.Lock() | |||
defer atomicLock.Unlock() | |||
return *p | |||
} | |||
func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) { | |||
atomicLock.Lock() | |||
defer atomicLock.Unlock() | |||
*p = v | |||
} | |||
func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo { | |||
atomicLock.Lock() | |||
defer atomicLock.Unlock() | |||
return *p | |||
} | |||
func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) { | |||
atomicLock.Lock() | |||
defer atomicLock.Unlock() | |||
*p = v | |||
} | |||
func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo { | |||
atomicLock.Lock() | |||
defer atomicLock.Unlock() | |||
return *p | |||
} | |||
func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) { | |||
atomicLock.Lock() | |||
defer atomicLock.Unlock() | |||
*p = v | |||
} | |||
func atomicLoadDiscardInfo(p **discardInfo) *discardInfo { | |||
atomicLock.Lock() | |||
defer atomicLock.Unlock() | |||
return *p | |||
} | |||
func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) { | |||
atomicLock.Lock() | |||
defer atomicLock.Unlock() | |||
*p = v | |||
} | |||
var atomicLock sync.Mutex |
@ -1,313 +0,0 @@ | |||
// Go support for Protocol Buffers - Google's data interchange format | |||
// | |||
// Copyright 2012 The Go Authors. All rights reserved. | |||
// https://github.com/golang/protobuf | |||
// | |||
// 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. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// 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. | |||
// +build !purego,!appengine,!js | |||
// This file contains the implementation of the proto field accesses using package unsafe. | |||
package proto | |||
import ( | |||
"reflect" | |||
"sync/atomic" | |||
"unsafe" | |||
) | |||
const unsafeAllowed = true | |||
// A field identifies a field in a struct, accessible from a pointer. | |||
// In this implementation, a field is identified by its byte offset from the start of the struct. | |||
type field uintptr | |||
// toField returns a field equivalent to the given reflect field. | |||
func toField(f *reflect.StructField) field { | |||
return field(f.Offset) | |||
} | |||
// invalidField is an invalid field identifier. | |||
const invalidField = ^field(0) | |||
// zeroField is a noop when calling pointer.offset. | |||
const zeroField = field(0) | |||
// IsValid reports whether the field identifier is valid. | |||
func (f field) IsValid() bool { | |||
return f != invalidField | |||
} | |||
// The pointer type below is for the new table-driven encoder/decoder. | |||
// The implementation here uses unsafe.Pointer to create a generic pointer. | |||
// In pointer_reflect.go we use reflect instead of unsafe to implement | |||
// the same (but slower) interface. | |||
type pointer struct { | |||
p unsafe.Pointer | |||
} | |||
// size of pointer | |||
var ptrSize = unsafe.Sizeof(uintptr(0)) | |||
// toPointer converts an interface of pointer type to a pointer | |||
// that points to the same target. | |||
func toPointer(i *Message) pointer { | |||
// Super-tricky - read pointer out of data word of interface value. | |||
// Saves ~25ns over the equivalent: | |||
// return valToPointer(reflect.ValueOf(*i)) | |||
return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]} | |||
} | |||
// toAddrPointer converts an interface to a pointer that points to | |||
// the interface data. | |||
func toAddrPointer(i *interface{}, isptr, deref bool) (p pointer) { | |||
// Super-tricky - read or get the address of data word of interface value. | |||
if isptr { | |||
// The interface is of pointer type, thus it is a direct interface. | |||
// The data word is the pointer data itself. We take its address. | |||
p = pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)} | |||
} else { | |||
// The interface is not of pointer type. The data word is the pointer | |||
// to the data. | |||
p = pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]} | |||
} | |||
if deref { | |||
p.p = *(*unsafe.Pointer)(p.p) | |||
} | |||
return p | |||
} | |||
// valToPointer converts v to a pointer. v must be of pointer type. | |||
func valToPointer(v reflect.Value) pointer { | |||
return pointer{p: unsafe.Pointer(v.Pointer())} | |||
} | |||
// offset converts from a pointer to a structure to a pointer to | |||
// one of its fields. | |||
func (p pointer) offset(f field) pointer { | |||
// For safety, we should panic if !f.IsValid, however calling panic causes | |||
// this to no longer be inlineable, which is a serious performance cost. | |||
/* | |||
if !f.IsValid() { | |||
panic("invalid field") | |||
} | |||
*/ | |||
return pointer{p: unsafe.Pointer(uintptr(p.p) + uintptr(f))} | |||
} | |||
func (p pointer) isNil() bool { | |||
return p.p == nil | |||
} | |||
func (p pointer) toInt64() *int64 { | |||
return (*int64)(p.p) | |||
} | |||
func (p pointer) toInt64Ptr() **int64 { | |||
return (**int64)(p.p) | |||
} | |||
func (p pointer) toInt64Slice() *[]int64 { | |||
return (*[]int64)(p.p) | |||
} | |||
func (p pointer) toInt32() *int32 { | |||
return (*int32)(p.p) | |||
} | |||
// See pointer_reflect.go for why toInt32Ptr/Slice doesn't exist. | |||
/* | |||
func (p pointer) toInt32Ptr() **int32 { | |||
return (**int32)(p.p) | |||
} | |||
func (p pointer) toInt32Slice() *[]int32 { | |||
return (*[]int32)(p.p) | |||
} | |||
*/ | |||
func (p pointer) getInt32Ptr() *int32 { | |||
return *(**int32)(p.p) | |||
} | |||
func (p pointer) setInt32Ptr(v int32) { | |||
*(**int32)(p.p) = &v | |||
} | |||
// getInt32Slice loads a []int32 from p. | |||
// The value returned is aliased with the original slice. | |||
// This behavior differs from the implementation in pointer_reflect.go. | |||
func (p pointer) getInt32Slice() []int32 { | |||
return *(*[]int32)(p.p) | |||
} | |||
// setInt32Slice stores a []int32 to p. | |||
// The value set is aliased with the input slice. | |||
// This behavior differs from the implementation in pointer_reflect.go. | |||
func (p pointer) setInt32Slice(v []int32) { | |||
*(*[]int32)(p.p) = v | |||
} | |||
// TODO: Can we get rid of appendInt32Slice and use setInt32Slice instead? | |||
func (p pointer) appendInt32Slice(v int32) { | |||
s := (*[]int32)(p.p) | |||
*s = append(*s, v) | |||
} | |||
func (p pointer) toUint64() *uint64 { | |||
return (*uint64)(p.p) | |||
} | |||
func (p pointer) toUint64Ptr() **uint64 { | |||
return (**uint64)(p.p) | |||
} | |||
func (p pointer) toUint64Slice() *[]uint64 { | |||
return (*[]uint64)(p.p) | |||
} | |||
func (p pointer) toUint32() *uint32 { | |||
return (*uint32)(p.p) | |||
} | |||
func (p pointer) toUint32Ptr() **uint32 { | |||
return (**uint32)(p.p) | |||
} | |||
func (p pointer) toUint32Slice() *[]uint32 { | |||
return (*[]uint32)(p.p) | |||
} | |||
func (p pointer) toBool() *bool { | |||
return (*bool)(p.p) | |||
} | |||
func (p pointer) toBoolPtr() **bool { | |||
return (**bool)(p.p) | |||
} | |||
func (p pointer) toBoolSlice() *[]bool { | |||
return (*[]bool)(p.p) | |||
} | |||
func (p pointer) toFloat64() *float64 { | |||
return (*float64)(p.p) | |||
} | |||
func (p pointer) toFloat64Ptr() **float64 { | |||
return (**float64)(p.p) | |||
} | |||
func (p pointer) toFloat64Slice() *[]float64 { | |||
return (*[]float64)(p.p) | |||
} | |||
func (p pointer) toFloat32() *float32 { | |||
return (*float32)(p.p) | |||
} | |||
func (p pointer) toFloat32Ptr() **float32 { | |||
return (**float32)(p.p) | |||
} | |||
func (p pointer) toFloat32Slice() *[]float32 { | |||
return (*[]float32)(p.p) | |||
} | |||
func (p pointer) toString() *string { | |||
return (*string)(p.p) | |||
} | |||
func (p pointer) toStringPtr() **string { | |||
return (**string)(p.p) | |||
} | |||
func (p pointer) toStringSlice() *[]string { | |||
return (*[]string)(p.p) | |||
} | |||
func (p pointer) toBytes() *[]byte { | |||
return (*[]byte)(p.p) | |||
} | |||
func (p pointer) toBytesSlice() *[][]byte { | |||
return (*[][]byte)(p.p) | |||
} | |||
func (p pointer) toExtensions() *XXX_InternalExtensions { | |||
return (*XXX_InternalExtensions)(p.p) | |||
} | |||
func (p pointer) toOldExtensions() *map[int32]Extension { | |||
return (*map[int32]Extension)(p.p) | |||
} | |||
// getPointerSlice loads []*T from p as a []pointer. | |||
// The value returned is aliased with the original slice. | |||
// This behavior differs from the implementation in pointer_reflect.go. | |||
func (p pointer) getPointerSlice() []pointer { | |||
// Super-tricky - p should point to a []*T where T is a | |||
// message type. We load it as []pointer. | |||
return *(*[]pointer)(p.p) | |||
} | |||
// setPointerSlice stores []pointer into p as a []*T. | |||
// The value set is aliased with the input slice. | |||
// This behavior differs from the implementation in pointer_reflect.go. | |||
func (p pointer) setPointerSlice(v []pointer) { | |||
// Super-tricky - p should point to a []*T where T is a | |||
// message type. We store it as []pointer. | |||
*(*[]pointer)(p.p) = v | |||
} | |||
// getPointer loads the pointer at p and returns it. | |||
func (p pointer) getPointer() pointer { | |||
return pointer{p: *(*unsafe.Pointer)(p.p)} | |||
} | |||
// setPointer stores the pointer q at p. | |||
func (p pointer) setPointer(q pointer) { | |||
*(*unsafe.Pointer)(p.p) = q.p | |||
} | |||
// append q to the slice pointed to by p. | |||
func (p pointer) appendPointer(q pointer) { | |||
s := (*[]unsafe.Pointer)(p.p) | |||
*s = append(*s, q.p) | |||
} | |||
// getInterfacePointer returns a pointer that points to the | |||
// interface data of the interface pointed by p. | |||
func (p pointer) getInterfacePointer() pointer { | |||
// Super-tricky - read pointer out of data word of interface value. | |||
return pointer{p: (*(*[2]unsafe.Pointer)(p.p))[1]} | |||
} | |||
// asPointerTo returns a reflect.Value that is a pointer to an | |||
// object of type t stored at p. | |||
func (p pointer) asPointerTo(t reflect.Type) reflect.Value { | |||
return reflect.NewAt(t, p.p) | |||
} | |||
func atomicLoadUnmarshalInfo(p **unmarshalInfo) *unmarshalInfo { | |||
return (*unmarshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) | |||
} | |||
func atomicStoreUnmarshalInfo(p **unmarshalInfo, v *unmarshalInfo) { | |||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) | |||
} | |||
func atomicLoadMarshalInfo(p **marshalInfo) *marshalInfo { | |||
return (*marshalInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) | |||
} | |||
func atomicStoreMarshalInfo(p **marshalInfo, v *marshalInfo) { | |||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) | |||
} | |||
func atomicLoadMergeInfo(p **mergeInfo) *mergeInfo { | |||
return (*mergeInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) | |||
} | |||
func atomicStoreMergeInfo(p **mergeInfo, v *mergeInfo) { | |||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) | |||
} | |||
func atomicLoadDiscardInfo(p **discardInfo) *discardInfo { | |||
return (*discardInfo)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(p)))) | |||
} | |||
func atomicStoreDiscardInfo(p **discardInfo, v *discardInfo) { | |||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(p)), unsafe.Pointer(v)) | |||
} |
@ -0,0 +1,167 @@ | |||
// Copyright 2019 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// Package proto provides functionality for handling protocol buffer messages. | |||
// In particular, it provides marshaling and unmarshaling between a protobuf | |||
// message and the binary wire format. | |||
// | |||
// See https://developers.google.com/protocol-buffers/docs/gotutorial for | |||
// more information. | |||
// | |||
// Deprecated: Use the "google.golang.org/protobuf/proto" package instead. | |||
package proto | |||
import ( | |||
protoV2 "google.golang.org/protobuf/proto" | |||
"google.golang.org/protobuf/reflect/protoreflect" | |||
"google.golang.org/protobuf/runtime/protoiface" | |||
"google.golang.org/protobuf/runtime/protoimpl" | |||
) | |||
const ( | |||
ProtoPackageIsVersion1 = true | |||
ProtoPackageIsVersion2 = true | |||
ProtoPackageIsVersion3 = true | |||
ProtoPackageIsVersion4 = true | |||
) | |||
// GeneratedEnum is any enum type generated by protoc-gen-go | |||
// which is a named int32 kind. | |||
// This type exists for documentation purposes. | |||
type GeneratedEnum interface{} | |||
// GeneratedMessage is any message type generated by protoc-gen-go | |||
// which is a pointer to a named struct kind. | |||
// This type exists for documentation purposes. | |||
type GeneratedMessage interface{} | |||
// Message is a protocol buffer message. | |||
// | |||
// This is the v1 version of the message interface and is marginally better | |||
// than an empty interface as it lacks any method to programatically interact | |||
// with the contents of the message. | |||
// | |||
// A v2 message is declared in "google.golang.org/protobuf/proto".Message and | |||
// exposes protobuf reflection as a first-class feature of the interface. | |||
// | |||
// To convert a v1 message to a v2 message, use the MessageV2 function. | |||
// To convert a v2 message to a v1 message, use the MessageV1 function. | |||
type Message = protoiface.MessageV1 | |||
// MessageV1 converts either a v1 or v2 message to a v1 message. | |||
// It returns nil if m is nil. | |||
func MessageV1(m GeneratedMessage) protoiface.MessageV1 { | |||
return protoimpl.X.ProtoMessageV1Of(m) | |||
} | |||
// MessageV2 converts either a v1 or v2 message to a v2 message. | |||
// It returns nil if m is nil. | |||
func MessageV2(m GeneratedMessage) protoV2.Message { | |||
return protoimpl.X.ProtoMessageV2Of(m) | |||
} | |||
// MessageReflect returns a reflective view for a message. | |||
// It returns nil if m is nil. | |||
func MessageReflect(m Message) protoreflect.Message { | |||
return protoimpl.X.MessageOf(m) | |||
} | |||
// Marshaler is implemented by messages that can marshal themselves. | |||
// This interface is used by the following functions: Size, Marshal, | |||
// Buffer.Marshal, and Buffer.EncodeMessage. | |||
// | |||
// Deprecated: Do not implement. | |||
type Marshaler interface { | |||
// Marshal formats the encoded bytes of the message. | |||
// It should be deterministic and emit valid protobuf wire data. | |||
// The caller takes ownership of the returned buffer. | |||
Marshal() ([]byte, error) | |||
} | |||
// Unmarshaler is implemented by messages that can unmarshal themselves. | |||
// This interface is used by the following functions: Unmarshal, UnmarshalMerge, | |||
// Buffer.Unmarshal, Buffer.DecodeMessage, and Buffer.DecodeGroup. | |||
// | |||
// Deprecated: Do not implement. | |||
type Unmarshaler interface { | |||
// Unmarshal parses the encoded bytes of the protobuf wire input. | |||
// The provided buffer is only valid for during method call. | |||
// It should not reset the receiver message. | |||
Unmarshal([]byte) error | |||
} | |||
// Merger is implemented by messages that can merge themselves. | |||
// This interface is used by the following functions: Clone and Merge. | |||
// | |||
// Deprecated: Do not implement. | |||
type Merger interface { | |||
// Merge merges the contents of src into the receiver message. | |||
// It clones all data structures in src such that it aliases no mutable | |||
// memory referenced by src. | |||
Merge(src Message) | |||
} | |||
// RequiredNotSetError is an error type returned when | |||
// marshaling or unmarshaling a message with missing required fields. | |||
type RequiredNotSetError struct { | |||
err error | |||
} | |||
func (e *RequiredNotSetError) Error() string { | |||
if e.err != nil { | |||
return e.err.Error() | |||
} | |||
return "proto: required field not set" | |||
} | |||
func (e *RequiredNotSetError) RequiredNotSet() bool { | |||
return true | |||
} | |||
func checkRequiredNotSet(m protoV2.Message) error { | |||
if err := protoV2.CheckInitialized(m); err != nil { | |||
return &RequiredNotSetError{err: err} | |||
} | |||
return nil | |||
} | |||
// Clone returns a deep copy of src. | |||
func Clone(src Message) Message { | |||
return MessageV1(protoV2.Clone(MessageV2(src))) | |||
} | |||
// Merge merges src into dst, which must be messages of the same type. | |||
// | |||
// Populated scalar fields in src are copied to dst, while populated | |||
// singular messages in src are merged into dst by recursively calling Merge. | |||
// The elements of every list field in src is appended to the corresponded | |||
// list fields in dst. The entries of every map field in src is copied into | |||
// the corresponding map field in dst, possibly replacing existing entries. | |||
// The unknown fields of src are appended to the unknown fields of dst. | |||
func Merge(dst, src Message) { | |||
protoV2.Merge(MessageV2(dst), MessageV2(src)) | |||
} | |||
// Equal reports whether two messages are equal. | |||
// If two messages marshal to the same bytes under deterministic serialization, | |||
// then Equal is guaranteed to report true. | |||
// | |||
// Two messages are equal if they are the same protobuf message type, | |||
// have the same set of populated known and extension field values, | |||
// and the same set of unknown fields values. | |||
// | |||
// Scalar values are compared with the equivalent of the == operator in Go, | |||
// except bytes values which are compared using bytes.Equal and | |||
// floating point values which specially treat NaNs as equal. | |||
// Message values are compared by recursively calling Equal. | |||
// Lists are equal if each element value is also equal. | |||
// Maps are equal if they have the same set of keys, where the pair of values | |||
// for each key is also equal. | |||
func Equal(x, y Message) bool { | |||
return protoV2.Equal(MessageV2(x), MessageV2(y)) | |||
} | |||
func isMessageSet(md protoreflect.MessageDescriptor) bool { | |||
ms, ok := md.(interface{ IsMessageSet() bool }) | |||
return ok && ms.IsMessageSet() | |||
} |
@ -0,0 +1,323 @@ | |||
// Copyright 2019 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package proto | |||
import ( | |||
"bytes" | |||
"compress/gzip" | |||
"fmt" | |||
"io/ioutil" | |||
"reflect" | |||
"strings" | |||
"sync" | |||
"google.golang.org/protobuf/reflect/protoreflect" | |||
"google.golang.org/protobuf/reflect/protoregistry" | |||
"google.golang.org/protobuf/runtime/protoimpl" | |||
) | |||
// filePath is the path to the proto source file. | |||
type filePath = string // e.g., "google/protobuf/descriptor.proto" | |||
// fileDescGZIP is the compressed contents of the encoded FileDescriptorProto. | |||
type fileDescGZIP = []byte | |||
var fileCache sync.Map // map[filePath]fileDescGZIP | |||
// RegisterFile is called from generated code to register the compressed | |||
// FileDescriptorProto with the file path for a proto source file. | |||
// | |||
// Deprecated: Use protoregistry.GlobalFiles.Register instead. | |||
func RegisterFile(s filePath, d fileDescGZIP) { | |||
// Decompress the descriptor. | |||
zr, err := gzip.NewReader(bytes.NewReader(d)) | |||
if err != nil { | |||
panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err)) | |||
} | |||
b, err := ioutil.ReadAll(zr) | |||
if err != nil { | |||
panic(fmt.Sprintf("proto: invalid compressed file descriptor: %v", err)) | |||
} | |||
// Construct a protoreflect.FileDescriptor from the raw descriptor. | |||
// Note that DescBuilder.Build automatically registers the constructed | |||
// file descriptor with the v2 registry. | |||
protoimpl.DescBuilder{RawDescriptor: b}.Build() | |||
// Locally cache the raw descriptor form for the file. | |||
fileCache.Store(s, d) | |||
} | |||
// FileDescriptor returns the compressed FileDescriptorProto given the file path | |||
// for a proto source file. It returns nil if not found. | |||
// | |||
// Deprecated: Use protoregistry.GlobalFiles.RangeFilesByPath instead. | |||
func FileDescriptor(s filePath) fileDescGZIP { | |||
if v, ok := fileCache.Load(s); ok { | |||
return v.(fileDescGZIP) | |||
} | |||
// Find the descriptor in the v2 registry. | |||
var b []byte | |||
if fd, _ := protoregistry.GlobalFiles.FindFileByPath(s); fd != nil { | |||
if fd, ok := fd.(interface{ ProtoLegacyRawDesc() []byte }); ok { | |||
b = fd.ProtoLegacyRawDesc() | |||
} else { | |||
// TODO: Use protodesc.ToFileDescriptorProto to construct | |||
// a descriptorpb.FileDescriptorProto and marshal it. | |||
// However, doing so causes the proto package to have a dependency | |||
// on descriptorpb, leading to cyclic dependency issues. | |||
} | |||
} | |||
// Locally cache the raw descriptor form for the file. | |||
if len(b) > 0 { | |||
v, _ := fileCache.LoadOrStore(s, protoimpl.X.CompressGZIP(b)) | |||
return v.(fileDescGZIP) | |||
} | |||
return nil | |||
} | |||
// enumName is the name of an enum. For historical reasons, the enum name is | |||
// neither the full Go name nor the full protobuf name of the enum. | |||
// The name is the dot-separated combination of just the proto package that the | |||
// enum is declared within followed by the Go type name of the generated enum. | |||
type enumName = string // e.g., "my.proto.package.GoMessage_GoEnum" | |||
// enumsByName maps enum values by name to their numeric counterpart. | |||
type enumsByName = map[string]int32 | |||
// enumsByNumber maps enum values by number to their name counterpart. | |||
type enumsByNumber = map[int32]string | |||
var enumCache sync.Map // map[enumName]enumsByName | |||
var numFilesCache sync.Map // map[protoreflect.FullName]int | |||
// RegisterEnum is called from the generated code to register the mapping of | |||
// enum value names to enum numbers for the enum identified by s. | |||
// | |||
// Deprecated: Use protoregistry.GlobalTypes.Register instead. | |||
func RegisterEnum(s enumName, _ enumsByNumber, m enumsByName) { | |||
if _, ok := enumCache.Load(s); ok { | |||
panic("proto: duplicate enum registered: " + s) | |||
} | |||
enumCache.Store(s, m) | |||
// This does not forward registration to the v2 registry since this API | |||
// lacks sufficient information to construct a complete v2 enum descriptor. | |||
} | |||
// EnumValueMap returns the mapping from enum value names to enum numbers for | |||
// the enum of the given name. It returns nil if not found. | |||
// | |||
// Deprecated: Use protoregistry.GlobalTypes.FindEnumByName instead. | |||
func EnumValueMap(s enumName) enumsByName { | |||
if v, ok := enumCache.Load(s); ok { | |||
return v.(enumsByName) | |||
} | |||
// Check whether the cache is stale. If the number of files in the current | |||
// package differs, then it means that some enums may have been recently | |||
// registered upstream that we do not know about. | |||
var protoPkg protoreflect.FullName | |||
if i := strings.LastIndexByte(s, '.'); i >= 0 { | |||
protoPkg = protoreflect.FullName(s[:i]) | |||
} | |||
v, _ := numFilesCache.Load(protoPkg) | |||
numFiles, _ := v.(int) | |||
if protoregistry.GlobalFiles.NumFilesByPackage(protoPkg) == numFiles { | |||
return nil // cache is up-to-date; was not found earlier | |||
} | |||
// Update the enum cache for all enums declared in the given proto package. | |||
numFiles = 0 | |||
protoregistry.GlobalFiles.RangeFilesByPackage(protoPkg, func(fd protoreflect.FileDescriptor) bool { | |||
walkEnums(fd, func(ed protoreflect.EnumDescriptor) { | |||
name := protoimpl.X.LegacyEnumName(ed) | |||
if _, ok := enumCache.Load(name); !ok { | |||
m := make(enumsByName) | |||
evs := ed.Values() | |||
for i := evs.Len() - 1; i >= 0; i-- { | |||
ev := evs.Get(i) | |||
m[string(ev.Name())] = int32(ev.Number()) | |||
} | |||
enumCache.LoadOrStore(name, m) | |||
} | |||
}) | |||
numFiles++ | |||
return true | |||
}) | |||
numFilesCache.Store(protoPkg, numFiles) | |||
// Check cache again for enum map. | |||
if v, ok := enumCache.Load(s); ok { | |||
return v.(enumsByName) | |||
} | |||
return nil | |||
} | |||
// walkEnums recursively walks all enums declared in d. | |||
func walkEnums(d interface { | |||
Enums() protoreflect.EnumDescriptors | |||
Messages() protoreflect.MessageDescriptors | |||
}, f func(protoreflect.EnumDescriptor)) { | |||
eds := d.Enums() | |||
for i := eds.Len() - 1; i >= 0; i-- { | |||
f(eds.Get(i)) | |||
} | |||
mds := d.Messages() | |||
for i := mds.Len() - 1; i >= 0; i-- { | |||
walkEnums(mds.Get(i), f) | |||
} | |||
} | |||
// messageName is the full name of protobuf message. | |||
type messageName = string | |||
var messageTypeCache sync.Map // map[messageName]reflect.Type | |||
// RegisterType is called from generated code to register the message Go type | |||
// for a message of the given name. | |||
// | |||
// Deprecated: Use protoregistry.GlobalTypes.Register instead. | |||
func RegisterType(m Message, s messageName) { | |||
mt := protoimpl.X.LegacyMessageTypeOf(m, protoreflect.FullName(s)) | |||
if err := protoregistry.GlobalTypes.RegisterMessage(mt); err != nil { | |||
panic(err) | |||
} | |||
messageTypeCache.Store(s, reflect.TypeOf(m)) | |||
} | |||
// RegisterMapType is called from generated code to register the Go map type | |||
// for a protobuf message representing a map entry. | |||
// | |||
// Deprecated: Do not use. | |||
func RegisterMapType(m interface{}, s messageName) { | |||
t := reflect.TypeOf(m) | |||
if t.Kind() != reflect.Map { | |||
panic(fmt.Sprintf("invalid map kind: %v", t)) | |||
} | |||
if _, ok := messageTypeCache.Load(s); ok { | |||
panic(fmt.Errorf("proto: duplicate proto message registered: %s", s)) | |||
} | |||
messageTypeCache.Store(s, t) | |||
} | |||
// MessageType returns the message type for a named message. | |||
// It returns nil if not found. | |||
// | |||
// Deprecated: Use protoregistry.GlobalTypes.FindMessageByName instead. | |||
func MessageType(s messageName) reflect.Type { | |||
if v, ok := messageTypeCache.Load(s); ok { | |||
return v.(reflect.Type) | |||
} | |||
// Derive the message type from the v2 registry. | |||
var t reflect.Type | |||
if mt, _ := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(s)); mt != nil { | |||
t = messageGoType(mt) | |||
} | |||
// If we could not get a concrete type, it is possible that it is a | |||
// pseudo-message for a map entry. | |||
if t == nil { | |||
d, _ := protoregistry.GlobalFiles.FindDescriptorByName(protoreflect.FullName(s)) | |||
if md, _ := d.(protoreflect.MessageDescriptor); md != nil && md.IsMapEntry() { | |||
kt := goTypeForField(md.Fields().ByNumber(1)) | |||
vt := goTypeForField(md.Fields().ByNumber(2)) | |||
t = reflect.MapOf(kt, vt) | |||
} | |||
} | |||
// Locally cache the message type for the given name. | |||
if t != nil { | |||
v, _ := messageTypeCache.LoadOrStore(s, t) | |||
return v.(reflect.Type) | |||
} | |||
return nil | |||
} | |||
func goTypeForField(fd protoreflect.FieldDescriptor) reflect.Type { | |||
switch k := fd.Kind(); k { | |||
case protoreflect.EnumKind: | |||
if et, _ := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName()); et != nil { | |||
return enumGoType(et) | |||
} | |||
return reflect.TypeOf(protoreflect.EnumNumber(0)) | |||
case protoreflect.MessageKind, protoreflect.GroupKind: | |||
if mt, _ := protoregistry.GlobalTypes.FindMessageByName(fd.Message().FullName()); mt != nil { | |||
return messageGoType(mt) | |||
} | |||
return reflect.TypeOf((*protoreflect.Message)(nil)).Elem() | |||
default: | |||
return reflect.TypeOf(fd.Default().Interface()) | |||
} | |||
} | |||
func enumGoType(et protoreflect.EnumType) reflect.Type { | |||
return reflect.TypeOf(et.New(0)) | |||
} | |||
func messageGoType(mt protoreflect.MessageType) reflect.Type { | |||
return reflect.TypeOf(MessageV1(mt.Zero().Interface())) | |||
} | |||
// MessageName returns the full protobuf name for the given message type. | |||
// | |||
// Deprecated: Use protoreflect.MessageDescriptor.FullName instead. | |||
func MessageName(m Message) messageName { | |||
if m == nil { | |||
return "" | |||
} | |||
if m, ok := m.(interface{ XXX_MessageName() messageName }); ok { | |||
return m.XXX_MessageName() | |||
} | |||
return messageName(protoimpl.X.MessageDescriptorOf(m).FullName()) | |||
} | |||
// RegisterExtension is called from the generated code to register | |||
// the extension descriptor. | |||
// | |||
// Deprecated: Use protoregistry.GlobalTypes.Register instead. | |||
func RegisterExtension(d *ExtensionDesc) { | |||
if err := protoregistry.GlobalTypes.RegisterExtension(d); err != nil { | |||
panic(err) | |||
} | |||
} | |||
type extensionsByNumber = map[int32]*ExtensionDesc | |||
var extensionCache sync.Map // map[messageName]extensionsByNumber | |||
// RegisteredExtensions returns a map of the registered extensions for the | |||
// provided protobuf message, indexed by the extension field number. | |||
// | |||
// Deprecated: Use protoregistry.GlobalTypes.RangeExtensionsByMessage instead. | |||
func RegisteredExtensions(m Message) extensionsByNumber { | |||
// Check whether the cache is stale. If the number of extensions for | |||
// the given message differs, then it means that some extensions were | |||
// recently registered upstream that we do not know about. | |||
s := MessageName(m) | |||
v, _ := extensionCache.Load(s) | |||
xs, _ := v.(extensionsByNumber) | |||
if protoregistry.GlobalTypes.NumExtensionsByMessage(protoreflect.FullName(s)) == len(xs) { | |||
return xs // cache is up-to-date | |||
} | |||
// Cache is stale, re-compute the extensions map. | |||
xs = make(extensionsByNumber) | |||
protoregistry.GlobalTypes.RangeExtensionsByMessage(protoreflect.FullName(s), func(xt protoreflect.ExtensionType) bool { | |||
if xd, ok := xt.(*ExtensionDesc); ok { | |||
xs[int32(xt.TypeDescriptor().Number())] = xd | |||
} else { | |||
// TODO: This implies that the protoreflect.ExtensionType is a | |||
// custom type not generated by protoc-gen-go. We could try and | |||
// convert the type to an ExtensionDesc. | |||
} | |||
return true | |||
}) | |||
extensionCache.Store(s, xs) | |||
return xs | |||
} |
@ -1,654 +0,0 @@ | |||
// Go support for Protocol Buffers - Google's data interchange format | |||
// | |||
// Copyright 2016 The Go Authors. All rights reserved. | |||
// https://github.com/golang/protobuf | |||
// | |||
// 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. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// 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. | |||
package proto | |||
import ( | |||
"fmt" | |||
"reflect" | |||
"strings" | |||
"sync" | |||
"sync/atomic" | |||
) | |||
// Merge merges the src message into dst. | |||
// This assumes that dst and src of the same type and are non-nil. | |||
func (a *InternalMessageInfo) Merge(dst, src Message) { | |||
mi := atomicLoadMergeInfo(&a.merge) | |||
if mi == nil { | |||
mi = getMergeInfo(reflect.TypeOf(dst).Elem()) | |||
atomicStoreMergeInfo(&a.merge, mi) | |||
} | |||
mi.merge(toPointer(&dst), toPointer(&src)) | |||
} | |||
type mergeInfo struct { | |||
typ reflect.Type | |||
initialized int32 // 0: only typ is valid, 1: everything is valid | |||
lock sync.Mutex | |||
fields []mergeFieldInfo | |||
unrecognized field // Offset of XXX_unrecognized | |||
} | |||
type mergeFieldInfo struct { | |||
field field // Offset of field, guaranteed to be valid | |||
// isPointer reports whether the value in the field is a pointer. | |||
// This is true for the following situations: | |||
// * Pointer to struct | |||
// * Pointer to basic type (proto2 only) | |||
// * Slice (first value in slice header is a pointer) | |||
// * String (first value in string header is a pointer) | |||
isPointer bool | |||
// basicWidth reports the width of the field assuming that it is directly | |||
// embedded in the struct (as is the case for basic types in proto3). | |||
// The possible values are: | |||
// 0: invalid | |||
// 1: bool | |||
// 4: int32, uint32, float32 | |||
// 8: int64, uint64, float64 | |||
basicWidth int | |||
// Where dst and src are pointers to the types being merged. | |||
merge func(dst, src pointer) | |||
} | |||
var ( | |||
mergeInfoMap = map[reflect.Type]*mergeInfo{} | |||
mergeInfoLock sync.Mutex | |||
) | |||
func getMergeInfo(t reflect.Type) *mergeInfo { | |||
mergeInfoLock.Lock() | |||
defer mergeInfoLock.Unlock() | |||
mi := mergeInfoMap[t] | |||
if mi == nil { | |||
mi = &mergeInfo{typ: t} | |||
mergeInfoMap[t] = mi | |||
} | |||
return mi | |||
} | |||
// merge merges src into dst assuming they are both of type *mi.typ. | |||
func (mi *mergeInfo) merge(dst, src pointer) { | |||
if dst.isNil() { | |||
panic("proto: nil destination") | |||
} | |||
if src.isNil() { | |||
return // Nothing to do. | |||
} | |||
if atomic.LoadInt32(&mi.initialized) == 0 { | |||
mi.computeMergeInfo() | |||
} | |||
for _, fi := range mi.fields { | |||
sfp := src.offset(fi.field) | |||
// As an optimization, we can avoid the merge function call cost | |||
// if we know for sure that the source will have no effect | |||
// by checking if it is the zero value. | |||
if unsafeAllowed { | |||
if fi.isPointer && sfp.getPointer().isNil() { // Could be slice or string | |||
continue | |||
} | |||
if fi.basicWidth > 0 { | |||
switch { | |||
case fi.basicWidth == 1 && !*sfp.toBool(): | |||
continue | |||
case fi.basicWidth == 4 && *sfp.toUint32() == 0: | |||
continue | |||
case fi.basicWidth == 8 && *sfp.toUint64() == 0: | |||
continue | |||
} | |||
} | |||
} | |||
dfp := dst.offset(fi.field) | |||
fi.merge(dfp, sfp) | |||
} | |||
// TODO: Make this faster? | |||
out := dst.asPointerTo(mi.typ).Elem() | |||
in := src.asPointerTo(mi.typ).Elem() | |||
if emIn, err := extendable(in.Addr().Interface()); err == nil { | |||
emOut, _ := extendable(out.Addr().Interface()) | |||
mIn, muIn := emIn.extensionsRead() | |||
if mIn != nil { | |||
mOut := emOut.extensionsWrite() | |||
muIn.Lock() | |||
mergeExtension(mOut, mIn) | |||
muIn.Unlock() | |||
} | |||
} | |||
if mi.unrecognized.IsValid() { | |||
if b := *src.offset(mi.unrecognized).toBytes(); len(b) > 0 { | |||
*dst.offset(mi.unrecognized).toBytes() = append([]byte(nil), b...) | |||
} | |||
} | |||
} | |||
func (mi *mergeInfo) computeMergeInfo() { | |||
mi.lock.Lock() | |||
defer mi.lock.Unlock() | |||
if mi.initialized != 0 { | |||
return | |||
} | |||
t := mi.typ | |||
n := t.NumField() | |||
props := GetProperties(t) | |||
for i := 0; i < n; i++ { | |||
f := t.Field(i) | |||
if strings.HasPrefix(f.Name, "XXX_") { | |||
continue | |||
} | |||
mfi := mergeFieldInfo{field: toField(&f)} | |||
tf := f.Type | |||
// As an optimization, we can avoid the merge function call cost | |||
// if we know for sure that the source will have no effect | |||
// by checking if it is the zero value. | |||
if unsafeAllowed { | |||
switch tf.Kind() { | |||
case reflect.Ptr, reflect.Slice, reflect.String: | |||
// As a special case, we assume slices and strings are pointers | |||
// since we know that the first field in the SliceSlice or | |||
// StringHeader is a data pointer. | |||
mfi.isPointer = true | |||
case reflect.Bool: | |||
mfi.basicWidth = 1 | |||
case reflect.Int32, reflect.Uint32, reflect.Float32: | |||
mfi.basicWidth = 4 | |||
case reflect.Int64, reflect.Uint64, reflect.Float64: | |||
mfi.basicWidth = 8 | |||
} | |||
} | |||
// Unwrap tf to get at its most basic type. | |||
var isPointer, isSlice bool | |||
if tf.Kind() == reflect.Slice && tf.Elem().Kind() != reflect.Uint8 { | |||
isSlice = true | |||
tf = tf.Elem() | |||
} | |||
if tf.Kind() == reflect.Ptr { | |||
isPointer = true | |||
tf = tf.Elem() | |||
} | |||
if isPointer && isSlice && tf.Kind() != reflect.Struct { | |||
panic("both pointer and slice for basic type in " + tf.Name()) | |||
} | |||
switch tf.Kind() { | |||
case reflect.Int32: | |||
switch { | |||
case isSlice: // E.g., []int32 | |||
mfi.merge = func(dst, src pointer) { | |||
// NOTE: toInt32Slice is not defined (see pointer_reflect.go). | |||
/* | |||
sfsp := src.toInt32Slice() | |||
if *sfsp != nil { | |||
dfsp := dst.toInt32Slice() | |||
*dfsp = append(*dfsp, *sfsp...) | |||
if *dfsp == nil { | |||
*dfsp = []int64{} | |||
} | |||
} | |||
*/ | |||
sfs := src.getInt32Slice() | |||
if sfs != nil { | |||
dfs := dst.getInt32Slice() | |||
dfs = append(dfs, sfs...) | |||
if dfs == nil { | |||
dfs = []int32{} | |||
} | |||
dst.setInt32Slice(dfs) | |||
} | |||
} | |||
case isPointer: // E.g., *int32 | |||
mfi.merge = func(dst, src pointer) { | |||
// NOTE: toInt32Ptr is not defined (see pointer_reflect.go). | |||
/* | |||
sfpp := src.toInt32Ptr() | |||
if *sfpp != nil { | |||
dfpp := dst.toInt32Ptr() | |||
if *dfpp == nil { | |||
*dfpp = Int32(**sfpp) | |||
} else { | |||
**dfpp = **sfpp | |||
} | |||
} | |||
*/ | |||
sfp := src.getInt32Ptr() | |||
if sfp != nil { | |||
dfp := dst.getInt32Ptr() | |||
if dfp == nil { | |||
dst.setInt32Ptr(*sfp) | |||
} else { | |||
*dfp = *sfp | |||
} | |||
} | |||
} | |||
default: // E.g., int32 | |||
mfi.merge = func(dst, src pointer) { | |||
if v := *src.toInt32(); v != 0 { | |||
*dst.toInt32() = v | |||
} | |||
} | |||
} | |||
case reflect.Int64: | |||
switch { | |||
case isSlice: // E.g., []int64 | |||
mfi.merge = func(dst, src pointer) { | |||
sfsp := src.toInt64Slice() | |||
if *sfsp != nil { | |||
dfsp := dst.toInt64Slice() | |||
*dfsp = append(*dfsp, *sfsp...) | |||
if *dfsp == nil { | |||
*dfsp = []int64{} | |||
} | |||
} | |||
} | |||
case isPointer: // E.g., *int64 | |||
mfi.merge = func(dst, src pointer) { | |||
sfpp := src.toInt64Ptr() | |||
if *sfpp != nil { | |||
dfpp := dst.toInt64Ptr() | |||
if *dfpp == nil { | |||
*dfpp = Int64(**sfpp) | |||
} else { | |||
**dfpp = **sfpp | |||
} | |||
} | |||
} | |||
default: // E.g., int64 | |||
mfi.merge = func(dst, src pointer) { | |||
if v := *src.toInt64(); v != 0 { | |||
*dst.toInt64() = v | |||
} | |||
} | |||
} | |||
case reflect.Uint32: | |||
switch { | |||
case isSlice: // E.g., []uint32 | |||
mfi.merge = func(dst, src pointer) { | |||
sfsp := src.toUint32Slice() | |||
if *sfsp != nil { | |||
dfsp := dst.toUint32Slice() | |||
*dfsp = append(*dfsp, *sfsp...) | |||
if *dfsp == nil { | |||
*dfsp = []uint32{} | |||
} | |||
} | |||
} | |||
case isPointer: // E.g., *uint32 | |||
mfi.merge = func(dst, src pointer) { | |||
sfpp := src.toUint32Ptr() | |||
if *sfpp != nil { | |||
dfpp := dst.toUint32Ptr() | |||
if *dfpp == nil { | |||
*dfpp = Uint32(**sfpp) | |||
} else { | |||
**dfpp = **sfpp | |||
} | |||
} | |||
} | |||
default: // E.g., uint32 | |||
mfi.merge = func(dst, src pointer) { | |||
if v := *src.toUint32(); v != 0 { | |||
*dst.toUint32() = v | |||
} | |||
} | |||
} | |||
case reflect.Uint64: | |||
switch { | |||
case isSlice: // E.g., []uint64 | |||
mfi.merge = func(dst, src pointer) { | |||
sfsp := src.toUint64Slice() | |||
if *sfsp != nil { | |||
dfsp := dst.toUint64Slice() | |||
*dfsp = append(*dfsp, *sfsp...) | |||
if *dfsp == nil { | |||
*dfsp = []uint64{} | |||
} | |||
} | |||
} | |||
case isPointer: // E.g., *uint64 | |||
mfi.merge = func(dst, src pointer) { | |||
sfpp := src.toUint64Ptr() | |||
if *sfpp != nil { | |||
dfpp := dst.toUint64Ptr() | |||
if *dfpp == nil { | |||
*dfpp = Uint64(**sfpp) | |||
} else { | |||
**dfpp = **sfpp | |||
} | |||
} | |||
} | |||
default: // E.g., uint64 | |||
mfi.merge = func(dst, src pointer) { | |||
if v := *src.toUint64(); v != 0 { | |||
*dst.toUint64() = v | |||
} | |||
} | |||
} | |||
case reflect.Float32: | |||
switch { | |||
case isSlice: // E.g., []float32 | |||
mfi.merge = func(dst, src pointer) { | |||
sfsp := src.toFloat32Slice() | |||
if *sfsp != nil { | |||
dfsp := dst.toFloat32Slice() | |||
*dfsp = append(*dfsp, *sfsp...) | |||
if *dfsp == nil { | |||
*dfsp = []float32{} | |||
} | |||
} | |||
} | |||
case isPointer: // E.g., *float32 | |||
mfi.merge = func(dst, src pointer) { | |||
sfpp := src.toFloat32Ptr() | |||
if *sfpp != nil { | |||
dfpp := dst.toFloat32Ptr() | |||
if *dfpp == nil { | |||
*dfpp = Float32(**sfpp) | |||
} else { | |||
**dfpp = **sfpp | |||
} | |||
} | |||
} | |||
default: // E.g., float32 | |||
mfi.merge = func(dst, src pointer) { | |||
if v := *src.toFloat32(); v != 0 { | |||
*dst.toFloat32() = v | |||
} | |||
} | |||
} | |||
case reflect.Float64: | |||
switch { | |||
case isSlice: // E.g., []float64 | |||
mfi.merge = func(dst, src pointer) { | |||
sfsp := src.toFloat64Slice() | |||
if *sfsp != nil { | |||
dfsp := dst.toFloat64Slice() | |||
*dfsp = append(*dfsp, *sfsp...) | |||
if *dfsp == nil { | |||
*dfsp = []float64{} | |||
} | |||
} | |||
} | |||
case isPointer: // E.g., *float64 | |||
mfi.merge = func(dst, src pointer) { | |||
sfpp := src.toFloat64Ptr() | |||
if *sfpp != nil { | |||
dfpp := dst.toFloat64Ptr() | |||
if *dfpp == nil { | |||
*dfpp = Float64(**sfpp) | |||
} else { | |||
**dfpp = **sfpp | |||
} | |||
} | |||
} | |||
default: // E.g., float64 | |||
mfi.merge = func(dst, src pointer) { | |||
if v := *src.toFloat64(); v != 0 { | |||
*dst.toFloat64() = v | |||
} | |||
} | |||
} | |||
case reflect.Bool: | |||
switch { | |||
case isSlice: // E.g., []bool | |||
mfi.merge = func(dst, src pointer) { | |||
sfsp := src.toBoolSlice() | |||
if *sfsp != nil { | |||
dfsp := dst.toBoolSlice() | |||
*dfsp = append(*dfsp, *sfsp...) | |||
if *dfsp == nil { | |||
*dfsp = []bool{} | |||
} | |||
} | |||
} | |||
case isPointer: // E.g., *bool | |||
mfi.merge = func(dst, src pointer) { | |||
sfpp := src.toBoolPtr() | |||
if *sfpp != nil { | |||
dfpp := dst.toBoolPtr() | |||
if *dfpp == nil { | |||
*dfpp = Bool(**sfpp) | |||
} else { | |||
**dfpp = **sfpp | |||
} | |||
} | |||
} | |||
default: // E.g., bool | |||
mfi.merge = func(dst, src pointer) { | |||
if v := *src.toBool(); v { | |||
*dst.toBool() = v | |||
} | |||
} | |||
} | |||
case reflect.String: | |||
switch { | |||
case isSlice: // E.g., []string | |||
mfi.merge = func(dst, src pointer) { | |||
sfsp := src.toStringSlice() | |||
if *sfsp != nil { | |||
dfsp := dst.toStringSlice() | |||
*dfsp = append(*dfsp, *sfsp...) | |||
if *dfsp == nil { | |||
*dfsp = []string{} | |||
} | |||
} | |||
} | |||
case isPointer: // E.g., *string | |||
mfi.merge = func(dst, src pointer) { | |||
sfpp := src.toStringPtr() | |||
if *sfpp != nil { | |||
dfpp := dst.toStringPtr() | |||
if *dfpp == nil { | |||
*dfpp = String(**sfpp) | |||
} else { | |||
**dfpp = **sfpp | |||
} | |||
} | |||
} | |||
default: // E.g., string | |||
mfi.merge = func(dst, src pointer) { | |||
if v := *src.toString(); v != "" { | |||
*dst.toString() = v | |||
} | |||
} | |||
} | |||
case reflect.Slice: | |||
isProto3 := props.Prop[i].proto3 | |||
switch { | |||
case isPointer: | |||
panic("bad pointer in byte slice case in " + tf.Name()) | |||
case tf.Elem().Kind() != reflect.Uint8: | |||
panic("bad element kind in byte slice case in " + tf.Name()) | |||
case isSlice: // E.g., [][]byte | |||
mfi.merge = func(dst, src pointer) { | |||
sbsp := src.toBytesSlice() | |||
if *sbsp != nil { | |||
dbsp := dst.toBytesSlice() | |||
for _, sb := range *sbsp { | |||
if sb == nil { | |||
*dbsp = append(*dbsp, nil) | |||
} else { | |||
*dbsp = append(*dbsp, append([]byte{}, sb...)) | |||
} | |||
} | |||
if *dbsp == nil { | |||
*dbsp = [][]byte{} | |||
} | |||
} | |||
} | |||
default: // E.g., []byte | |||
mfi.merge = func(dst, src pointer) { | |||
sbp := src.toBytes() | |||
if *sbp != nil { | |||
dbp := dst.toBytes() | |||
if !isProto3 || len(*sbp) > 0 { | |||
*dbp = append([]byte{}, *sbp...) | |||
} | |||
} | |||
} | |||
} | |||
case reflect.Struct: | |||
switch { | |||
case !isPointer: | |||
panic(fmt.Sprintf("message field %s without pointer", tf)) | |||
case isSlice: // E.g., []*pb.T | |||
mi := getMergeInfo(tf) | |||
mfi.merge = func(dst, src pointer) { | |||
sps := src.getPointerSlice() | |||
if sps != nil { | |||
dps := dst.getPointerSlice() | |||
for _, sp := range sps { | |||
var dp pointer | |||
if !sp.isNil() { | |||
dp = valToPointer(reflect.New(tf)) | |||
mi.merge(dp, sp) | |||
} | |||
dps = append(dps, dp) | |||
} | |||
if dps == nil { | |||
dps = []pointer{} | |||
} | |||
dst.setPointerSlice(dps) | |||
} | |||
} | |||
default: // E.g., *pb.T | |||
mi := getMergeInfo(tf) | |||
mfi.merge = func(dst, src pointer) { | |||
sp := src.getPointer() | |||
if !sp.isNil() { | |||
dp := dst.getPointer() | |||
if dp.isNil() { | |||
dp = valToPointer(reflect.New(tf)) | |||
dst.setPointer(dp) | |||
} | |||
mi.merge(dp, sp) | |||
} | |||
} | |||
} | |||
case reflect.Map: | |||
switch { | |||
case isPointer || isSlice: | |||
panic("bad pointer or slice in map case in " + tf.Name()) | |||
default: // E.g., map[K]V | |||
mfi.merge = func(dst, src pointer) { | |||
sm := src.asPointerTo(tf).Elem() | |||
if sm.Len() == 0 { | |||
return | |||
} | |||
dm := dst.asPointerTo(tf).Elem() | |||
if dm.IsNil() { | |||
dm.Set(reflect.MakeMap(tf)) | |||
} | |||
switch tf.Elem().Kind() { | |||
case reflect.Ptr: // Proto struct (e.g., *T) | |||
for _, key := range sm.MapKeys() { | |||
val := sm.MapIndex(key) | |||
val = reflect.ValueOf(Clone(val.Interface().(Message))) | |||
dm.SetMapIndex(key, val) | |||
} | |||
case reflect.Slice: // E.g. Bytes type (e.g., []byte) | |||
for _, key := range sm.MapKeys() { | |||
val := sm.MapIndex(key) | |||
val = reflect.ValueOf(append([]byte{}, val.Bytes()...)) | |||
dm.SetMapIndex(key, val) | |||
} | |||
default: // Basic type (e.g., string) | |||
for _, key := range sm.MapKeys() { | |||
val := sm.MapIndex(key) | |||
dm.SetMapIndex(key, val) | |||
} | |||
} | |||
} | |||
} | |||
case reflect.Interface: | |||
// Must be oneof field. | |||
switch { | |||
case isPointer || isSlice: | |||
panic("bad pointer or slice in interface case in " + tf.Name()) | |||
default: // E.g., interface{} | |||
// TODO: Make this faster? | |||
mfi.merge = func(dst, src pointer) { | |||
su := src.asPointerTo(tf).Elem() | |||
if !su.IsNil() { | |||
du := dst.asPointerTo(tf).Elem() | |||
typ := su.Elem().Type() | |||
if du.IsNil() || du.Elem().Type() != typ { | |||
du.Set(reflect.New(typ.Elem())) // Initialize interface if empty | |||
} | |||
sv := su.Elem().Elem().Field(0) | |||
if sv.Kind() == reflect.Ptr && sv.IsNil() { | |||
return | |||
} | |||
dv := du.Elem().Elem().Field(0) | |||
if dv.Kind() == reflect.Ptr && dv.IsNil() { | |||
dv.Set(reflect.New(sv.Type().Elem())) // Initialize proto message if empty | |||
} | |||
switch sv.Type().Kind() { | |||
case reflect.Ptr: // Proto struct (e.g., *T) | |||
Merge(dv.Interface().(Message), sv.Interface().(Message)) | |||
case reflect.Slice: // E.g. Bytes type (e.g., []byte) | |||
dv.Set(reflect.ValueOf(append([]byte{}, sv.Bytes()...))) | |||
default: // Basic type (e.g., string) | |||
dv.Set(sv) | |||
} | |||
} | |||
} | |||
} | |||
default: | |||
panic(fmt.Sprintf("merger not found for type:%s", tf)) | |||
} | |||
mi.fields = append(mi.fields, mfi) | |||
} | |||
mi.unrecognized = invalidField | |||
if f, ok := t.FieldByName("XXX_unrecognized"); ok { | |||
if f.Type != reflect.TypeOf([]byte{}) { | |||
panic("expected XXX_unrecognized to be of type []byte") | |||
} | |||
mi.unrecognized = toField(&f) | |||
} | |||
atomic.StoreInt32(&mi.initialized, 1) | |||
} |
@ -1,845 +0,0 @@ | |||
// Go support for Protocol Buffers - Google's data interchange format | |||
// | |||
// Copyright 2010 The Go Authors. All rights reserved. | |||
// https://github.com/golang/protobuf | |||
// | |||
// 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. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// 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. | |||
package proto | |||
// Functions for writing the text protocol buffer format. | |||
import ( | |||
"bufio" | |||
"bytes" | |||
"encoding" | |||
"errors" | |||
"fmt" | |||
"io" | |||
"log" | |||
"math" | |||
"reflect" | |||
"sort" | |||
"strings" | |||
) | |||
var ( | |||
newline = []byte("\n") | |||
spaces = []byte(" ") | |||
endBraceNewline = []byte("}\n") | |||
backslashN = []byte{'\\', 'n'} | |||
backslashR = []byte{'\\', 'r'} | |||
backslashT = []byte{'\\', 't'} | |||
backslashDQ = []byte{'\\', '"'} | |||
backslashBS = []byte{'\\', '\\'} | |||
posInf = []byte("inf") | |||
negInf = []byte("-inf") | |||
nan = []byte("nan") | |||
) | |||
type writer interface { | |||
io.Writer | |||
WriteByte(byte) error | |||
} | |||
// textWriter is an io.Writer that tracks its indentation level. | |||
type textWriter struct { | |||
ind int | |||
complete bool // if the current position is a complete line | |||
compact bool // whether to write out as a one-liner | |||
w writer | |||
} | |||
func (w *textWriter) WriteString(s string) (n int, err error) { | |||
if !strings.Contains(s, "\n") { | |||
if !w.compact && w.complete { | |||
w.writeIndent() | |||
} | |||
w.complete = false | |||
return io.WriteString(w.w, s) | |||
} | |||
// WriteString is typically called without newlines, so this | |||
// codepath and its copy are rare. We copy to avoid | |||
// duplicating all of Write's logic here. | |||
return w.Write([]byte(s)) | |||
} | |||
func (w *textWriter) Write(p []byte) (n int, err error) { | |||
newlines := bytes.Count(p, newline) | |||
if newlines == 0 { | |||
if !w.compact && w.complete { | |||
w.writeIndent() | |||
} | |||
n, err = w.w.Write(p) | |||
w.complete = false | |||
return n, err | |||
} | |||
frags := bytes.SplitN(p, newline, newlines+1) | |||
if w.compact { | |||
for i, frag := range frags { | |||
if i > 0 { | |||
if err := w.w.WriteByte(' '); err != nil { | |||
return n, err | |||
} | |||
n++ | |||
} | |||
nn, err := w.w.Write(frag) | |||
n += nn | |||
if err != nil { | |||
return n, err | |||
} | |||
} | |||
return n, nil | |||
} | |||
for i, frag := range frags { | |||
if w.complete { | |||
w.writeIndent() | |||
} | |||
nn, err := w.w.Write(frag) | |||
n += nn | |||
if err != nil { | |||
return n, err | |||
} | |||
if i+1 < len(frags) { | |||
if err := w.w.WriteByte('\n'); err != nil { | |||
return n, err | |||
} | |||
n++ | |||
} | |||
} | |||
w.complete = len(frags[len(frags)-1]) == 0 | |||
return n, nil | |||
} | |||
func (w *textWriter) WriteByte(c byte) error { | |||
if w.compact && c == '\n' { | |||
c = ' ' | |||
} | |||
if !w.compact && w.complete { | |||
w.writeIndent() | |||
} | |||
err := w.w.WriteByte(c) | |||
w.complete = c == '\n' | |||
return err | |||
} | |||
func (w *textWriter) indent() { w.ind++ } | |||
func (w *textWriter) unindent() { | |||
if w.ind == 0 { | |||
log.Print("proto: textWriter unindented too far") | |||
return | |||
} | |||
w.ind-- | |||
} | |||
func writeName(w *textWriter, props *Properties) error { | |||
if _, err := w.WriteString(props.OrigName); err != nil { | |||
return err | |||
} | |||
if props.Wire != "group" { | |||
return w.WriteByte(':') | |||
} | |||
return nil | |||
} | |||
func requiresQuotes(u string) bool { | |||
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted. | |||
for _, ch := range u { | |||
switch { | |||
case ch == '.' || ch == '/' || ch == '_': | |||
continue | |||
case '0' <= ch && ch <= '9': | |||
continue | |||
case 'A' <= ch && ch <= 'Z': | |||
continue | |||
case 'a' <= ch && ch <= 'z': | |||
continue | |||
default: | |||
return true | |||
} | |||
} | |||
return false | |||
} | |||
// isAny reports whether sv is a google.protobuf.Any message | |||
func isAny(sv reflect.Value) bool { | |||
type wkt interface { | |||
XXX_WellKnownType() string | |||
} | |||
t, ok := sv.Addr().Interface().(wkt) | |||
return ok && t.XXX_WellKnownType() == "Any" | |||
} | |||
// writeProto3Any writes an expanded google.protobuf.Any message. | |||
// | |||
// It returns (false, nil) if sv value can't be unmarshaled (e.g. because | |||
// required messages are not linked in). | |||
// | |||
// It returns (true, error) when sv was written in expanded format or an error | |||
// was encountered. | |||
func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) { | |||
turl := sv.FieldByName("TypeUrl") | |||
val := sv.FieldByName("Value") | |||
if !turl.IsValid() || !val.IsValid() { | |||
return true, errors.New("proto: invalid google.protobuf.Any message") | |||
} | |||
b, ok := val.Interface().([]byte) | |||
if !ok { | |||
return true, errors.New("proto: invalid google.protobuf.Any message") | |||
} | |||
parts := strings.Split(turl.String(), "/") | |||
mt := MessageType(parts[len(parts)-1]) | |||
if mt == nil { | |||
return false, nil | |||
} | |||
m := reflect.New(mt.Elem()) | |||
if err := Unmarshal(b, m.Interface().(Message)); err != nil { | |||
return false, nil | |||
} | |||
w.Write([]byte("[")) | |||
u := turl.String() | |||
if requiresQuotes(u) { | |||
writeString(w, u) | |||
} else { | |||
w.Write([]byte(u)) | |||
} | |||
if w.compact { | |||
w.Write([]byte("]:<")) | |||
} else { | |||
w.Write([]byte("]: <\n")) | |||
w.ind++ | |||
} | |||
if err := tm.writeStruct(w, m.Elem()); err != nil { | |||
return true, err | |||
} | |||
if w.compact { | |||
w.Write([]byte("> ")) | |||
} else { | |||
w.ind-- | |||
w.Write([]byte(">\n")) | |||
} | |||
return true, nil | |||
} | |||
func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error { | |||
if tm.ExpandAny && isAny(sv) { | |||
if canExpand, err := tm.writeProto3Any(w, sv); canExpand { | |||
return err | |||
} | |||
} | |||
st := sv.Type() | |||
sprops := GetProperties(st) | |||
for i := 0; i < sv.NumField(); i++ { | |||
fv := sv.Field(i) | |||
props := sprops.Prop[i] | |||
name := st.Field(i).Name | |||
if name == "XXX_NoUnkeyedLiteral" { | |||
continue | |||
} | |||
if strings.HasPrefix(name, "XXX_") { | |||
// There are two XXX_ fields: | |||
// XXX_unrecognized []byte | |||
// XXX_extensions map[int32]proto.Extension | |||
// The first is handled here; | |||
// the second is handled at the bottom of this function. | |||
if name == "XXX_unrecognized" && !fv.IsNil() { | |||
if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil { | |||
return err | |||
} | |||
} | |||
continue | |||
} | |||
if fv.Kind() == reflect.Ptr && fv.IsNil() { | |||
// Field not filled in. This could be an optional field or | |||
// a required field that wasn't filled in. Either way, there | |||
// isn't anything we can show for it. | |||
continue | |||
} | |||
if fv.Kind() == reflect.Slice && fv.IsNil() { | |||
// Repeated field that is empty, or a bytes field that is unused. | |||
continue | |||
} | |||
if props.Repeated && fv.Kind() == reflect.Slice { | |||
// Repeated field. | |||
for j := 0; j < fv.Len(); j++ { | |||
if err := writeName(w, props); err != nil { | |||
return err | |||
} | |||
if !w.compact { | |||
if err := w.WriteByte(' '); err != nil { | |||
return err | |||
} | |||
} | |||
v := fv.Index(j) | |||
if v.Kind() == reflect.Ptr && v.IsNil() { | |||
// A nil message in a repeated field is not valid, | |||
// but we can handle that more gracefully than panicking. | |||
if _, err := w.Write([]byte("<nil>\n")); err != nil { | |||
return err | |||
} | |||
continue | |||
} | |||
if err := tm.writeAny(w, v, props); err != nil { | |||
return err | |||
} | |||
if err := w.WriteByte('\n'); err != nil { | |||
return err | |||
} | |||
} | |||
continue | |||
} | |||
if fv.Kind() == reflect.Map { | |||
// Map fields are rendered as a repeated struct with key/value fields. | |||
keys := fv.MapKeys() | |||
sort.Sort(mapKeys(keys)) | |||
for _, key := range keys { | |||
val := fv.MapIndex(key) | |||
if err := writeName(w, props); err != nil { | |||
return err | |||
} | |||
if !w.compact { | |||
if err := w.WriteByte(' '); err != nil { | |||
return err | |||
} | |||
} | |||
// open struct | |||
if err := w.WriteByte('<'); err != nil { | |||
return err | |||
} | |||
if !w.compact { | |||
if err := w.WriteByte('\n'); err != nil { | |||
return err | |||
} | |||
} | |||
w.indent() | |||
// key | |||
if _, err := w.WriteString("key:"); err != nil { | |||
return err | |||
} | |||
if !w.compact { | |||
if err := w.WriteByte(' '); err != nil { | |||
return err | |||
} | |||
} | |||
if err := tm.writeAny(w, key, props.MapKeyProp); err != nil { | |||
return err | |||
} | |||
if err := w.WriteByte('\n'); err != nil { | |||
return err | |||
} | |||
// nil values aren't legal, but we can avoid panicking because of them. | |||
if val.Kind() != reflect.Ptr || !val.IsNil() { | |||
// value | |||
if _, err := w.WriteString("value:"); err != nil { | |||
return err | |||
} | |||
if !w.compact { | |||
if err := w.WriteByte(' '); err != nil { | |||
return err | |||
} | |||
} | |||
if err := tm.writeAny(w, val, props.MapValProp); err != nil { | |||
return err | |||
} | |||
if err := w.WriteByte('\n'); err != nil { | |||
return err | |||
} | |||
} | |||
// close struct | |||
w.unindent() | |||
if err := w.WriteByte('>'); err != nil { | |||
return err | |||
} | |||
if err := w.WriteByte('\n'); err != nil { | |||
return err | |||
} | |||
} | |||
continue | |||
} | |||
if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 { | |||
// empty bytes field | |||
continue | |||
} | |||
if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice { | |||
// proto3 non-repeated scalar field; skip if zero value | |||
if isProto3Zero(fv) { | |||
continue | |||
} | |||
} | |||
if fv.Kind() == reflect.Interface { | |||
// Check if it is a oneof. | |||
if st.Field(i).Tag.Get("protobuf_oneof") != "" { | |||
// fv is nil, or holds a pointer to generated struct. | |||
// That generated struct has exactly one field, | |||
// which has a protobuf struct tag. | |||
if fv.IsNil() { | |||
continue | |||
} | |||
inner := fv.Elem().Elem() // interface -> *T -> T | |||
tag := inner.Type().Field(0).Tag.Get("protobuf") | |||
props = new(Properties) // Overwrite the outer props var, but not its pointee. | |||
props.Parse(tag) | |||
// Write the value in the oneof, not the oneof itself. | |||
fv = inner.Field(0) | |||
// Special case to cope with malformed messages gracefully: | |||
// If the value in the oneof is a nil pointer, don't panic | |||
// in writeAny. | |||
if fv.Kind() == reflect.Ptr && fv.IsNil() { | |||
// Use errors.New so writeAny won't render quotes. | |||
msg := errors.New("/* nil */") | |||
fv = reflect.ValueOf(&msg).Elem() | |||
} | |||
} | |||
} | |||
if err := writeName(w, props); err != nil { | |||
return err | |||
} | |||
if !w.compact { | |||
if err := w.WriteByte(' '); err != nil { | |||
return err | |||
} | |||
} | |||
// Enums have a String method, so writeAny will work fine. | |||
if err := tm.writeAny(w, fv, props); err != nil { | |||
return err | |||
} | |||
if err := w.WriteByte('\n'); err != nil { | |||
return err | |||
} | |||
} | |||
// Extensions (the XXX_extensions field). | |||
pv := sv.Addr() | |||
if _, err := extendable(pv.Interface()); err == nil { | |||
if err := tm.writeExtensions(w, pv); err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} | |||
var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() | |||
// writeAny writes an arbitrary field. | |||
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error { | |||
v = reflect.Indirect(v) | |||
// Floats have special cases. | |||
if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 { | |||
x := v.Float() | |||
var b []byte | |||
switch { | |||
case math.IsInf(x, 1): | |||
b = posInf | |||
case math.IsInf(x, -1): | |||
b = negInf | |||
case math.IsNaN(x): | |||
b = nan | |||
} | |||
if b != nil { | |||
_, err := w.Write(b) | |||
return err | |||
} | |||
// Other values are handled below. | |||
} | |||
// We don't attempt to serialise every possible value type; only those | |||
// that can occur in protocol buffers. | |||
switch v.Kind() { | |||
case reflect.Slice: | |||
// Should only be a []byte; repeated fields are handled in writeStruct. | |||
if err := writeString(w, string(v.Bytes())); err != nil { | |||
return err | |||
} | |||
case reflect.String: | |||
if err := writeString(w, v.String()); err != nil { | |||
return err | |||
} | |||
case reflect.Struct: | |||
// Required/optional group/message. | |||
var bra, ket byte = '<', '>' | |||
if props != nil && props.Wire == "group" { | |||
bra, ket = '{', '}' | |||
} | |||
if err := w.WriteByte(bra); err != nil { | |||
return err | |||
} | |||
if !w.compact { | |||
if err := w.WriteByte('\n'); err != nil { | |||
return err | |||
} | |||
} | |||
w.indent() | |||
if v.CanAddr() { | |||
// Calling v.Interface on a struct causes the reflect package to | |||
// copy the entire struct. This is racy with the new Marshaler | |||
// since we atomically update the XXX_sizecache. | |||
// | |||
// Thus, we retrieve a pointer to the struct if possible to avoid | |||
// a race since v.Interface on the pointer doesn't copy the struct. | |||
// | |||
// If v is not addressable, then we are not worried about a race | |||
// since it implies that the binary Marshaler cannot possibly be | |||
// mutating this value. | |||
v = v.Addr() | |||
} | |||
if v.Type().Implements(textMarshalerType) { | |||
text, err := v.Interface().(encoding.TextMarshaler).MarshalText() | |||
if err != nil { | |||
return err | |||
} | |||
if _, err = w.Write(text); err != nil { | |||
return err | |||
} | |||
} else { | |||
if v.Kind() == reflect.Ptr { | |||
v = v.Elem() | |||
} | |||
if err := tm.writeStruct(w, v); err != nil { | |||
return err | |||
} | |||
} | |||
w.unindent() | |||
if err := w.WriteByte(ket); err != nil { | |||
return err | |||
} | |||
default: | |||
_, err := fmt.Fprint(w, v.Interface()) | |||
return err | |||
} | |||
return nil | |||
} | |||
// equivalent to C's isprint. | |||
func isprint(c byte) bool { | |||
return c >= 0x20 && c < 0x7f | |||
} | |||
// writeString writes a string in the protocol buffer text format. | |||
// It is similar to strconv.Quote except we don't use Go escape sequences, | |||
// we treat the string as a byte sequence, and we use octal escapes. | |||
// These differences are to maintain interoperability with the other | |||
// languages' implementations of the text format. | |||
func writeString(w *textWriter, s string) error { | |||
// use WriteByte here to get any needed indent | |||
if err := w.WriteByte('"'); err != nil { | |||
return err | |||
} | |||
// Loop over the bytes, not the runes. | |||
for i := 0; i < len(s); i++ { | |||
var err error | |||
// Divergence from C++: we don't escape apostrophes. | |||
// There's no need to escape them, and the C++ parser | |||
// copes with a naked apostrophe. | |||
switch c := s[i]; c { | |||
case '\n': | |||
_, err = w.w.Write(backslashN) | |||
case '\r': | |||
_, err = w.w.Write(backslashR) | |||
case '\t': | |||
_, err = w.w.Write(backslashT) | |||
case '"': | |||
_, err = w.w.Write(backslashDQ) | |||
case '\\': | |||
_, err = w.w.Write(backslashBS) | |||
default: | |||
if isprint(c) { | |||
err = w.w.WriteByte(c) | |||
} else { | |||
_, err = fmt.Fprintf(w.w, "\\%03o", c) | |||
} | |||
} | |||
if err != nil { | |||
return err | |||
} | |||
} | |||
return w.WriteByte('"') | |||
} | |||
func writeUnknownStruct(w *textWriter, data []byte) (err error) { | |||
if !w.compact { | |||
if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil { | |||
return err | |||
} | |||
} | |||
b := NewBuffer(data) | |||
for b.index < len(b.buf) { | |||
x, err := b.DecodeVarint() | |||
if err != nil { | |||
_, err := fmt.Fprintf(w, "/* %v */\n", err) | |||
return err | |||
} | |||
wire, tag := x&7, x>>3 | |||
if wire == WireEndGroup { | |||
w.unindent() | |||
if _, err := w.Write(endBraceNewline); err != nil { | |||
return err | |||
} | |||
continue | |||
} | |||
if _, err := fmt.Fprint(w, tag); err != nil { | |||
return err | |||
} | |||
if wire != WireStartGroup { | |||
if err := w.WriteByte(':'); err != nil { | |||
return err | |||
} | |||
} | |||
if !w.compact || wire == WireStartGroup { | |||
if err := w.WriteByte(' '); err != nil { | |||
return err | |||
} | |||
} | |||
switch wire { | |||
case WireBytes: | |||
buf, e := b.DecodeRawBytes(false) | |||
if e == nil { | |||
_, err = fmt.Fprintf(w, "%q", buf) | |||
} else { | |||
_, err = fmt.Fprintf(w, "/* %v */", e) | |||
} | |||
case WireFixed32: | |||
x, err = b.DecodeFixed32() | |||
err = writeUnknownInt(w, x, err) | |||
case WireFixed64: | |||
x, err = b.DecodeFixed64() | |||
err = writeUnknownInt(w, x, err) | |||
case WireStartGroup: | |||
err = w.WriteByte('{') | |||
w.indent() | |||
case WireVarint: | |||
x, err = b.DecodeVarint() | |||
err = writeUnknownInt(w, x, err) | |||
default: | |||
_, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire) | |||
} | |||
if err != nil { | |||
return err | |||
} | |||
if err = w.WriteByte('\n'); err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} | |||
func writeUnknownInt(w *textWriter, x uint64, err error) error { | |||
if err == nil { | |||
_, err = fmt.Fprint(w, x) | |||
} else { | |||
_, err = fmt.Fprintf(w, "/* %v */", err) | |||
} | |||
return err | |||
} | |||
type int32Slice []int32 | |||
func (s int32Slice) Len() int { return len(s) } | |||
func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] } | |||
func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | |||
// writeExtensions writes all the extensions in pv. | |||
// pv is assumed to be a pointer to a protocol message struct that is extendable. | |||
func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error { | |||
emap := extensionMaps[pv.Type().Elem()] | |||
ep, _ := extendable(pv.Interface()) | |||
// Order the extensions by ID. | |||
// This isn't strictly necessary, but it will give us | |||
// canonical output, which will also make testing easier. | |||
m, mu := ep.extensionsRead() | |||
if m == nil { | |||
return nil | |||
} | |||
mu.Lock() | |||
ids := make([]int32, 0, len(m)) | |||
for id := range m { | |||
ids = append(ids, id) | |||
} | |||
sort.Sort(int32Slice(ids)) | |||
mu.Unlock() | |||
for _, extNum := range ids { | |||
ext := m[extNum] | |||
var desc *ExtensionDesc | |||
if emap != nil { | |||
desc = emap[extNum] | |||
} | |||
if desc == nil { | |||
// Unknown extension. | |||
if err := writeUnknownStruct(w, ext.enc); err != nil { | |||
return err | |||
} | |||
continue | |||
} | |||
pb, err := GetExtension(ep, desc) | |||
if err != nil { | |||
return fmt.Errorf("failed getting extension: %v", err) | |||
} | |||
// Repeated extensions will appear as a slice. | |||
if !desc.repeated() { | |||
if err := tm.writeExtension(w, desc.Name, pb); err != nil { | |||
return err | |||
} | |||
} else { | |||
v := reflect.ValueOf(pb) | |||
for i := 0; i < v.Len(); i++ { | |||
if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil { | |||
return err | |||
} | |||
} | |||
} | |||
} | |||
return nil | |||
} | |||
func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error { | |||
if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil { | |||
return err | |||
} | |||
if !w.compact { | |||
if err := w.WriteByte(' '); err != nil { | |||
return err | |||
} | |||
} | |||
if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil { | |||
return err | |||
} | |||
if err := w.WriteByte('\n'); err != nil { | |||
return err | |||
} | |||
return nil | |||
} | |||
func (w *textWriter) writeIndent() { | |||
if !w.complete { | |||
return | |||
} | |||
remain := w.ind * 2 | |||
for remain > 0 { | |||
n := remain | |||
if n > len(spaces) { | |||
n = len(spaces) | |||
} | |||
w.w.Write(spaces[:n]) | |||
remain -= n | |||
} | |||
w.complete = false | |||
} | |||
// TextMarshaler is a configurable text format marshaler. | |||
type TextMarshaler struct { | |||
Compact bool // use compact text format (one line). | |||
ExpandAny bool // expand google.protobuf.Any messages of known types | |||
} | |||
// Marshal writes a given protocol buffer in text format. | |||
// The only errors returned are from w. | |||
func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error { | |||
val := reflect.ValueOf(pb) | |||
if pb == nil || val.IsNil() { | |||
w.Write([]byte("<nil>")) | |||
return nil | |||
} | |||
var bw *bufio.Writer | |||
ww, ok := w.(writer) | |||
if !ok { | |||
bw = bufio.NewWriter(w) | |||
ww = bw | |||
} | |||
aw := &textWriter{ | |||
w: ww, | |||
complete: true, | |||
compact: tm.Compact, | |||
} | |||
if etm, ok := pb.(encoding.TextMarshaler); ok { | |||
text, err := etm.MarshalText() | |||
if err != nil { | |||
return err | |||
} | |||
if _, err = aw.Write(text); err != nil { | |||
return err | |||
} | |||
if bw != nil { | |||
return bw.Flush() | |||
} | |||
return nil | |||
} | |||
// Dereference the received pointer so we don't have outer < and >. | |||
v := reflect.Indirect(val) | |||
if err := tm.writeStruct(aw, v); err != nil { | |||
return err | |||
} | |||
if bw != nil { | |||
return bw.Flush() | |||
} | |||
return nil | |||
} | |||
// Text is the same as Marshal, but returns the string directly. | |||
func (tm *TextMarshaler) Text(pb Message) string { | |||
var buf bytes.Buffer | |||
tm.Marshal(&buf, pb) | |||
return buf.String() | |||
} | |||
var ( | |||
defaultTextMarshaler = TextMarshaler{} | |||
compactTextMarshaler = TextMarshaler{Compact: true} | |||
) | |||
// TODO: consider removing some of the Marshal functions below. | |||
// MarshalText writes a given protocol buffer in text format. | |||
// The only errors returned are from w. | |||
func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) } | |||
// MarshalTextString is the same as MarshalText, but returns the string directly. | |||
func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) } | |||
// CompactText writes a given protocol buffer in compact text format (one line). | |||
func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) } | |||
// CompactTextString is the same as CompactText, but returns the string directly. | |||
func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) } |
@ -0,0 +1,801 @@ | |||
// Copyright 2010 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package proto | |||
import ( | |||
"encoding" | |||
"errors" | |||
"fmt" | |||
"reflect" | |||
"strconv" | |||
"strings" | |||
"unicode/utf8" | |||
"google.golang.org/protobuf/encoding/prototext" | |||
protoV2 "google.golang.org/protobuf/proto" | |||
"google.golang.org/protobuf/reflect/protoreflect" | |||
"google.golang.org/protobuf/reflect/protoregistry" | |||
) | |||
const wrapTextUnmarshalV2 = false | |||
// ParseError is returned by UnmarshalText. | |||
type ParseError struct { | |||
Message string | |||
// Deprecated: Do not use. | |||
Line, Offset int | |||
} | |||
func (e *ParseError) Error() string { | |||
if wrapTextUnmarshalV2 { | |||
return e.Message | |||
} | |||
if e.Line == 1 { | |||
return fmt.Sprintf("line 1.%d: %v", e.Offset, e.Message) | |||
} | |||
return fmt.Sprintf("line %d: %v", e.Line, e.Message) | |||
} | |||
// UnmarshalText parses a proto text formatted string into m. | |||
func UnmarshalText(s string, m Message) error { | |||
if u, ok := m.(encoding.TextUnmarshaler); ok { | |||
return u.UnmarshalText([]byte(s)) | |||
} | |||
m.Reset() | |||
mi := MessageV2(m) | |||
if wrapTextUnmarshalV2 { | |||
err := prototext.UnmarshalOptions{ | |||
AllowPartial: true, | |||
}.Unmarshal([]byte(s), mi) | |||
if err != nil { | |||
return &ParseError{Message: err.Error()} | |||
} | |||
return checkRequiredNotSet(mi) | |||
} else { | |||
if err := newTextParser(s).unmarshalMessage(mi.ProtoReflect(), ""); err != nil { | |||
return err | |||
} | |||
return checkRequiredNotSet(mi) | |||
} | |||
} | |||
type textParser struct { | |||
s string // remaining input | |||
done bool // whether the parsing is finished (success or error) | |||
backed bool // whether back() was called | |||
offset, line int | |||
cur token | |||
} | |||
type token struct { | |||
value string | |||
err *ParseError | |||
line int // line number | |||
offset int // byte number from start of input, not start of line | |||
unquoted string // the unquoted version of value, if it was a quoted string | |||
} | |||
func newTextParser(s string) *textParser { | |||
p := new(textParser) | |||
p.s = s | |||
p.line = 1 | |||
p.cur.line = 1 | |||
return p | |||
} | |||
func (p *textParser) unmarshalMessage(m protoreflect.Message, terminator string) (err error) { | |||
md := m.Descriptor() | |||
fds := md.Fields() | |||
// A struct is a sequence of "name: value", terminated by one of | |||
// '>' or '}', or the end of the input. A name may also be | |||
// "[extension]" or "[type/url]". | |||
// | |||
// The whole struct can also be an expanded Any message, like: | |||
// [type/url] < ... struct contents ... > | |||
seen := make(map[protoreflect.FieldNumber]bool) | |||
for { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
if tok.value == terminator { | |||
break | |||
} | |||
if tok.value == "[" { | |||
if err := p.unmarshalExtensionOrAny(m, seen); err != nil { | |||
return err | |||
} | |||
continue | |||
} | |||
// This is a normal, non-extension field. | |||
name := protoreflect.Name(tok.value) | |||
fd := fds.ByName(name) | |||
switch { | |||
case fd == nil: | |||
gd := fds.ByName(protoreflect.Name(strings.ToLower(string(name)))) | |||
if gd != nil && gd.Kind() == protoreflect.GroupKind && gd.Message().Name() == name { | |||
fd = gd | |||
} | |||
case fd.Kind() == protoreflect.GroupKind && fd.Message().Name() != name: | |||
fd = nil | |||
case fd.IsWeak() && fd.Message().IsPlaceholder(): | |||
fd = nil | |||
} | |||
if fd == nil { | |||
typeName := string(md.FullName()) | |||
if m, ok := m.Interface().(Message); ok { | |||
t := reflect.TypeOf(m) | |||
if t.Kind() == reflect.Ptr { | |||
typeName = t.Elem().String() | |||
} | |||
} | |||
return p.errorf("unknown field name %q in %v", name, typeName) | |||
} | |||
if od := fd.ContainingOneof(); od != nil && m.WhichOneof(od) != nil { | |||
return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, od.Name()) | |||
} | |||
if fd.Cardinality() != protoreflect.Repeated && seen[fd.Number()] { | |||
return p.errorf("non-repeated field %q was repeated", fd.Name()) | |||
} | |||
seen[fd.Number()] = true | |||
// Consume any colon. | |||
if err := p.checkForColon(fd); err != nil { | |||
return err | |||
} | |||
// Parse into the field. | |||
v := m.Get(fd) | |||
if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) { | |||
v = m.Mutable(fd) | |||
} | |||
if v, err = p.unmarshalValue(v, fd); err != nil { | |||
return err | |||
} | |||
m.Set(fd, v) | |||
if err := p.consumeOptionalSeparator(); err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} | |||
func (p *textParser) unmarshalExtensionOrAny(m protoreflect.Message, seen map[protoreflect.FieldNumber]bool) error { | |||
name, err := p.consumeExtensionOrAnyName() | |||
if err != nil { | |||
return err | |||
} | |||
// If it contains a slash, it's an Any type URL. | |||
if slashIdx := strings.LastIndex(name, "/"); slashIdx >= 0 { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
// consume an optional colon | |||
if tok.value == ":" { | |||
tok = p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
} | |||
var terminator string | |||
switch tok.value { | |||
case "<": | |||
terminator = ">" | |||
case "{": | |||
terminator = "}" | |||
default: | |||
return p.errorf("expected '{' or '<', found %q", tok.value) | |||
} | |||
mt, err := protoregistry.GlobalTypes.FindMessageByURL(name) | |||
if err != nil { | |||
return p.errorf("unrecognized message %q in google.protobuf.Any", name[slashIdx+len("/"):]) | |||
} | |||
m2 := mt.New() | |||
if err := p.unmarshalMessage(m2, terminator); err != nil { | |||
return err | |||
} | |||
b, err := protoV2.Marshal(m2.Interface()) | |||
if err != nil { | |||
return p.errorf("failed to marshal message of type %q: %v", name[slashIdx+len("/"):], err) | |||
} | |||
urlFD := m.Descriptor().Fields().ByName("type_url") | |||
valFD := m.Descriptor().Fields().ByName("value") | |||
if seen[urlFD.Number()] { | |||
return p.errorf("Any message unpacked multiple times, or %q already set", urlFD.Name()) | |||
} | |||
if seen[valFD.Number()] { | |||
return p.errorf("Any message unpacked multiple times, or %q already set", valFD.Name()) | |||
} | |||
m.Set(urlFD, protoreflect.ValueOfString(name)) | |||
m.Set(valFD, protoreflect.ValueOfBytes(b)) | |||
seen[urlFD.Number()] = true | |||
seen[valFD.Number()] = true | |||
return nil | |||
} | |||
xname := protoreflect.FullName(name) | |||
xt, _ := protoregistry.GlobalTypes.FindExtensionByName(xname) | |||
if xt == nil && isMessageSet(m.Descriptor()) { | |||
xt, _ = protoregistry.GlobalTypes.FindExtensionByName(xname.Append("message_set_extension")) | |||
} | |||
if xt == nil { | |||
return p.errorf("unrecognized extension %q", name) | |||
} | |||
fd := xt.TypeDescriptor() | |||
if fd.ContainingMessage().FullName() != m.Descriptor().FullName() { | |||
return p.errorf("extension field %q does not extend message %q", name, m.Descriptor().FullName()) | |||
} | |||
if err := p.checkForColon(fd); err != nil { | |||
return err | |||
} | |||
v := m.Get(fd) | |||
if !m.Has(fd) && (fd.IsList() || fd.IsMap() || fd.Message() != nil) { | |||
v = m.Mutable(fd) | |||
} | |||
v, err = p.unmarshalValue(v, fd) | |||
if err != nil { | |||
return err | |||
} | |||
m.Set(fd, v) | |||
return p.consumeOptionalSeparator() | |||
} | |||
func (p *textParser) unmarshalValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return v, tok.err | |||
} | |||
if tok.value == "" { | |||
return v, p.errorf("unexpected EOF") | |||
} | |||
switch { | |||
case fd.IsList(): | |||
lv := v.List() | |||
var err error | |||
if tok.value == "[" { | |||
// Repeated field with list notation, like [1,2,3]. | |||
for { | |||
vv := lv.NewElement() | |||
vv, err = p.unmarshalSingularValue(vv, fd) | |||
if err != nil { | |||
return v, err | |||
} | |||
lv.Append(vv) | |||
tok := p.next() | |||
if tok.err != nil { | |||
return v, tok.err | |||
} | |||
if tok.value == "]" { | |||
break | |||
} | |||
if tok.value != "," { | |||
return v, p.errorf("Expected ']' or ',' found %q", tok.value) | |||
} | |||
} | |||
return v, nil | |||
} | |||
// One value of the repeated field. | |||
p.back() | |||
vv := lv.NewElement() | |||
vv, err = p.unmarshalSingularValue(vv, fd) | |||
if err != nil { | |||
return v, err | |||
} | |||
lv.Append(vv) | |||
return v, nil | |||
case fd.IsMap(): | |||
// The map entry should be this sequence of tokens: | |||
// < key : KEY value : VALUE > | |||
// However, implementations may omit key or value, and technically | |||
// we should support them in any order. | |||
var terminator string | |||
switch tok.value { | |||
case "<": | |||
terminator = ">" | |||
case "{": | |||
terminator = "}" | |||
default: | |||
return v, p.errorf("expected '{' or '<', found %q", tok.value) | |||
} | |||
keyFD := fd.MapKey() | |||
valFD := fd.MapValue() | |||
mv := v.Map() | |||
kv := keyFD.Default() | |||
vv := mv.NewValue() | |||
for { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return v, tok.err | |||
} | |||
if tok.value == terminator { | |||
break | |||
} | |||
var err error | |||
switch tok.value { | |||
case "key": | |||
if err := p.consumeToken(":"); err != nil { | |||
return v, err | |||
} | |||
if kv, err = p.unmarshalSingularValue(kv, keyFD); err != nil { | |||
return v, err | |||
} | |||
if err := p.consumeOptionalSeparator(); err != nil { | |||
return v, err | |||
} | |||
case "value": | |||
if err := p.checkForColon(valFD); err != nil { | |||
return v, err | |||
} | |||
if vv, err = p.unmarshalSingularValue(vv, valFD); err != nil { | |||
return v, err | |||
} | |||
if err := p.consumeOptionalSeparator(); err != nil { | |||
return v, err | |||
} | |||
default: | |||
p.back() | |||
return v, p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value) | |||
} | |||
} | |||
mv.Set(kv.MapKey(), vv) | |||
return v, nil | |||
default: | |||
p.back() | |||
return p.unmarshalSingularValue(v, fd) | |||
} | |||
} | |||
func (p *textParser) unmarshalSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) (protoreflect.Value, error) { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return v, tok.err | |||
} | |||
if tok.value == "" { | |||
return v, p.errorf("unexpected EOF") | |||
} | |||
switch fd.Kind() { | |||
case protoreflect.BoolKind: | |||
switch tok.value { | |||
case "true", "1", "t", "True": | |||
return protoreflect.ValueOfBool(true), nil | |||
case "false", "0", "f", "False": | |||
return protoreflect.ValueOfBool(false), nil | |||
} | |||
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: | |||
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil { | |||
return protoreflect.ValueOfInt32(int32(x)), nil | |||
} | |||
// The C++ parser accepts large positive hex numbers that uses | |||
// two's complement arithmetic to represent negative numbers. | |||
// This feature is here for backwards compatibility with C++. | |||
if strings.HasPrefix(tok.value, "0x") { | |||
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { | |||
return protoreflect.ValueOfInt32(int32(-(int64(^x) + 1))), nil | |||
} | |||
} | |||
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: | |||
if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil { | |||
return protoreflect.ValueOfInt64(int64(x)), nil | |||
} | |||
// The C++ parser accepts large positive hex numbers that uses | |||
// two's complement arithmetic to represent negative numbers. | |||
// This feature is here for backwards compatibility with C++. | |||
if strings.HasPrefix(tok.value, "0x") { | |||
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil { | |||
return protoreflect.ValueOfInt64(int64(-(int64(^x) + 1))), nil | |||
} | |||
} | |||
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: | |||
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { | |||
return protoreflect.ValueOfUint32(uint32(x)), nil | |||
} | |||
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: | |||
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil { | |||
return protoreflect.ValueOfUint64(uint64(x)), nil | |||
} | |||
case protoreflect.FloatKind: | |||
// Ignore 'f' for compatibility with output generated by C++, | |||
// but don't remove 'f' when the value is "-inf" or "inf". | |||
v := tok.value | |||
if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" { | |||
v = v[:len(v)-len("f")] | |||
} | |||
if x, err := strconv.ParseFloat(v, 32); err == nil { | |||
return protoreflect.ValueOfFloat32(float32(x)), nil | |||
} | |||
case protoreflect.DoubleKind: | |||
// Ignore 'f' for compatibility with output generated by C++, | |||
// but don't remove 'f' when the value is "-inf" or "inf". | |||
v := tok.value | |||
if strings.HasSuffix(v, "f") && v != "-inf" && v != "inf" { | |||
v = v[:len(v)-len("f")] | |||
} | |||
if x, err := strconv.ParseFloat(v, 64); err == nil { | |||
return protoreflect.ValueOfFloat64(float64(x)), nil | |||
} | |||
case protoreflect.StringKind: | |||
if isQuote(tok.value[0]) { | |||
return protoreflect.ValueOfString(tok.unquoted), nil | |||
} | |||
case protoreflect.BytesKind: | |||
if isQuote(tok.value[0]) { | |||
return protoreflect.ValueOfBytes([]byte(tok.unquoted)), nil | |||
} | |||
case protoreflect.EnumKind: | |||
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil { | |||
return protoreflect.ValueOfEnum(protoreflect.EnumNumber(x)), nil | |||
} | |||
vd := fd.Enum().Values().ByName(protoreflect.Name(tok.value)) | |||
if vd != nil { | |||
return protoreflect.ValueOfEnum(vd.Number()), nil | |||
} | |||
case protoreflect.MessageKind, protoreflect.GroupKind: | |||
var terminator string | |||
switch tok.value { | |||
case "{": | |||
terminator = "}" | |||
case "<": | |||
terminator = ">" | |||
default: | |||
return v, p.errorf("expected '{' or '<', found %q", tok.value) | |||
} | |||
err := p.unmarshalMessage(v.Message(), terminator) | |||
return v, err | |||
default: | |||
panic(fmt.Sprintf("invalid kind %v", fd.Kind())) | |||
} | |||
return v, p.errorf("invalid %v: %v", fd.Kind(), tok.value) | |||
} | |||
// Consume a ':' from the input stream (if the next token is a colon), | |||
// returning an error if a colon is needed but not present. | |||
func (p *textParser) checkForColon(fd protoreflect.FieldDescriptor) *ParseError { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
if tok.value != ":" { | |||
if fd.Message() == nil { | |||
return p.errorf("expected ':', found %q", tok.value) | |||
} | |||
p.back() | |||
} | |||
return nil | |||
} | |||
// consumeExtensionOrAnyName consumes an extension name or an Any type URL and | |||
// the following ']'. It returns the name or URL consumed. | |||
func (p *textParser) consumeExtensionOrAnyName() (string, error) { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return "", tok.err | |||
} | |||
// If extension name or type url is quoted, it's a single token. | |||
if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] { | |||
name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0])) | |||
if err != nil { | |||
return "", err | |||
} | |||
return name, p.consumeToken("]") | |||
} | |||
// Consume everything up to "]" | |||
var parts []string | |||
for tok.value != "]" { | |||
parts = append(parts, tok.value) | |||
tok = p.next() | |||
if tok.err != nil { | |||
return "", p.errorf("unrecognized type_url or extension name: %s", tok.err) | |||
} | |||
if p.done && tok.value != "]" { | |||
return "", p.errorf("unclosed type_url or extension name") | |||
} | |||
} | |||
return strings.Join(parts, ""), nil | |||
} | |||
// consumeOptionalSeparator consumes an optional semicolon or comma. | |||
// It is used in unmarshalMessage to provide backward compatibility. | |||
func (p *textParser) consumeOptionalSeparator() error { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
if tok.value != ";" && tok.value != "," { | |||
p.back() | |||
} | |||
return nil | |||
} | |||
func (p *textParser) errorf(format string, a ...interface{}) *ParseError { | |||
pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset} | |||
p.cur.err = pe | |||
p.done = true | |||
return pe | |||
} | |||
func (p *textParser) skipWhitespace() { | |||
i := 0 | |||
for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') { | |||
if p.s[i] == '#' { | |||
// comment; skip to end of line or input | |||
for i < len(p.s) && p.s[i] != '\n' { | |||
i++ | |||
} | |||
if i == len(p.s) { | |||
break | |||
} | |||
} | |||
if p.s[i] == '\n' { | |||
p.line++ | |||
} | |||
i++ | |||
} | |||
p.offset += i | |||
p.s = p.s[i:len(p.s)] | |||
if len(p.s) == 0 { | |||
p.done = true | |||
} | |||
} | |||
func (p *textParser) advance() { | |||
// Skip whitespace | |||
p.skipWhitespace() | |||
if p.done { | |||
return | |||
} | |||
// Start of non-whitespace | |||
p.cur.err = nil | |||
p.cur.offset, p.cur.line = p.offset, p.line | |||
p.cur.unquoted = "" | |||
switch p.s[0] { | |||
case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/': | |||
// Single symbol | |||
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)] | |||
case '"', '\'': | |||
// Quoted string | |||
i := 1 | |||
for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' { | |||
if p.s[i] == '\\' && i+1 < len(p.s) { | |||
// skip escaped char | |||
i++ | |||
} | |||
i++ | |||
} | |||
if i >= len(p.s) || p.s[i] != p.s[0] { | |||
p.errorf("unmatched quote") | |||
return | |||
} | |||
unq, err := unquoteC(p.s[1:i], rune(p.s[0])) | |||
if err != nil { | |||
p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err) | |||
return | |||
} | |||
p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)] | |||
p.cur.unquoted = unq | |||
default: | |||
i := 0 | |||
for i < len(p.s) && isIdentOrNumberChar(p.s[i]) { | |||
i++ | |||
} | |||
if i == 0 { | |||
p.errorf("unexpected byte %#x", p.s[0]) | |||
return | |||
} | |||
p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)] | |||
} | |||
p.offset += len(p.cur.value) | |||
} | |||
// Back off the parser by one token. Can only be done between calls to next(). | |||
// It makes the next advance() a no-op. | |||
func (p *textParser) back() { p.backed = true } | |||
// Advances the parser and returns the new current token. | |||
func (p *textParser) next() *token { | |||
if p.backed || p.done { | |||
p.backed = false | |||
return &p.cur | |||
} | |||
p.advance() | |||
if p.done { | |||
p.cur.value = "" | |||
} else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) { | |||
// Look for multiple quoted strings separated by whitespace, | |||
// and concatenate them. | |||
cat := p.cur | |||
for { | |||
p.skipWhitespace() | |||
if p.done || !isQuote(p.s[0]) { | |||
break | |||
} | |||
p.advance() | |||
if p.cur.err != nil { | |||
return &p.cur | |||
} | |||
cat.value += " " + p.cur.value | |||
cat.unquoted += p.cur.unquoted | |||
} | |||
p.done = false // parser may have seen EOF, but we want to return cat | |||
p.cur = cat | |||
} | |||
return &p.cur | |||
} | |||
func (p *textParser) consumeToken(s string) error { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
if tok.value != s { | |||
p.back() | |||
return p.errorf("expected %q, found %q", s, tok.value) | |||
} | |||
return nil | |||
} | |||
var errBadUTF8 = errors.New("proto: bad UTF-8") | |||
func unquoteC(s string, quote rune) (string, error) { | |||
// This is based on C++'s tokenizer.cc. | |||
// Despite its name, this is *not* parsing C syntax. | |||
// For instance, "\0" is an invalid quoted string. | |||
// Avoid allocation in trivial cases. | |||
simple := true | |||
for _, r := range s { | |||
if r == '\\' || r == quote { | |||
simple = false | |||
break | |||
} | |||
} | |||
if simple { | |||
return s, nil | |||
} | |||
buf := make([]byte, 0, 3*len(s)/2) | |||
for len(s) > 0 { | |||
r, n := utf8.DecodeRuneInString(s) | |||
if r == utf8.RuneError && n == 1 { | |||
return "", errBadUTF8 | |||
} | |||
s = s[n:] | |||
if r != '\\' { | |||
if r < utf8.RuneSelf { | |||
buf = append(buf, byte(r)) | |||
} else { | |||
buf = append(buf, string(r)...) | |||
} | |||
continue | |||
} | |||
ch, tail, err := unescape(s) | |||
if err != nil { | |||
return "", err | |||
} | |||
buf = append(buf, ch...) | |||
s = tail | |||
} | |||
return string(buf), nil | |||
} | |||
func unescape(s string) (ch string, tail string, err error) { | |||
r, n := utf8.DecodeRuneInString(s) | |||
if r == utf8.RuneError && n == 1 { | |||
return "", "", errBadUTF8 | |||
} | |||
s = s[n:] | |||
switch r { | |||
case 'a': | |||
return "\a", s, nil | |||
case 'b': | |||
return "\b", s, nil | |||
case 'f': | |||
return "\f", s, nil | |||
case 'n': | |||
return "\n", s, nil | |||
case 'r': | |||
return "\r", s, nil | |||
case 't': | |||
return "\t", s, nil | |||
case 'v': | |||
return "\v", s, nil | |||
case '?': | |||
return "?", s, nil // trigraph workaround | |||
case '\'', '"', '\\': | |||
return string(r), s, nil | |||
case '0', '1', '2', '3', '4', '5', '6', '7': | |||
if len(s) < 2 { | |||
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r) | |||
} | |||
ss := string(r) + s[:2] | |||
s = s[2:] | |||
i, err := strconv.ParseUint(ss, 8, 8) | |||
if err != nil { | |||
return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss) | |||
} | |||
return string([]byte{byte(i)}), s, nil | |||
case 'x', 'X', 'u', 'U': | |||
var n int | |||
switch r { | |||
case 'x', 'X': | |||
n = 2 | |||
case 'u': | |||
n = 4 | |||
case 'U': | |||
n = 8 | |||
} | |||
if len(s) < n { | |||
return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n) | |||
} | |||
ss := s[:n] | |||
s = s[n:] | |||
i, err := strconv.ParseUint(ss, 16, 64) | |||
if err != nil { | |||
return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss) | |||
} | |||
if r == 'x' || r == 'X' { | |||
return string([]byte{byte(i)}), s, nil | |||
} | |||
if i > utf8.MaxRune { | |||
return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss) | |||
} | |||
return string(i), s, nil | |||
} | |||
return "", "", fmt.Errorf(`unknown escape \%c`, r) | |||
} | |||
func isIdentOrNumberChar(c byte) bool { | |||
switch { | |||
case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z': | |||
return true | |||
case '0' <= c && c <= '9': | |||
return true | |||
} | |||
switch c { | |||
case '-', '+', '.', '_': | |||
return true | |||
} | |||
return false | |||
} | |||
func isWhitespace(c byte) bool { | |||
switch c { | |||
case ' ', '\t', '\n', '\r': | |||
return true | |||
} | |||
return false | |||
} | |||
func isQuote(c byte) bool { | |||
switch c { | |||
case '"', '\'': | |||
return true | |||
} | |||
return false | |||
} |
@ -0,0 +1,560 @@ | |||
// Copyright 2010 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package proto | |||
import ( | |||
"bytes" | |||
"encoding" | |||
"fmt" | |||
"io" | |||
"math" | |||
"sort" | |||
"strings" | |||
"google.golang.org/protobuf/encoding/prototext" | |||
"google.golang.org/protobuf/encoding/protowire" | |||
"google.golang.org/protobuf/proto" | |||
"google.golang.org/protobuf/reflect/protoreflect" | |||
"google.golang.org/protobuf/reflect/protoregistry" | |||
) | |||
const wrapTextMarshalV2 = false | |||
// TextMarshaler is a configurable text format marshaler. | |||
type TextMarshaler struct { | |||
Compact bool // use compact text format (one line) | |||
ExpandAny bool // expand google.protobuf.Any messages of known types | |||
} | |||
// Marshal writes the proto text format of m to w. | |||
func (tm *TextMarshaler) Marshal(w io.Writer, m Message) error { | |||
b, err := tm.marshal(m) | |||
if len(b) > 0 { | |||
if _, err := w.Write(b); err != nil { | |||
return err | |||
} | |||
} | |||
return err | |||
} | |||
// Text returns a proto text formatted string of m. | |||
func (tm *TextMarshaler) Text(m Message) string { | |||
b, _ := tm.marshal(m) | |||
return string(b) | |||
} | |||
func (tm *TextMarshaler) marshal(m Message) ([]byte, error) { | |||
mr := MessageReflect(m) | |||
if mr == nil || !mr.IsValid() { | |||
return []byte("<nil>"), nil | |||
} | |||
if wrapTextMarshalV2 { | |||
if m, ok := m.(encoding.TextMarshaler); ok { | |||
return m.MarshalText() | |||
} | |||
opts := prototext.MarshalOptions{ | |||
AllowPartial: true, | |||
EmitUnknown: true, | |||
} | |||
if !tm.Compact { | |||
opts.Indent = " " | |||
} | |||
if !tm.ExpandAny { | |||
opts.Resolver = (*protoregistry.Types)(nil) | |||
} | |||
return opts.Marshal(mr.Interface()) | |||
} else { | |||
w := &textWriter{ | |||
compact: tm.Compact, | |||
expandAny: tm.ExpandAny, | |||
complete: true, | |||
} | |||
if m, ok := m.(encoding.TextMarshaler); ok { | |||
b, err := m.MarshalText() | |||
if err != nil { | |||
return nil, err | |||
} | |||
w.Write(b) | |||
return w.buf, nil | |||
} | |||
err := w.writeMessage(mr) | |||
return w.buf, err | |||
} | |||
} | |||
var ( | |||
defaultTextMarshaler = TextMarshaler{} | |||
compactTextMarshaler = TextMarshaler{Compact: true} | |||
) | |||
// MarshalText writes the proto text format of m to w. | |||
func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) } | |||
// MarshalTextString returns a proto text formatted string of m. | |||
func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) } | |||
// CompactText writes the compact proto text format of m to w. | |||
func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) } | |||
// CompactTextString returns a compact proto text formatted string of m. | |||
func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) } | |||
var ( | |||
newline = []byte("\n") | |||
endBraceNewline = []byte("}\n") | |||
posInf = []byte("inf") | |||
negInf = []byte("-inf") | |||
nan = []byte("nan") | |||
) | |||
// textWriter is an io.Writer that tracks its indentation level. | |||
type textWriter struct { | |||
compact bool // same as TextMarshaler.Compact | |||
expandAny bool // same as TextMarshaler.ExpandAny | |||
complete bool // whether the current position is a complete line | |||
indent int // indentation level; never negative | |||
buf []byte | |||
} | |||
func (w *textWriter) Write(p []byte) (n int, _ error) { | |||
newlines := bytes.Count(p, newline) | |||
if newlines == 0 { | |||
if !w.compact && w.complete { | |||
w.writeIndent() | |||
} | |||
w.buf = append(w.buf, p...) | |||
w.complete = false | |||
return len(p), nil | |||
} | |||
frags := bytes.SplitN(p, newline, newlines+1) | |||
if w.compact { | |||
for i, frag := range frags { | |||
if i > 0 { | |||
w.buf = append(w.buf, ' ') | |||
n++ | |||
} | |||
w.buf = append(w.buf, frag...) | |||
n += len(frag) | |||
} | |||
return n, nil | |||
} | |||
for i, frag := range frags { | |||
if w.complete { | |||
w.writeIndent() | |||
} | |||
w.buf = append(w.buf, frag...) | |||
n += len(frag) | |||
if i+1 < len(frags) { | |||
w.buf = append(w.buf, '\n') | |||
n++ | |||
} | |||
} | |||
w.complete = len(frags[len(frags)-1]) == 0 | |||
return n, nil | |||
} | |||
func (w *textWriter) WriteByte(c byte) error { | |||
if w.compact && c == '\n' { | |||
c = ' ' | |||
} | |||
if !w.compact && w.complete { | |||
w.writeIndent() | |||
} | |||
w.buf = append(w.buf, c) | |||
w.complete = c == '\n' | |||
return nil | |||
} | |||
func (w *textWriter) writeName(fd protoreflect.FieldDescriptor) { | |||
if !w.compact && w.complete { | |||
w.writeIndent() | |||
} | |||
w.complete = false | |||
if fd.Kind() != protoreflect.GroupKind { | |||
w.buf = append(w.buf, fd.Name()...) | |||
w.WriteByte(':') | |||
} else { | |||
// Use message type name for group field name. | |||
w.buf = append(w.buf, fd.Message().Name()...) | |||
} | |||
if !w.compact { | |||
w.WriteByte(' ') | |||
} | |||
} | |||
func requiresQuotes(u string) bool { | |||
// When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted. | |||
for _, ch := range u { | |||
switch { | |||
case ch == '.' || ch == '/' || ch == '_': | |||
continue | |||
case '0' <= ch && ch <= '9': | |||
continue | |||
case 'A' <= ch && ch <= 'Z': | |||
continue | |||
case 'a' <= ch && ch <= 'z': | |||
continue | |||
default: | |||
return true | |||
} | |||
} | |||
return false | |||
} | |||
// writeProto3Any writes an expanded google.protobuf.Any message. | |||
// | |||
// It returns (false, nil) if sv value can't be unmarshaled (e.g. because | |||
// required messages are not linked in). | |||
// | |||
// It returns (true, error) when sv was written in expanded format or an error | |||
// was encountered. | |||
func (w *textWriter) writeProto3Any(m protoreflect.Message) (bool, error) { | |||
md := m.Descriptor() | |||
fdURL := md.Fields().ByName("type_url") | |||
fdVal := md.Fields().ByName("value") | |||
url := m.Get(fdURL).String() | |||
mt, err := protoregistry.GlobalTypes.FindMessageByURL(url) | |||
if err != nil { | |||
return false, nil | |||
} | |||
b := m.Get(fdVal).Bytes() | |||
m2 := mt.New() | |||
if err := proto.Unmarshal(b, m2.Interface()); err != nil { | |||
return false, nil | |||
} | |||
w.Write([]byte("[")) | |||
if requiresQuotes(url) { | |||
w.writeQuotedString(url) | |||
} else { | |||
w.Write([]byte(url)) | |||
} | |||
if w.compact { | |||
w.Write([]byte("]:<")) | |||
} else { | |||
w.Write([]byte("]: <\n")) | |||
w.indent++ | |||
} | |||
if err := w.writeMessage(m2); err != nil { | |||
return true, err | |||
} | |||
if w.compact { | |||
w.Write([]byte("> ")) | |||
} else { | |||
w.indent-- | |||
w.Write([]byte(">\n")) | |||
} | |||
return true, nil | |||
} | |||
func (w *textWriter) writeMessage(m protoreflect.Message) error { | |||
md := m.Descriptor() | |||
if w.expandAny && md.FullName() == "google.protobuf.Any" { | |||
if canExpand, err := w.writeProto3Any(m); canExpand { | |||
return err | |||
} | |||
} | |||
fds := md.Fields() | |||
for i := 0; i < fds.Len(); { | |||
fd := fds.Get(i) | |||
if od := fd.ContainingOneof(); od != nil { | |||
fd = m.WhichOneof(od) | |||
i += od.Fields().Len() | |||
} else { | |||
i++ | |||
} | |||
if fd == nil || !m.Has(fd) { | |||
continue | |||
} | |||
switch { | |||
case fd.IsList(): | |||
lv := m.Get(fd).List() | |||
for j := 0; j < lv.Len(); j++ { | |||
w.writeName(fd) | |||
v := lv.Get(j) | |||
if err := w.writeSingularValue(v, fd); err != nil { | |||
return err | |||
} | |||
w.WriteByte('\n') | |||
} | |||
case fd.IsMap(): | |||
kfd := fd.MapKey() | |||
vfd := fd.MapValue() | |||
mv := m.Get(fd).Map() | |||
type entry struct{ key, val protoreflect.Value } | |||
var entries []entry | |||
mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { | |||
entries = append(entries, entry{k.Value(), v}) | |||
return true | |||
}) | |||
sort.Slice(entries, func(i, j int) bool { | |||
switch kfd.Kind() { | |||
case protoreflect.BoolKind: | |||
return !entries[i].key.Bool() && entries[j].key.Bool() | |||
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: | |||
return entries[i].key.Int() < entries[j].key.Int() | |||
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind: | |||
return entries[i].key.Uint() < entries[j].key.Uint() | |||
case protoreflect.StringKind: | |||
return entries[i].key.String() < entries[j].key.String() | |||
default: | |||
panic("invalid kind") | |||
} | |||
}) | |||
for _, entry := range entries { | |||
w.writeName(fd) | |||
w.WriteByte('<') | |||
if !w.compact { | |||
w.WriteByte('\n') | |||
} | |||
w.indent++ | |||
w.writeName(kfd) | |||
if err := w.writeSingularValue(entry.key, kfd); err != nil { | |||
return err | |||
} | |||
w.WriteByte('\n') | |||
w.writeName(vfd) | |||
if err := w.writeSingularValue(entry.val, vfd); err != nil { | |||
return err | |||
} | |||
w.WriteByte('\n') | |||
w.indent-- | |||
w.WriteByte('>') | |||
w.WriteByte('\n') | |||
} | |||
default: | |||
w.writeName(fd) | |||
if err := w.writeSingularValue(m.Get(fd), fd); err != nil { | |||
return err | |||
} | |||
w.WriteByte('\n') | |||
} | |||
} | |||
if b := m.GetUnknown(); len(b) > 0 { | |||
w.writeUnknownFields(b) | |||
} | |||
return w.writeExtensions(m) | |||
} | |||
func (w *textWriter) writeSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) error { | |||
switch fd.Kind() { | |||
case protoreflect.FloatKind, protoreflect.DoubleKind: | |||
switch vf := v.Float(); { | |||
case math.IsInf(vf, +1): | |||
w.Write(posInf) | |||
case math.IsInf(vf, -1): | |||
w.Write(negInf) | |||
case math.IsNaN(vf): | |||
w.Write(nan) | |||
default: | |||
fmt.Fprint(w, v.Interface()) | |||
} | |||
case protoreflect.StringKind: | |||
// NOTE: This does not validate UTF-8 for historical reasons. | |||
w.writeQuotedString(string(v.String())) | |||
case protoreflect.BytesKind: | |||
w.writeQuotedString(string(v.Bytes())) | |||
case protoreflect.MessageKind, protoreflect.GroupKind: | |||
var bra, ket byte = '<', '>' | |||
if fd.Kind() == protoreflect.GroupKind { | |||
bra, ket = '{', '}' | |||
} | |||
w.WriteByte(bra) | |||
if !w.compact { | |||
w.WriteByte('\n') | |||
} | |||
w.indent++ | |||
m := v.Message() | |||
if m2, ok := m.Interface().(encoding.TextMarshaler); ok { | |||
b, err := m2.MarshalText() | |||
if err != nil { | |||
return err | |||
} | |||
w.Write(b) | |||
} else { | |||
w.writeMessage(m) | |||
} | |||
w.indent-- | |||
w.WriteByte(ket) | |||
case protoreflect.EnumKind: | |||
if ev := fd.Enum().Values().ByNumber(v.Enum()); ev != nil { | |||
fmt.Fprint(w, ev.Name()) | |||
} else { | |||
fmt.Fprint(w, v.Enum()) | |||
} | |||
default: | |||
fmt.Fprint(w, v.Interface()) | |||
} | |||
return nil | |||
} | |||
// writeQuotedString writes a quoted string in the protocol buffer text format. | |||
func (w *textWriter) writeQuotedString(s string) { | |||
w.WriteByte('"') | |||
for i := 0; i < len(s); i++ { | |||
switch c := s[i]; c { | |||
case '\n': | |||
w.buf = append(w.buf, `\n`...) | |||
case '\r': | |||
w.buf = append(w.buf, `\r`...) | |||
case '\t': | |||
w.buf = append(w.buf, `\t`...) | |||
case '"': | |||
w.buf = append(w.buf, `\"`...) | |||
case '\\': | |||
w.buf = append(w.buf, `\\`...) | |||
default: | |||
if isPrint := c >= 0x20 && c < 0x7f; isPrint { | |||
w.buf = append(w.buf, c) | |||
} else { | |||
w.buf = append(w.buf, fmt.Sprintf(`\%03o`, c)...) | |||
} | |||
} | |||
} | |||
w.WriteByte('"') | |||
} | |||
func (w *textWriter) writeUnknownFields(b []byte) { | |||
if !w.compact { | |||
fmt.Fprintf(w, "/* %d unknown bytes */\n", len(b)) | |||
} | |||
for len(b) > 0 { | |||
num, wtyp, n := protowire.ConsumeTag(b) | |||
if n < 0 { | |||
return | |||
} | |||
b = b[n:] | |||
if wtyp == protowire.EndGroupType { | |||
w.indent-- | |||
w.Write(endBraceNewline) | |||
continue | |||
} | |||
fmt.Fprint(w, num) | |||
if wtyp != protowire.StartGroupType { | |||
w.WriteByte(':') | |||
} | |||
if !w.compact || wtyp == protowire.StartGroupType { | |||
w.WriteByte(' ') | |||
} | |||
switch wtyp { | |||
case protowire.VarintType: | |||
v, n := protowire.ConsumeVarint(b) | |||
if n < 0 { | |||
return | |||
} | |||
b = b[n:] | |||
fmt.Fprint(w, v) | |||
case protowire.Fixed32Type: | |||
v, n := protowire.ConsumeFixed32(b) | |||
if n < 0 { | |||
return | |||
} | |||
b = b[n:] | |||
fmt.Fprint(w, v) | |||
case protowire.Fixed64Type: | |||
v, n := protowire.ConsumeFixed64(b) | |||
if n < 0 { | |||
return | |||
} | |||
b = b[n:] | |||
fmt.Fprint(w, v) | |||
case protowire.BytesType: | |||
v, n := protowire.ConsumeBytes(b) | |||
if n < 0 { | |||
return | |||
} | |||
b = b[n:] | |||
fmt.Fprintf(w, "%q", v) | |||
case protowire.StartGroupType: | |||
w.WriteByte('{') | |||
w.indent++ | |||
default: | |||
fmt.Fprintf(w, "/* unknown wire type %d */", wtyp) | |||
} | |||
w.WriteByte('\n') | |||
} | |||
} | |||
// writeExtensions writes all the extensions in m. | |||
func (w *textWriter) writeExtensions(m protoreflect.Message) error { | |||
md := m.Descriptor() | |||
if md.ExtensionRanges().Len() == 0 { | |||
return nil | |||
} | |||
type ext struct { | |||
desc protoreflect.FieldDescriptor | |||
val protoreflect.Value | |||
} | |||
var exts []ext | |||
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { | |||
if fd.IsExtension() { | |||
exts = append(exts, ext{fd, v}) | |||
} | |||
return true | |||
}) | |||
sort.Slice(exts, func(i, j int) bool { | |||
return exts[i].desc.Number() < exts[j].desc.Number() | |||
}) | |||
for _, ext := range exts { | |||
// For message set, use the name of the message as the extension name. | |||
name := string(ext.desc.FullName()) | |||
if isMessageSet(ext.desc.ContainingMessage()) { | |||
name = strings.TrimSuffix(name, ".message_set_extension") | |||
} | |||
if !ext.desc.IsList() { | |||
if err := w.writeSingularExtension(name, ext.val, ext.desc); err != nil { | |||
return err | |||
} | |||
} else { | |||
lv := ext.val.List() | |||
for i := 0; i < lv.Len(); i++ { | |||
if err := w.writeSingularExtension(name, lv.Get(i), ext.desc); err != nil { | |||
return err | |||
} | |||
} | |||
} | |||
} | |||
return nil | |||
} | |||
func (w *textWriter) writeSingularExtension(name string, v protoreflect.Value, fd protoreflect.FieldDescriptor) error { | |||
fmt.Fprintf(w, "[%s]:", name) | |||
if !w.compact { | |||
w.WriteByte(' ') | |||
} | |||
if err := w.writeSingularValue(v, fd); err != nil { | |||
return err | |||
} | |||
w.WriteByte('\n') | |||
return nil | |||
} | |||
func (w *textWriter) writeIndent() { | |||
if !w.complete { | |||
return | |||
} | |||
for i := 0; i < w.indent*2; i++ { | |||
w.buf = append(w.buf, ' ') | |||
} | |||
w.complete = false | |||
} |
@ -1,880 +0,0 @@ | |||
// Go support for Protocol Buffers - Google's data interchange format | |||
// | |||
// Copyright 2010 The Go Authors. All rights reserved. | |||
// https://github.com/golang/protobuf | |||
// | |||
// 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. | |||
// * Neither the name of Google Inc. nor the names of its | |||
// contributors may be used to endorse or promote products derived from | |||
// this software without specific prior written permission. | |||
// | |||
// 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. | |||
package proto | |||
// Functions for parsing the Text protocol buffer format. | |||
// TODO: message sets. | |||
import ( | |||
"encoding" | |||
"errors" | |||
"fmt" | |||
"reflect" | |||
"strconv" | |||
"strings" | |||
"unicode/utf8" | |||
) | |||
// Error string emitted when deserializing Any and fields are already set | |||
const anyRepeatedlyUnpacked = "Any message unpacked multiple times, or %q already set" | |||
type ParseError struct { | |||
Message string | |||
Line int // 1-based line number | |||
Offset int // 0-based byte offset from start of input | |||
} | |||
func (p *ParseError) Error() string { | |||
if p.Line == 1 { | |||
// show offset only for first line | |||
return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message) | |||
} | |||
return fmt.Sprintf("line %d: %v", p.Line, p.Message) | |||
} | |||
type token struct { | |||
value string | |||
err *ParseError | |||
line int // line number | |||
offset int // byte number from start of input, not start of line | |||
unquoted string // the unquoted version of value, if it was a quoted string | |||
} | |||
func (t *token) String() string { | |||
if t.err == nil { | |||
return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset) | |||
} | |||
return fmt.Sprintf("parse error: %v", t.err) | |||
} | |||
type textParser struct { | |||
s string // remaining input | |||
done bool // whether the parsing is finished (success or error) | |||
backed bool // whether back() was called | |||
offset, line int | |||
cur token | |||
} | |||
func newTextParser(s string) *textParser { | |||
p := new(textParser) | |||
p.s = s | |||
p.line = 1 | |||
p.cur.line = 1 | |||
return p | |||
} | |||
func (p *textParser) errorf(format string, a ...interface{}) *ParseError { | |||
pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset} | |||
p.cur.err = pe | |||
p.done = true | |||
return pe | |||
} | |||
// Numbers and identifiers are matched by [-+._A-Za-z0-9] | |||
func isIdentOrNumberChar(c byte) bool { | |||
switch { | |||
case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z': | |||
return true | |||
case '0' <= c && c <= '9': | |||
return true | |||
} | |||
switch c { | |||
case '-', '+', '.', '_': | |||
return true | |||
} | |||
return false | |||
} | |||
func isWhitespace(c byte) bool { | |||
switch c { | |||
case ' ', '\t', '\n', '\r': | |||
return true | |||
} | |||
return false | |||
} | |||
func isQuote(c byte) bool { | |||
switch c { | |||
case '"', '\'': | |||
return true | |||
} | |||
return false | |||
} | |||
func (p *textParser) skipWhitespace() { | |||
i := 0 | |||
for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') { | |||
if p.s[i] == '#' { | |||
// comment; skip to end of line or input | |||
for i < len(p.s) && p.s[i] != '\n' { | |||
i++ | |||
} | |||
if i == len(p.s) { | |||
break | |||
} | |||
} | |||
if p.s[i] == '\n' { | |||
p.line++ | |||
} | |||
i++ | |||
} | |||
p.offset += i | |||
p.s = p.s[i:len(p.s)] | |||
if len(p.s) == 0 { | |||
p.done = true | |||
} | |||
} | |||
func (p *textParser) advance() { | |||
// Skip whitespace | |||
p.skipWhitespace() | |||
if p.done { | |||
return | |||
} | |||
// Start of non-whitespace | |||
p.cur.err = nil | |||
p.cur.offset, p.cur.line = p.offset, p.line | |||
p.cur.unquoted = "" | |||
switch p.s[0] { | |||
case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/': | |||
// Single symbol | |||
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)] | |||
case '"', '\'': | |||
// Quoted string | |||
i := 1 | |||
for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' { | |||
if p.s[i] == '\\' && i+1 < len(p.s) { | |||
// skip escaped char | |||
i++ | |||
} | |||
i++ | |||
} | |||
if i >= len(p.s) || p.s[i] != p.s[0] { | |||
p.errorf("unmatched quote") | |||
return | |||
} | |||
unq, err := unquoteC(p.s[1:i], rune(p.s[0])) | |||
if err != nil { | |||
p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err) | |||
return | |||
} | |||
p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)] | |||
p.cur.unquoted = unq | |||
default: | |||
i := 0 | |||
for i < len(p.s) && isIdentOrNumberChar(p.s[i]) { | |||
i++ | |||
} | |||
if i == 0 { | |||
p.errorf("unexpected byte %#x", p.s[0]) | |||
return | |||
} | |||
p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)] | |||
} | |||
p.offset += len(p.cur.value) | |||
} | |||
var ( | |||
errBadUTF8 = errors.New("proto: bad UTF-8") | |||
) | |||
func unquoteC(s string, quote rune) (string, error) { | |||
// This is based on C++'s tokenizer.cc. | |||
// Despite its name, this is *not* parsing C syntax. | |||
// For instance, "\0" is an invalid quoted string. | |||
// Avoid allocation in trivial cases. | |||
simple := true | |||
for _, r := range s { | |||
if r == '\\' || r == quote { | |||
simple = false | |||
break | |||
} | |||
} | |||
if simple { | |||
return s, nil | |||
} | |||
buf := make([]byte, 0, 3*len(s)/2) | |||
for len(s) > 0 { | |||
r, n := utf8.DecodeRuneInString(s) | |||
if r == utf8.RuneError && n == 1 { | |||
return "", errBadUTF8 | |||
} | |||
s = s[n:] | |||
if r != '\\' { | |||
if r < utf8.RuneSelf { | |||
buf = append(buf, byte(r)) | |||
} else { | |||
buf = append(buf, string(r)...) | |||
} | |||
continue | |||
} | |||
ch, tail, err := unescape(s) | |||
if err != nil { | |||
return "", err | |||
} | |||
buf = append(buf, ch...) | |||
s = tail | |||
} | |||
return string(buf), nil | |||
} | |||
func unescape(s string) (ch string, tail string, err error) { | |||
r, n := utf8.DecodeRuneInString(s) | |||
if r == utf8.RuneError && n == 1 { | |||
return "", "", errBadUTF8 | |||
} | |||
s = s[n:] | |||
switch r { | |||
case 'a': | |||
return "\a", s, nil | |||
case 'b': | |||
return "\b", s, nil | |||
case 'f': | |||
return "\f", s, nil | |||
case 'n': | |||
return "\n", s, nil | |||
case 'r': | |||
return "\r", s, nil | |||
case 't': | |||
return "\t", s, nil | |||
case 'v': | |||
return "\v", s, nil | |||
case '?': | |||
return "?", s, nil // trigraph workaround | |||
case '\'', '"', '\\': | |||
return string(r), s, nil | |||
case '0', '1', '2', '3', '4', '5', '6', '7': | |||
if len(s) < 2 { | |||
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r) | |||
} | |||
ss := string(r) + s[:2] | |||
s = s[2:] | |||
i, err := strconv.ParseUint(ss, 8, 8) | |||
if err != nil { | |||
return "", "", fmt.Errorf(`\%s contains non-octal digits`, ss) | |||
} | |||
return string([]byte{byte(i)}), s, nil | |||
case 'x', 'X', 'u', 'U': | |||
var n int | |||
switch r { | |||
case 'x', 'X': | |||
n = 2 | |||
case 'u': | |||
n = 4 | |||
case 'U': | |||
n = 8 | |||
} | |||
if len(s) < n { | |||
return "", "", fmt.Errorf(`\%c requires %d following digits`, r, n) | |||
} | |||
ss := s[:n] | |||
s = s[n:] | |||
i, err := strconv.ParseUint(ss, 16, 64) | |||
if err != nil { | |||
return "", "", fmt.Errorf(`\%c%s contains non-hexadecimal digits`, r, ss) | |||
} | |||
if r == 'x' || r == 'X' { | |||
return string([]byte{byte(i)}), s, nil | |||
} | |||
if i > utf8.MaxRune { | |||
return "", "", fmt.Errorf(`\%c%s is not a valid Unicode code point`, r, ss) | |||
} | |||
return string(i), s, nil | |||
} | |||
return "", "", fmt.Errorf(`unknown escape \%c`, r) | |||
} | |||
// Back off the parser by one token. Can only be done between calls to next(). | |||
// It makes the next advance() a no-op. | |||
func (p *textParser) back() { p.backed = true } | |||
// Advances the parser and returns the new current token. | |||
func (p *textParser) next() *token { | |||
if p.backed || p.done { | |||
p.backed = false | |||
return &p.cur | |||
} | |||
p.advance() | |||
if p.done { | |||
p.cur.value = "" | |||
} else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) { | |||
// Look for multiple quoted strings separated by whitespace, | |||
// and concatenate them. | |||
cat := p.cur | |||
for { | |||
p.skipWhitespace() | |||
if p.done || !isQuote(p.s[0]) { | |||
break | |||
} | |||
p.advance() | |||
if p.cur.err != nil { | |||
return &p.cur | |||
} | |||
cat.value += " " + p.cur.value | |||
cat.unquoted += p.cur.unquoted | |||
} | |||
p.done = false // parser may have seen EOF, but we want to return cat | |||
p.cur = cat | |||
} | |||
return &p.cur | |||
} | |||
func (p *textParser) consumeToken(s string) error { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
if tok.value != s { | |||
p.back() | |||
return p.errorf("expected %q, found %q", s, tok.value) | |||
} | |||
return nil | |||
} | |||
// Return a RequiredNotSetError indicating which required field was not set. | |||
func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError { | |||
st := sv.Type() | |||
sprops := GetProperties(st) | |||
for i := 0; i < st.NumField(); i++ { | |||
if !isNil(sv.Field(i)) { | |||
continue | |||
} | |||
props := sprops.Prop[i] | |||
if props.Required { | |||
return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)} | |||
} | |||
} | |||
return &RequiredNotSetError{fmt.Sprintf("%v.<unknown field name>", st)} // should not happen | |||
} | |||
// Returns the index in the struct for the named field, as well as the parsed tag properties. | |||
func structFieldByName(sprops *StructProperties, name string) (int, *Properties, bool) { | |||
i, ok := sprops.decoderOrigNames[name] | |||
if ok { | |||
return i, sprops.Prop[i], true | |||
} | |||
return -1, nil, false | |||
} | |||
// Consume a ':' from the input stream (if the next token is a colon), | |||
// returning an error if a colon is needed but not present. | |||
func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
if tok.value != ":" { | |||
// Colon is optional when the field is a group or message. | |||
needColon := true | |||
switch props.Wire { | |||
case "group": | |||
needColon = false | |||
case "bytes": | |||
// A "bytes" field is either a message, a string, or a repeated field; | |||
// those three become *T, *string and []T respectively, so we can check for | |||
// this field being a pointer to a non-string. | |||
if typ.Kind() == reflect.Ptr { | |||
// *T or *string | |||
if typ.Elem().Kind() == reflect.String { | |||
break | |||
} | |||
} else if typ.Kind() == reflect.Slice { | |||
// []T or []*T | |||
if typ.Elem().Kind() != reflect.Ptr { | |||
break | |||
} | |||
} else if typ.Kind() == reflect.String { | |||
// The proto3 exception is for a string field, | |||
// which requires a colon. | |||
break | |||
} | |||
needColon = false | |||
} | |||
if needColon { | |||
return p.errorf("expected ':', found %q", tok.value) | |||
} | |||
p.back() | |||
} | |||
return nil | |||
} | |||
func (p *textParser) readStruct(sv reflect.Value, terminator string) error { | |||
st := sv.Type() | |||
sprops := GetProperties(st) | |||
reqCount := sprops.reqCount | |||
var reqFieldErr error | |||
fieldSet := make(map[string]bool) | |||
// A struct is a sequence of "name: value", terminated by one of | |||
// '>' or '}', or the end of the input. A name may also be | |||
// "[extension]" or "[type/url]". | |||
// | |||
// The whole struct can also be an expanded Any message, like: | |||
// [type/url] < ... struct contents ... > | |||
for { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
if tok.value == terminator { | |||
break | |||
} | |||
if tok.value == "[" { | |||
// Looks like an extension or an Any. | |||
// | |||
// TODO: Check whether we need to handle | |||
// namespace rooted names (e.g. ".something.Foo"). | |||
extName, err := p.consumeExtName() | |||
if err != nil { | |||
return err | |||
} | |||
if s := strings.LastIndex(extName, "/"); s >= 0 { | |||
// If it contains a slash, it's an Any type URL. | |||
messageName := extName[s+1:] | |||
mt := MessageType(messageName) | |||
if mt == nil { | |||
return p.errorf("unrecognized message %q in google.protobuf.Any", messageName) | |||
} | |||
tok = p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
// consume an optional colon | |||
if tok.value == ":" { | |||
tok = p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
} | |||
var terminator string | |||
switch tok.value { | |||
case "<": | |||
terminator = ">" | |||
case "{": | |||
terminator = "}" | |||
default: | |||
return p.errorf("expected '{' or '<', found %q", tok.value) | |||
} | |||
v := reflect.New(mt.Elem()) | |||
if pe := p.readStruct(v.Elem(), terminator); pe != nil { | |||
return pe | |||
} | |||
b, err := Marshal(v.Interface().(Message)) | |||
if err != nil { | |||
return p.errorf("failed to marshal message of type %q: %v", messageName, err) | |||
} | |||
if fieldSet["type_url"] { | |||
return p.errorf(anyRepeatedlyUnpacked, "type_url") | |||
} | |||
if fieldSet["value"] { | |||
return p.errorf(anyRepeatedlyUnpacked, "value") | |||
} | |||
sv.FieldByName("TypeUrl").SetString(extName) | |||
sv.FieldByName("Value").SetBytes(b) | |||
fieldSet["type_url"] = true | |||
fieldSet["value"] = true | |||
continue | |||
} | |||
var desc *ExtensionDesc | |||
// This could be faster, but it's functional. | |||
// TODO: Do something smarter than a linear scan. | |||
for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) { | |||
if d.Name == extName { | |||
desc = d | |||
break | |||
} | |||
} | |||
if desc == nil { | |||
return p.errorf("unrecognized extension %q", extName) | |||
} | |||
props := &Properties{} | |||
props.Parse(desc.Tag) | |||
typ := reflect.TypeOf(desc.ExtensionType) | |||
if err := p.checkForColon(props, typ); err != nil { | |||
return err | |||
} | |||
rep := desc.repeated() | |||
// Read the extension structure, and set it in | |||
// the value we're constructing. | |||
var ext reflect.Value | |||
if !rep { | |||
ext = reflect.New(typ).Elem() | |||
} else { | |||
ext = reflect.New(typ.Elem()).Elem() | |||
} | |||
if err := p.readAny(ext, props); err != nil { | |||
if _, ok := err.(*RequiredNotSetError); !ok { | |||
return err | |||
} | |||
reqFieldErr = err | |||
} | |||
ep := sv.Addr().Interface().(Message) | |||
if !rep { | |||
SetExtension(ep, desc, ext.Interface()) | |||
} else { | |||
old, err := GetExtension(ep, desc) | |||
var sl reflect.Value | |||
if err == nil { | |||
sl = reflect.ValueOf(old) // existing slice | |||
} else { | |||
sl = reflect.MakeSlice(typ, 0, 1) | |||
} | |||
sl = reflect.Append(sl, ext) | |||
SetExtension(ep, desc, sl.Interface()) | |||
} | |||
if err := p.consumeOptionalSeparator(); err != nil { | |||
return err | |||
} | |||
continue | |||
} | |||
// This is a normal, non-extension field. | |||
name := tok.value | |||
var dst reflect.Value | |||
fi, props, ok := structFieldByName(sprops, name) | |||
if ok { | |||
dst = sv.Field(fi) | |||
} else if oop, ok := sprops.OneofTypes[name]; ok { | |||
// It is a oneof. | |||
props = oop.Prop | |||
nv := reflect.New(oop.Type.Elem()) | |||
dst = nv.Elem().Field(0) | |||
field := sv.Field(oop.Field) | |||
if !field.IsNil() { | |||
return p.errorf("field '%s' would overwrite already parsed oneof '%s'", name, sv.Type().Field(oop.Field).Name) | |||
} | |||
field.Set(nv) | |||
} | |||
if !dst.IsValid() { | |||
return p.errorf("unknown field name %q in %v", name, st) | |||
} | |||
if dst.Kind() == reflect.Map { | |||
// Consume any colon. | |||
if err := p.checkForColon(props, dst.Type()); err != nil { | |||
return err | |||
} | |||
// Construct the map if it doesn't already exist. | |||
if dst.IsNil() { | |||
dst.Set(reflect.MakeMap(dst.Type())) | |||
} | |||
key := reflect.New(dst.Type().Key()).Elem() | |||
val := reflect.New(dst.Type().Elem()).Elem() | |||
// The map entry should be this sequence of tokens: | |||
// < key : KEY value : VALUE > | |||
// However, implementations may omit key or value, and technically | |||
// we should support them in any order. See b/28924776 for a time | |||
// this went wrong. | |||
tok := p.next() | |||
var terminator string | |||
switch tok.value { | |||
case "<": | |||
terminator = ">" | |||
case "{": | |||
terminator = "}" | |||
default: | |||
return p.errorf("expected '{' or '<', found %q", tok.value) | |||
} | |||
for { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
if tok.value == terminator { | |||
break | |||
} | |||
switch tok.value { | |||
case "key": | |||
if err := p.consumeToken(":"); err != nil { | |||
return err | |||
} | |||
if err := p.readAny(key, props.MapKeyProp); err != nil { | |||
return err | |||
} | |||
if err := p.consumeOptionalSeparator(); err != nil { | |||
return err | |||
} | |||
case "value": | |||
if err := p.checkForColon(props.MapValProp, dst.Type().Elem()); err != nil { | |||
return err | |||
} | |||
if err := p.readAny(val, props.MapValProp); err != nil { | |||
return err | |||
} | |||
if err := p.consumeOptionalSeparator(); err != nil { | |||
return err | |||
} | |||
default: | |||
p.back() | |||
return p.errorf(`expected "key", "value", or %q, found %q`, terminator, tok.value) | |||
} | |||
} | |||
dst.SetMapIndex(key, val) | |||
continue | |||
} | |||
// Check that it's not already set if it's not a repeated field. | |||
if !props.Repeated && fieldSet[name] { | |||
return p.errorf("non-repeated field %q was repeated", name) | |||
} | |||
if err := p.checkForColon(props, dst.Type()); err != nil { | |||
return err | |||
} | |||
// Parse into the field. | |||
fieldSet[name] = true | |||
if err := p.readAny(dst, props); err != nil { | |||
if _, ok := err.(*RequiredNotSetError); !ok { | |||
return err | |||
} | |||
reqFieldErr = err | |||
} | |||
if props.Required { | |||
reqCount-- | |||
} | |||
if err := p.consumeOptionalSeparator(); err != nil { | |||
return err | |||
} | |||
} | |||
if reqCount > 0 { | |||
return p.missingRequiredFieldError(sv) | |||
} | |||
return reqFieldErr | |||
} | |||
// consumeExtName consumes extension name or expanded Any type URL and the | |||
// following ']'. It returns the name or URL consumed. | |||
func (p *textParser) consumeExtName() (string, error) { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return "", tok.err | |||
} | |||
// If extension name or type url is quoted, it's a single token. | |||
if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] { | |||
name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0])) | |||
if err != nil { | |||
return "", err | |||
} | |||
return name, p.consumeToken("]") | |||
} | |||
// Consume everything up to "]" | |||
var parts []string | |||
for tok.value != "]" { | |||
parts = append(parts, tok.value) | |||
tok = p.next() | |||
if tok.err != nil { | |||
return "", p.errorf("unrecognized type_url or extension name: %s", tok.err) | |||
} | |||
if p.done && tok.value != "]" { | |||
return "", p.errorf("unclosed type_url or extension name") | |||
} | |||
} | |||
return strings.Join(parts, ""), nil | |||
} | |||
// consumeOptionalSeparator consumes an optional semicolon or comma. | |||
// It is used in readStruct to provide backward compatibility. | |||
func (p *textParser) consumeOptionalSeparator() error { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
if tok.value != ";" && tok.value != "," { | |||
p.back() | |||
} | |||
return nil | |||
} | |||
func (p *textParser) readAny(v reflect.Value, props *Properties) error { | |||
tok := p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
if tok.value == "" { | |||
return p.errorf("unexpected EOF") | |||
} | |||
switch fv := v; fv.Kind() { | |||
case reflect.Slice: | |||
at := v.Type() | |||
if at.Elem().Kind() == reflect.Uint8 { | |||
// Special case for []byte | |||
if tok.value[0] != '"' && tok.value[0] != '\'' { | |||
// Deliberately written out here, as the error after | |||
// this switch statement would write "invalid []byte: ...", | |||
// which is not as user-friendly. | |||
return p.errorf("invalid string: %v", tok.value) | |||
} | |||
bytes := []byte(tok.unquoted) | |||
fv.Set(reflect.ValueOf(bytes)) | |||
return nil | |||
} | |||
// Repeated field. | |||
if tok.value == "[" { | |||
// Repeated field with list notation, like [1,2,3]. | |||
for { | |||
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem())) | |||
err := p.readAny(fv.Index(fv.Len()-1), props) | |||
if err != nil { | |||
return err | |||
} | |||
tok := p.next() | |||
if tok.err != nil { | |||
return tok.err | |||
} | |||
if tok.value == "]" { | |||
break | |||
} | |||
if tok.value != "," { | |||
return p.errorf("Expected ']' or ',' found %q", tok.value) | |||
} | |||
} | |||
return nil | |||
} | |||
// One value of the repeated field. | |||
p.back() | |||
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem())) | |||
return p.readAny(fv.Index(fv.Len()-1), props) | |||
case reflect.Bool: | |||
// true/1/t/True or false/f/0/False. | |||
switch tok.value { | |||
case "true", "1", "t", "True": | |||
fv.SetBool(true) | |||
return nil | |||
case "false", "0", "f", "False": | |||
fv.SetBool(false) | |||
return nil | |||
} | |||
case reflect.Float32, reflect.Float64: | |||
v := tok.value | |||
// Ignore 'f' for compatibility with output generated by C++, but don't | |||
// remove 'f' when the value is "-inf" or "inf". | |||
if strings.HasSuffix(v, "f") && tok.value != "-inf" && tok.value != "inf" { | |||
v = v[:len(v)-1] | |||
} | |||
if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil { | |||
fv.SetFloat(f) | |||
return nil | |||
} | |||
case reflect.Int32: | |||
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil { | |||
fv.SetInt(x) | |||
return nil | |||
} | |||
if len(props.Enum) == 0 { | |||
break | |||
} | |||
m, ok := enumValueMaps[props.Enum] | |||
if !ok { | |||
break | |||
} | |||
x, ok := m[tok.value] | |||
if !ok { | |||
break | |||
} | |||
fv.SetInt(int64(x)) | |||
return nil | |||
case reflect.Int64: | |||
if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil { | |||
fv.SetInt(x) | |||
return nil | |||
} | |||
case reflect.Ptr: | |||
// A basic field (indirected through pointer), or a repeated message/group | |||
p.back() | |||
fv.Set(reflect.New(fv.Type().Elem())) | |||
return p.readAny(fv.Elem(), props) | |||
case reflect.String: | |||
if tok.value[0] == '"' || tok.value[0] == '\'' { | |||
fv.SetString(tok.unquoted) | |||
return nil | |||
} | |||
case reflect.Struct: | |||
var terminator string | |||
switch tok.value { | |||
case "{": | |||
terminator = "}" | |||
case "<": | |||
terminator = ">" | |||
default: | |||
return p.errorf("expected '{' or '<', found %q", tok.value) | |||
} | |||
// TODO: Handle nested messages which implement encoding.TextUnmarshaler. | |||
return p.readStruct(fv, terminator) | |||
case reflect.Uint32: | |||
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil { | |||
fv.SetUint(uint64(x)) | |||
return nil | |||
} | |||
case reflect.Uint64: | |||
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil { | |||
fv.SetUint(x) | |||
return nil | |||
} | |||
} | |||
return p.errorf("invalid %v: %v", v.Type(), tok.value) | |||
} | |||
// UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb | |||
// before starting to unmarshal, so any existing data in pb is always removed. | |||
// If a required field is not set and no other error occurs, | |||
// UnmarshalText returns *RequiredNotSetError. | |||
func UnmarshalText(s string, pb Message) error { | |||
if um, ok := pb.(encoding.TextUnmarshaler); ok { | |||
return um.UnmarshalText([]byte(s)) | |||
} | |||
pb.Reset() | |||
v := reflect.ValueOf(pb) | |||
return newTextParser(s).readStruct(v.Elem(), "") | |||
} |
@ -0,0 +1,78 @@ | |||
// Copyright 2019 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package proto | |||
import ( | |||
protoV2 "google.golang.org/protobuf/proto" | |||
"google.golang.org/protobuf/runtime/protoiface" | |||
) | |||
// Size returns the size in bytes of the wire-format encoding of m. | |||
func Size(m Message) int { | |||
if m == nil { | |||
return 0 | |||
} | |||
mi := MessageV2(m) | |||
return protoV2.Size(mi) | |||
} | |||
// Marshal returns the wire-format encoding of m. | |||
func Marshal(m Message) ([]byte, error) { | |||
b, err := marshalAppend(nil, m, false) | |||
if b == nil { | |||
b = zeroBytes | |||
} | |||
return b, err | |||
} | |||
var zeroBytes = make([]byte, 0, 0) | |||
func marshalAppend(buf []byte, m Message, deterministic bool) ([]byte, error) { | |||
if m == nil { | |||
return nil, ErrNil | |||
} | |||
mi := MessageV2(m) | |||
nbuf, err := protoV2.MarshalOptions{ | |||
Deterministic: deterministic, | |||
AllowPartial: true, | |||
}.MarshalAppend(buf, mi) | |||
if err != nil { | |||
return buf, err | |||
} | |||
if len(buf) == len(nbuf) { | |||
if !mi.ProtoReflect().IsValid() { | |||
return buf, ErrNil | |||
} | |||
} | |||
return nbuf, checkRequiredNotSet(mi) | |||
} | |||
// Unmarshal parses a wire-format message in b and places the decoded results in m. | |||
// | |||
// Unmarshal resets m before starting to unmarshal, so any existing data in m is always | |||
// removed. Use UnmarshalMerge to preserve and append to existing data. | |||
func Unmarshal(b []byte, m Message) error { | |||
m.Reset() | |||
return UnmarshalMerge(b, m) | |||
} | |||
// UnmarshalMerge parses a wire-format message in b and places the decoded results in m. | |||
func UnmarshalMerge(b []byte, m Message) error { | |||
mi := MessageV2(m) | |||
out, err := protoV2.UnmarshalOptions{ | |||
AllowPartial: true, | |||
Merge: true, | |||
}.UnmarshalState(protoiface.UnmarshalInput{ | |||
Buf: b, | |||
Message: mi.ProtoReflect(), | |||
}) | |||
if err != nil { | |||
return err | |||
} | |||
if out.Flags&protoiface.UnmarshalInitialized > 0 { | |||
return nil | |||
} | |||
return checkRequiredNotSet(mi) | |||
} |
@ -0,0 +1,34 @@ | |||
// Copyright 2019 The Go Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package proto | |||
// Bool stores v in a new bool value and returns a pointer to it. | |||
func Bool(v bool) *bool { return &v } | |||
// Int stores v in a new int32 value and returns a pointer to it. | |||
// | |||
// Deprecated: Use Int32 instead. | |||
func Int(v int) *int32 { return Int32(int32(v)) } | |||
// Int32 stores v in a new int32 value and returns a pointer to it. | |||
func Int32(v int32) *int32 { return &v } | |||
// Int64 stores v in a new int64 value and returns a pointer to it. | |||
func Int64(v int64) *int64 { return &v } | |||
// Uint32 stores v in a new uint32 value and returns a pointer to it. | |||
func Uint32(v uint32) *uint32 { return &v } | |||
// Uint64 stores v in a new uint64 value and returns a pointer to it. | |||
func Uint64(v uint64) *uint64 { return &v } | |||
// Float32 stores v in a new float32 value and returns a pointer to it. | |||
func Float32(v float32) *float32 { return &v } | |||
// Float64 stores v in a new float64 value and returns a pointer to it. | |||
func Float64(v float64) *float64 { return &v } | |||
// String stores v in a new string value and returns a pointer to it. | |||
func String(v string) *string { return &v } |
@ -0,0 +1,363 @@ | |||
Mozilla Public License, version 2.0 | |||
1. Definitions | |||
1.1. "Contributor" | |||
means each individual or legal entity that creates, contributes to the | |||
creation of, or owns Covered Software. | |||
1.2. "Contributor Version" | |||
means the combination of the Contributions of others (if any) used by a | |||
Contributor and that particular Contributor's Contribution. | |||
1.3. "Contribution" | |||
means Covered Software of a particular Contributor. | |||
1.4. "Covered Software" | |||
means Source Code Form to which the initial Contributor has attached the | |||
notice in Exhibit A, the Executable Form of such Source Code Form, and | |||
Modifications of such Source Code Form, in each case including portions | |||
thereof. | |||
1.5. "Incompatible With Secondary Licenses" | |||
means | |||
a. that the initial Contributor has attached the notice described in | |||
Exhibit B to the Covered Software; or | |||
b. that the Covered Software was made available under the terms of | |||
version 1.1 or earlier of the License, but not also under the terms of | |||
a Secondary License. | |||
1.6. "Executable Form" | |||
means any form of the work other than Source Code Form. | |||
1.7. "Larger Work" | |||
means a work that combines Covered Software with other material, in a | |||
separate file or files, that is not Covered Software. | |||
1.8. "License" | |||
means this document. | |||
1.9. "Licensable" | |||
means having the right to grant, to the maximum extent possible, whether | |||
at the time of the initial grant or subsequently, any and all of the | |||
rights conveyed by this License. | |||
1.10. "Modifications" | |||
means any of the following: | |||
a. any file in Source Code Form that results from an addition to, | |||
deletion from, or modification of the contents of Covered Software; or | |||
b. any new file in Source Code Form that contains any Covered Software. | |||
1.11. "Patent Claims" of a Contributor | |||
means any patent claim(s), including without limitation, method, | |||
process, and apparatus claims, in any patent Licensable by such | |||
Contributor that would be infringed, but for the grant of the License, | |||
by the making, using, selling, offering for sale, having made, import, | |||
or transfer of either its Contributions or its Contributor Version. | |||
1.12. "Secondary License" | |||
means either the GNU General Public License, Version 2.0, the GNU Lesser | |||
General Public License, Version 2.1, the GNU Affero General Public | |||
License, Version 3.0, or any later versions of those licenses. | |||
1.13. "Source Code Form" | |||
means the form of the work preferred for making modifications. | |||
1.14. "You" (or "Your") | |||
means an individual or a legal entity exercising rights under this | |||
License. For legal entities, "You" includes any entity that controls, is | |||
controlled by, or is under common control with You. For purposes of this | |||
definition, "control" means (a) the power, direct or indirect, to cause | |||
the direction or management of such entity, whether by contract or | |||
otherwise, or (b) ownership of more than fifty percent (50%) of the | |||
outstanding shares or beneficial ownership of such entity. | |||
2. License Grants and Conditions | |||
2.1. Grants | |||
Each Contributor hereby grants You a world-wide, royalty-free, | |||
non-exclusive license: | |||
a. under intellectual property rights (other than patent or trademark) | |||
Licensable by such Contributor to use, reproduce, make available, | |||
modify, display, perform, distribute, and otherwise exploit its | |||
Contributions, either on an unmodified basis, with Modifications, or | |||
as part of a Larger Work; and | |||
b. under Patent Claims of such Contributor to make, use, sell, offer for | |||
sale, have made, import, and otherwise transfer either its | |||
Contributions or its Contributor Version. | |||
2.2. Effective Date | |||
The licenses granted in Section 2.1 with respect to any Contribution | |||
become effective for each Contribution on the date the Contributor first | |||
distributes such Contribution. | |||
2.3. Limitations on Grant Scope | |||
The licenses granted in this Section 2 are the only rights granted under | |||
this License. No additional rights or licenses will be implied from the | |||
distribution or licensing of Covered Software under this License. | |||
Notwithstanding Section 2.1(b) above, no patent license is granted by a | |||
Contributor: | |||
a. for any code that a Contributor has removed from Covered Software; or | |||
b. for infringements caused by: (i) Your and any other third party's | |||
modifications of Covered Software, or (ii) the combination of its | |||
Contributions with other software (except as part of its Contributor | |||
Version); or | |||
c. under Patent Claims infringed by Covered Software in the absence of | |||
its Contributions. | |||
This License does not grant any rights in the trademarks, service marks, | |||
or logos of any Contributor (except as may be necessary to comply with | |||
the notice requirements in Section 3.4). | |||
2.4. Subsequent Licenses | |||
No Contributor makes additional grants as a result of Your choice to | |||
distribute the Covered Software under a subsequent version of this | |||
License (see Section 10.2) or under the terms of a Secondary License (if | |||
permitted under the terms of Section 3.3). | |||
2.5. Representation | |||
Each Contributor represents that the Contributor believes its | |||
Contributions are its original creation(s) or it has sufficient rights to | |||
grant the rights to its Contributions conveyed by this License. | |||
2.6. Fair Use | |||
This License is not intended to limit any rights You have under | |||
applicable copyright doctrines of fair use, fair dealing, or other | |||
equivalents. | |||
2.7. Conditions | |||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | |||
Section 2.1. | |||
3. Responsibilities | |||
3.1. Distribution of Source Form | |||
All distribution of Covered Software in Source Code Form, including any | |||
Modifications that You create or to which You contribute, must be under | |||
the terms of this License. You must inform recipients that the Source | |||
Code Form of the Covered Software is governed by the terms of this | |||
License, and how they can obtain a copy of this License. You may not | |||
attempt to alter or restrict the recipients' rights in the Source Code | |||
Form. | |||
3.2. Distribution of Executable Form | |||
If You distribute Covered Software in Executable Form then: | |||
a. such Covered Software must also be made available in Source Code Form, | |||
as described in Section 3.1, and You must inform recipients of the | |||
Executable Form how they can obtain a copy of such Source Code Form by | |||
reasonable means in a timely manner, at a charge no more than the cost | |||
of distribution to the recipient; and | |||
b. You may distribute such Executable Form under the terms of this | |||
License, or sublicense it under different terms, provided that the | |||
license for the Executable Form does not attempt to limit or alter the | |||
recipients' rights in the Source Code Form under this License. | |||
3.3. Distribution of a Larger Work | |||
You may create and distribute a Larger Work under terms of Your choice, | |||
provided that You also comply with the requirements of this License for | |||
the Covered Software. If the Larger Work is a combination of Covered | |||
Software with a work governed by one or more Secondary Licenses, and the | |||
Covered Software is not Incompatible With Secondary Licenses, this | |||
License permits You to additionally distribute such Covered Software | |||
under the terms of such Secondary License(s), so that the recipient of | |||
the Larger Work may, at their option, further distribute the Covered | |||
Software under the terms of either this License or such Secondary | |||
License(s). | |||
3.4. Notices | |||
You may not remove or alter the substance of any license notices | |||
(including copyright notices, patent notices, disclaimers of warranty, or | |||
limitations of liability) contained within the Source Code Form of the | |||
Covered Software, except that You may alter any license notices to the | |||
extent required to remedy known factual inaccuracies. | |||
3.5. Application of Additional Terms | |||
You may choose to offer, and to charge a fee for, warranty, support, | |||
indemnity or liability obligations to one or more recipients of Covered | |||
Software. However, You may do so only on Your own behalf, and not on | |||
behalf of any Contributor. You must make it absolutely clear that any | |||
such warranty, support, indemnity, or liability obligation is offered by | |||
You alone, and You hereby agree to indemnify every Contributor for any | |||
liability incurred by such Contributor as a result of warranty, support, | |||
indemnity or liability terms You offer. You may include additional | |||
disclaimers of warranty and limitations of liability specific to any | |||
jurisdiction. | |||
4. Inability to Comply Due to Statute or Regulation | |||
If it is impossible for You to comply with any of the terms of this License | |||
with respect to some or all of the Covered Software due to statute, | |||
judicial order, or regulation then You must: (a) comply with the terms of | |||
this License to the maximum extent possible; and (b) describe the | |||
limitations and the code they affect. Such description must be placed in a | |||
text file included with all distributions of the Covered Software under | |||
this License. Except to the extent prohibited by statute or regulation, | |||
such description must be sufficiently detailed for a recipient of ordinary | |||
skill to be able to understand it. | |||
5. Termination | |||
5.1. The rights granted under this License will terminate automatically if You | |||
fail to comply with any of its terms. However, if You become compliant, | |||
then the rights granted under this License from a particular Contributor | |||
are reinstated (a) provisionally, unless and until such Contributor | |||
explicitly and finally terminates Your grants, and (b) on an ongoing | |||
basis, if such Contributor fails to notify You of the non-compliance by | |||
some reasonable means prior to 60 days after You have come back into | |||
compliance. Moreover, Your grants from a particular Contributor are | |||
reinstated on an ongoing basis if such Contributor notifies You of the | |||
non-compliance by some reasonable means, this is the first time You have | |||
received notice of non-compliance with this License from such | |||
Contributor, and You become compliant prior to 30 days after Your receipt | |||
of the notice. | |||
5.2. If You initiate litigation against any entity by asserting a patent | |||
infringement claim (excluding declaratory judgment actions, | |||
counter-claims, and cross-claims) alleging that a Contributor Version | |||
directly or indirectly infringes any patent, then the rights granted to | |||
You by any and all Contributors for the Covered Software under Section | |||
2.1 of this License shall terminate. | |||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user | |||
license agreements (excluding distributors and resellers) which have been | |||
validly granted by You or Your distributors under this License prior to | |||
termination shall survive termination. | |||
6. Disclaimer of Warranty | |||
Covered Software is provided under this License on an "as is" basis, | |||
without warranty of any kind, either expressed, implied, or statutory, | |||
including, without limitation, warranties that the Covered Software is free | |||
of defects, merchantable, fit for a particular purpose or non-infringing. | |||
The entire risk as to the quality and performance of the Covered Software | |||
is with You. Should any Covered Software prove defective in any respect, | |||
You (not any Contributor) assume the cost of any necessary servicing, | |||
repair, or correction. This disclaimer of warranty constitutes an essential | |||
part of this License. No use of any Covered Software is authorized under | |||
this License except under this disclaimer. | |||
7. Limitation of Liability | |||
Under no circumstances and under no legal theory, whether tort (including | |||
negligence), contract, or otherwise, shall any Contributor, or anyone who | |||
distributes Covered Software as permitted above, be liable to You for any | |||
direct, indirect, special, incidental, or consequential damages of any | |||
character including, without limitation, damages for lost profits, loss of | |||
goodwill, work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses, even if such party shall have been | |||
informed of the possibility of such damages. This limitation of liability | |||
shall not apply to liability for death or personal injury resulting from | |||
such party's negligence to the extent applicable law prohibits such | |||
limitation. Some jurisdictions do not allow the exclusion or limitation of | |||
incidental or consequential damages, so this exclusion and limitation may | |||
not apply to You. | |||
8. Litigation | |||
Any litigation relating to this License may be brought only in the courts | |||
of a jurisdiction where the defendant maintains its principal place of | |||
business and such litigation shall be governed by laws of that | |||
jurisdiction, without reference to its conflict-of-law provisions. Nothing | |||
in this Section shall prevent a party's ability to bring cross-claims or | |||
counter-claims. | |||
9. Miscellaneous | |||
This License represents the complete agreement concerning the subject | |||
matter hereof. If any provision of this License is held to be | |||
unenforceable, such provision shall be reformed only to the extent | |||
necessary to make it enforceable. Any law or regulation which provides that | |||
the language of a contract shall be construed against the drafter shall not | |||
be used to construe this License against a Contributor. | |||
10. Versions of the License | |||
10.1. New Versions | |||
Mozilla Foundation is the license steward. Except as provided in Section | |||
10.3, no one other than the license steward has the right to modify or | |||
publish new versions of this License. Each version will be given a | |||
distinguishing version number. | |||
10.2. Effect of New Versions | |||
You may distribute the Covered Software under the terms of the version | |||
of the License under which You originally received the Covered Software, | |||
or under the terms of any subsequent version published by the license | |||
steward. | |||
10.3. Modified Versions | |||
If you create software not governed by this License, and you want to | |||
create a new license for such software, you may create and use a | |||
modified version of this License if you rename the license and remove | |||
any references to the name of the license steward (except to note that | |||
such modified license differs from this License). | |||
10.4. Distributing Source Code Form that is Incompatible With Secondary | |||
Licenses If You choose to distribute Source Code Form that is | |||
Incompatible With Secondary Licenses under the terms of this version of | |||
the License, the notice described in Exhibit B of this License must be | |||
attached. | |||
Exhibit A - Source Code Form License Notice | |||
This Source Code Form is subject to the | |||
terms of the Mozilla Public License, v. | |||
2.0. If a copy of the MPL was not | |||
distributed with this file, You can | |||
obtain one at | |||
http://mozilla.org/MPL/2.0/. | |||
If it is not possible or desirable to put the notice in a particular file, | |||
then You may include the notice in a location (such as a LICENSE file in a | |||
relevant directory) where a recipient would be likely to look for such a | |||
notice. | |||
You may add additional accurate notices of copyright ownership. | |||
Exhibit B - "Incompatible With Secondary Licenses" Notice | |||
This Source Code Form is "Incompatible | |||
With Secondary Licenses", as defined by | |||
the Mozilla Public License, v. 2.0. | |||
@ -0,0 +1,30 @@ | |||
# cleanhttp | |||
Functions for accessing "clean" Go http.Client values | |||
------------- | |||
The Go standard library contains a default `http.Client` called | |||
`http.DefaultClient`. It is a common idiom in Go code to start with | |||
`http.DefaultClient` and tweak it as necessary, and in fact, this is | |||
encouraged; from the `http` package documentation: | |||
> The Client's Transport typically has internal state (cached TCP connections), | |||
so Clients should be reused instead of created as needed. Clients are safe for | |||
concurrent use by multiple goroutines. | |||
Unfortunately, this is a shared value, and it is not uncommon for libraries to | |||
assume that they are free to modify it at will. With enough dependencies, it | |||
can be very easy to encounter strange problems and race conditions due to | |||
manipulation of this shared value across libraries and goroutines (clients are | |||
safe for concurrent use, but writing values to the client struct itself is not | |||
protected). | |||
Making things worse is the fact that a bare `http.Client` will use a default | |||
`http.Transport` called `http.DefaultTransport`, which is another global value | |||
that behaves the same way. So it is not simply enough to replace | |||
`http.DefaultClient` with `&http.Client{}`. | |||
This repository provides some simple functions to get a "clean" `http.Client` | |||
-- one that uses the same default values as the Go standard library, but | |||
returns a client that does not share any state with other clients. |
@ -0,0 +1,57 @@ | |||
package cleanhttp | |||
import ( | |||
"net" | |||
"net/http" | |||
"runtime" | |||
"time" | |||
) | |||
// DefaultTransport returns a new http.Transport with similar default values to | |||
// http.DefaultTransport, but with idle connections and keepalives disabled. | |||
func DefaultTransport() *http.Transport { | |||
transport := DefaultPooledTransport() | |||
transport.DisableKeepAlives = true | |||
transport.MaxIdleConnsPerHost = -1 | |||
return transport | |||
} | |||
// DefaultPooledTransport returns a new http.Transport with similar default | |||
// values to http.DefaultTransport. Do not use this for transient transports as | |||
// it can leak file descriptors over time. Only use this for transports that | |||
// will be re-used for the same host(s). | |||
func DefaultPooledTransport() *http.Transport { | |||
transport := &http.Transport{ | |||
Proxy: http.ProxyFromEnvironment, | |||
DialContext: (&net.Dialer{ | |||
Timeout: 30 * time.Second, | |||
KeepAlive: 30 * time.Second, | |||
DualStack: true, | |||
}).DialContext, | |||
MaxIdleConns: 100, | |||
IdleConnTimeout: 90 * time.Second, | |||
TLSHandshakeTimeout: 10 * time.Second, | |||
ExpectContinueTimeout: 1 * time.Second, | |||
MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1, | |||
} | |||
return transport | |||
} | |||
// DefaultClient returns a new http.Client with similar default values to | |||
// http.Client, but with a non-shared Transport, idle connections disabled, and | |||
// keepalives disabled. | |||
func DefaultClient() *http.Client { | |||
return &http.Client{ | |||
Transport: DefaultTransport(), | |||
} | |||
} | |||
// DefaultPooledClient returns a new http.Client with similar default values to | |||
// http.Client, but with a shared Transport. Do not use this function for | |||
// transient clients as it can leak file descriptors over time. Only use this | |||
// for clients that will be re-used for the same host(s). | |||
func DefaultPooledClient() *http.Client { | |||
return &http.Client{ | |||
Transport: DefaultPooledTransport(), | |||
} | |||
} |
@ -0,0 +1,20 @@ | |||
// Package cleanhttp offers convenience utilities for acquiring "clean" | |||
// http.Transport and http.Client structs. | |||
// | |||
// Values set on http.DefaultClient and http.DefaultTransport affect all | |||
// callers. This can have detrimental effects, esepcially in TLS contexts, | |||
// where client or root certificates set to talk to multiple endpoints can end | |||
// up displacing each other, leading to hard-to-debug issues. This package | |||
// provides non-shared http.Client and http.Transport structs to ensure that | |||
// the configuration will not be overwritten by other parts of the application | |||
// or dependencies. | |||
// | |||
// The DefaultClient and DefaultTransport functions disable idle connections | |||
// and keepalives. Without ensuring that idle connections are closed before | |||
// garbage collection, short-term clients/transports can leak file descriptors, | |||
// eventually leading to "too many open files" errors. If you will be | |||
// connecting to the same hosts repeatedly from the same client, you can use | |||
// DefaultPooledClient to receive a client that has connection pooling | |||
// semantics similar to http.DefaultClient. | |||
// | |||
package cleanhttp |
@ -0,0 +1 @@ | |||
module github.com/hashicorp/go-cleanhttp |
@ -0,0 +1,48 @@ | |||
package cleanhttp | |||
import ( | |||
"net/http" | |||
"strings" | |||
"unicode" | |||
) | |||
// HandlerInput provides input options to cleanhttp's handlers | |||
type HandlerInput struct { | |||
ErrStatus int | |||
} | |||
// PrintablePathCheckHandler is a middleware that ensures the request path | |||
// contains only printable runes. | |||
func PrintablePathCheckHandler(next http.Handler, input *HandlerInput) http.Handler { | |||
// Nil-check on input to make it optional | |||
if input == nil { | |||
input = &HandlerInput{ | |||
ErrStatus: http.StatusBadRequest, | |||
} | |||
} | |||
// Default to http.StatusBadRequest on error | |||
if input.ErrStatus == 0 { | |||
input.ErrStatus = http.StatusBadRequest | |||
} | |||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |||
if r != nil { | |||
// Check URL path for non-printable characters | |||
idx := strings.IndexFunc(r.URL.Path, func(c rune) bool { | |||
return !unicode.IsPrint(c) | |||
}) | |||
if idx != -1 { | |||
w.WriteHeader(input.ErrStatus) | |||
return | |||
} | |||
if next != nil { | |||
next.ServeHTTP(w, r) | |||
} | |||
} | |||
return | |||
}) | |||
} |
@ -0,0 +1,4 @@ | |||
.idea/ | |||
*.iml | |||
*.test | |||
.vscode/ |
@ -0,0 +1,12 @@ | |||
sudo: false | |||
language: go | |||
go: | |||
- 1.12.4 | |||
branches: | |||
only: | |||
- master | |||
script: make updatedeps test |
@ -0,0 +1,363 @@ | |||
Mozilla Public License, version 2.0 | |||
1. Definitions | |||
1.1. "Contributor" | |||
means each individual or legal entity that creates, contributes to the | |||
creation of, or owns Covered Software. | |||
1.2. "Contributor Version" | |||
means the combination of the Contributions of others (if any) used by a | |||
Contributor and that particular Contributor's Contribution. | |||
1.3. "Contribution" | |||
means Covered Software of a particular Contributor. | |||
1.4. "Covered Software" | |||
means Source Code Form to which the initial Contributor has attached the | |||
notice in Exhibit A, the Executable Form of such Source Code Form, and | |||
Modifications of such Source Code Form, in each case including portions | |||
thereof. | |||
1.5. "Incompatible With Secondary Licenses" | |||
means | |||
a. that the initial Contributor has attached the notice described in | |||
Exhibit B to the Covered Software; or | |||
b. that the Covered Software was made available under the terms of | |||
version 1.1 or earlier of the License, but not also under the terms of | |||
a Secondary License. | |||
1.6. "Executable Form" | |||
means any form of the work other than Source Code Form. | |||
1.7. "Larger Work" | |||
means a work that combines Covered Software with other material, in a | |||
separate file or files, that is not Covered Software. | |||
1.8. "License" | |||
means this document. | |||
1.9. "Licensable" | |||
means having the right to grant, to the maximum extent possible, whether | |||
at the time of the initial grant or subsequently, any and all of the | |||
rights conveyed by this License. | |||
1.10. "Modifications" | |||
means any of the following: | |||
a. any file in Source Code Form that results from an addition to, | |||
deletion from, or modification of the contents of Covered Software; or | |||
b. any new file in Source Code Form that contains any Covered Software. | |||
1.11. "Patent Claims" of a Contributor | |||
means any patent claim(s), including without limitation, method, | |||
process, and apparatus claims, in any patent Licensable by such | |||
Contributor that would be infringed, but for the grant of the License, | |||
by the making, using, selling, offering for sale, having made, import, | |||
or transfer of either its Contributions or its Contributor Version. | |||
1.12. "Secondary License" | |||
means either the GNU General Public License, Version 2.0, the GNU Lesser | |||
General Public License, Version 2.1, the GNU Affero General Public | |||
License, Version 3.0, or any later versions of those licenses. | |||
1.13. "Source Code Form" | |||
means the form of the work preferred for making modifications. | |||
1.14. "You" (or "Your") | |||
means an individual or a legal entity exercising rights under this | |||
License. For legal entities, "You" includes any entity that controls, is | |||
controlled by, or is under common control with You. For purposes of this | |||
definition, "control" means (a) the power, direct or indirect, to cause | |||
the direction or management of such entity, whether by contract or | |||
otherwise, or (b) ownership of more than fifty percent (50%) of the | |||
outstanding shares or beneficial ownership of such entity. | |||
2. License Grants and Conditions | |||
2.1. Grants | |||
Each Contributor hereby grants You a world-wide, royalty-free, | |||
non-exclusive license: | |||
a. under intellectual property rights (other than patent or trademark) | |||
Licensable by such Contributor to use, reproduce, make available, | |||
modify, display, perform, distribute, and otherwise exploit its | |||
Contributions, either on an unmodified basis, with Modifications, or | |||
as part of a Larger Work; and | |||
b. under Patent Claims of such Contributor to make, use, sell, offer for | |||
sale, have made, import, and otherwise transfer either its | |||
Contributions or its Contributor Version. | |||
2.2. Effective Date | |||
The licenses granted in Section 2.1 with respect to any Contribution | |||
become effective for each Contribution on the date the Contributor first | |||
distributes such Contribution. | |||
2.3. Limitations on Grant Scope | |||
The licenses granted in this Section 2 are the only rights granted under | |||
this License. No additional rights or licenses will be implied from the | |||
distribution or licensing of Covered Software under this License. | |||
Notwithstanding Section 2.1(b) above, no patent license is granted by a | |||
Contributor: | |||
a. for any code that a Contributor has removed from Covered Software; or | |||
b. for infringements caused by: (i) Your and any other third party's | |||
modifications of Covered Software, or (ii) the combination of its | |||
Contributions with other software (except as part of its Contributor | |||
Version); or | |||
c. under Patent Claims infringed by Covered Software in the absence of | |||
its Contributions. | |||
This License does not grant any rights in the trademarks, service marks, | |||
or logos of any Contributor (except as may be necessary to comply with | |||
the notice requirements in Section 3.4). | |||
2.4. Subsequent Licenses | |||
No Contributor makes additional grants as a result of Your choice to | |||
distribute the Covered Software under a subsequent version of this | |||
License (see Section 10.2) or under the terms of a Secondary License (if | |||
permitted under the terms of Section 3.3). | |||
2.5. Representation | |||
Each Contributor represents that the Contributor believes its | |||
Contributions are its original creation(s) or it has sufficient rights to | |||
grant the rights to its Contributions conveyed by this License. | |||
2.6. Fair Use | |||
This License is not intended to limit any rights You have under | |||
applicable copyright doctrines of fair use, fair dealing, or other | |||
equivalents. | |||
2.7. Conditions | |||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | |||
Section 2.1. | |||
3. Responsibilities | |||
3.1. Distribution of Source Form | |||
All distribution of Covered Software in Source Code Form, including any | |||
Modifications that You create or to which You contribute, must be under | |||
the terms of this License. You must inform recipients that the Source | |||
Code Form of the Covered Software is governed by the terms of this | |||
License, and how they can obtain a copy of this License. You may not | |||
attempt to alter or restrict the recipients' rights in the Source Code | |||
Form. | |||
3.2. Distribution of Executable Form | |||
If You distribute Covered Software in Executable Form then: | |||
a. such Covered Software must also be made available in Source Code Form, | |||
as described in Section 3.1, and You must inform recipients of the | |||
Executable Form how they can obtain a copy of such Source Code Form by | |||
reasonable means in a timely manner, at a charge no more than the cost | |||
of distribution to the recipient; and | |||
b. You may distribute such Executable Form under the terms of this | |||
License, or sublicense it under different terms, provided that the | |||
license for the Executable Form does not attempt to limit or alter the | |||
recipients' rights in the Source Code Form under this License. | |||
3.3. Distribution of a Larger Work | |||
You may create and distribute a Larger Work under terms of Your choice, | |||
provided that You also comply with the requirements of this License for | |||
the Covered Software. If the Larger Work is a combination of Covered | |||
Software with a work governed by one or more Secondary Licenses, and the | |||
Covered Software is not Incompatible With Secondary Licenses, this | |||
License permits You to additionally distribute such Covered Software | |||
under the terms of such Secondary License(s), so that the recipient of | |||
the Larger Work may, at their option, further distribute the Covered | |||
Software under the terms of either this License or such Secondary | |||
License(s). | |||
3.4. Notices | |||
You may not remove or alter the substance of any license notices | |||
(including copyright notices, patent notices, disclaimers of warranty, or | |||
limitations of liability) contained within the Source Code Form of the | |||
Covered Software, except that You may alter any license notices to the | |||
extent required to remedy known factual inaccuracies. | |||
3.5. Application of Additional Terms | |||
You may choose to offer, and to charge a fee for, warranty, support, | |||
indemnity or liability obligations to one or more recipients of Covered | |||
Software. However, You may do so only on Your own behalf, and not on | |||
behalf of any Contributor. You must make it absolutely clear that any | |||
such warranty, support, indemnity, or liability obligation is offered by | |||
You alone, and You hereby agree to indemnify every Contributor for any | |||
liability incurred by such Contributor as a result of warranty, support, | |||
indemnity or liability terms You offer. You may include additional | |||
disclaimers of warranty and limitations of liability specific to any | |||
jurisdiction. | |||
4. Inability to Comply Due to Statute or Regulation | |||
If it is impossible for You to comply with any of the terms of this License | |||
with respect to some or all of the Covered Software due to statute, | |||
judicial order, or regulation then You must: (a) comply with the terms of | |||
this License to the maximum extent possible; and (b) describe the | |||
limitations and the code they affect. Such description must be placed in a | |||
text file included with all distributions of the Covered Software under | |||
this License. Except to the extent prohibited by statute or regulation, | |||
such description must be sufficiently detailed for a recipient of ordinary | |||
skill to be able to understand it. | |||
5. Termination | |||
5.1. The rights granted under this License will terminate automatically if You | |||
fail to comply with any of its terms. However, if You become compliant, | |||
then the rights granted under this License from a particular Contributor | |||
are reinstated (a) provisionally, unless and until such Contributor | |||
explicitly and finally terminates Your grants, and (b) on an ongoing | |||
basis, if such Contributor fails to notify You of the non-compliance by | |||
some reasonable means prior to 60 days after You have come back into | |||
compliance. Moreover, Your grants from a particular Contributor are | |||
reinstated on an ongoing basis if such Contributor notifies You of the | |||
non-compliance by some reasonable means, this is the first time You have | |||
received notice of non-compliance with this License from such | |||
Contributor, and You become compliant prior to 30 days after Your receipt | |||
of the notice. | |||
5.2. If You initiate litigation against any entity by asserting a patent | |||
infringement claim (excluding declaratory judgment actions, | |||
counter-claims, and cross-claims) alleging that a Contributor Version | |||
directly or indirectly infringes any patent, then the rights granted to | |||
You by any and all Contributors for the Covered Software under Section | |||
2.1 of this License shall terminate. | |||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user | |||
license agreements (excluding distributors and resellers) which have been | |||
validly granted by You or Your distributors under this License prior to | |||
termination shall survive termination. | |||
6. Disclaimer of Warranty | |||
Covered Software is provided under this License on an "as is" basis, | |||
without warranty of any kind, either expressed, implied, or statutory, | |||
including, without limitation, warranties that the Covered Software is free | |||
of defects, merchantable, fit for a particular purpose or non-infringing. | |||
The entire risk as to the quality and performance of the Covered Software | |||
is with You. Should any Covered Software prove defective in any respect, | |||
You (not any Contributor) assume the cost of any necessary servicing, | |||
repair, or correction. This disclaimer of warranty constitutes an essential | |||
part of this License. No use of any Covered Software is authorized under | |||
this License except under this disclaimer. | |||
7. Limitation of Liability | |||
Under no circumstances and under no legal theory, whether tort (including | |||
negligence), contract, or otherwise, shall any Contributor, or anyone who | |||
distributes Covered Software as permitted above, be liable to You for any | |||
direct, indirect, special, incidental, or consequential damages of any | |||
character including, without limitation, damages for lost profits, loss of | |||
goodwill, work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses, even if such party shall have been | |||
informed of the possibility of such damages. This limitation of liability | |||
shall not apply to liability for death or personal injury resulting from | |||
such party's negligence to the extent applicable law prohibits such | |||
limitation. Some jurisdictions do not allow the exclusion or limitation of | |||
incidental or consequential damages, so this exclusion and limitation may | |||
not apply to You. | |||
8. Litigation | |||
Any litigation relating to this License may be brought only in the courts | |||
of a jurisdiction where the defendant maintains its principal place of | |||
business and such litigation shall be governed by laws of that | |||
jurisdiction, without reference to its conflict-of-law provisions. Nothing | |||
in this Section shall prevent a party's ability to bring cross-claims or | |||
counter-claims. | |||
9. Miscellaneous | |||
This License represents the complete agreement concerning the subject | |||
matter hereof. If any provision of this License is held to be | |||
unenforceable, such provision shall be reformed only to the extent | |||
necessary to make it enforceable. Any law or regulation which provides that | |||
the language of a contract shall be construed against the drafter shall not | |||
be used to construe this License against a Contributor. | |||
10. Versions of the License | |||
10.1. New Versions | |||
Mozilla Foundation is the license steward. Except as provided in Section | |||
10.3, no one other than the license steward has the right to modify or | |||
publish new versions of this License. Each version will be given a | |||
distinguishing version number. | |||
10.2. Effect of New Versions | |||
You may distribute the Covered Software under the terms of the version | |||
of the License under which You originally received the Covered Software, | |||
or under the terms of any subsequent version published by the license | |||
steward. | |||
10.3. Modified Versions | |||
If you create software not governed by this License, and you want to | |||
create a new license for such software, you may create and use a | |||
modified version of this License if you rename the license and remove | |||
any references to the name of the license steward (except to note that | |||
such modified license differs from this License). | |||
10.4. Distributing Source Code Form that is Incompatible With Secondary | |||
Licenses If You choose to distribute Source Code Form that is | |||
Incompatible With Secondary Licenses under the terms of this version of | |||
the License, the notice described in Exhibit B of this License must be | |||
attached. | |||
Exhibit A - Source Code Form License Notice | |||
This Source Code Form is subject to the | |||
terms of the Mozilla Public License, v. | |||
2.0. If a copy of the MPL was not | |||
distributed with this file, You can | |||
obtain one at | |||
http://mozilla.org/MPL/2.0/. | |||
If it is not possible or desirable to put the notice in a particular file, | |||
then You may include the notice in a location (such as a LICENSE file in a | |||
relevant directory) where a recipient would be likely to look for such a | |||
notice. | |||
You may add additional accurate notices of copyright ownership. | |||
Exhibit B - "Incompatible With Secondary Licenses" Notice | |||
This Source Code Form is "Incompatible | |||
With Secondary Licenses", as defined by | |||
the Mozilla Public License, v. 2.0. | |||
@ -0,0 +1,11 @@ | |||
default: test | |||
test: | |||
go vet ./... | |||
go test -race ./... | |||
updatedeps: | |||
go get -f -t -u ./... | |||
go get -f -u ./... | |||
.PHONY: default test updatedeps |
@ -0,0 +1,61 @@ | |||
go-retryablehttp | |||
================ | |||
[![Build Status](http://img.shields.io/travis/hashicorp/go-retryablehttp.svg?style=flat-square)][travis] | |||
[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs] | |||
[travis]: http://travis-ci.org/hashicorp/go-retryablehttp | |||
[godocs]: http://godoc.org/github.com/hashicorp/go-retryablehttp | |||
The `retryablehttp` package provides a familiar HTTP client interface with | |||
automatic retries and exponential backoff. It is a thin wrapper over the | |||
standard `net/http` client library and exposes nearly the same public API. This | |||
makes `retryablehttp` very easy to drop into existing programs. | |||
`retryablehttp` performs automatic retries under certain conditions. Mainly, if | |||
an error is returned by the client (connection errors, etc.), or if a 500-range | |||
response code is received (except 501), then a retry is invoked after a wait | |||
period. Otherwise, the response is returned and left to the caller to | |||
interpret. | |||
The main difference from `net/http` is that requests which take a request body | |||
(POST/PUT et. al) can have the body provided in a number of ways (some more or | |||
less efficient) that allow "rewinding" the request body if the initial request | |||
fails so that the full request can be attempted again. See the | |||
[godoc](http://godoc.org/github.com/hashicorp/go-retryablehttp) for more | |||
details. | |||
Version 0.6.0 and before are compatible with Go prior to 1.12. From 0.6.1 onward, Go 1.12+ is required. | |||
Example Use | |||
=========== | |||
Using this library should look almost identical to what you would do with | |||
`net/http`. The most simple example of a GET request is shown below: | |||
```go | |||
resp, err := retryablehttp.Get("/foo") | |||
if err != nil { | |||
panic(err) | |||
} | |||
``` | |||
The returned response object is an `*http.Response`, the same thing you would | |||
usually get from `net/http`. Had the request failed one or more times, the above | |||
call would block and retry with exponential backoff. | |||
## Getting a stdlib `*http.Client` with retries | |||
It's possible to convert a `*retryablehttp.Client` directly to a `*http.Client`. | |||
This makes use of retryablehttp broadly applicable with minimal effort. Simply | |||
configure a `*retryablehttp.Client` as you wish, and then call `StandardClient()`: | |||
```go | |||
retryClient := retryablehttp.NewClient() | |||
retryClient.RetryMax = 10 | |||
standardClient := retryClient.StandardClient() // *http.Client | |||
``` | |||
For more usage and examples see the | |||
[godoc](http://godoc.org/github.com/hashicorp/go-retryablehttp). |
@ -0,0 +1,705 @@ | |||
// Package retryablehttp provides a familiar HTTP client interface with | |||
// automatic retries and exponential backoff. It is a thin wrapper over the | |||
// standard net/http client library and exposes nearly the same public API. | |||
// This makes retryablehttp very easy to drop into existing programs. | |||
// | |||
// retryablehttp performs automatic retries under certain conditions. Mainly, if | |||
// an error is returned by the client (connection errors etc), or if a 500-range | |||
// response is received, then a retry is invoked. Otherwise, the response is | |||
// returned and left to the caller to interpret. | |||
// | |||
// Requests which take a request body should provide a non-nil function | |||
// parameter. The best choice is to provide either a function satisfying | |||
// ReaderFunc which provides multiple io.Readers in an efficient manner, a | |||
// *bytes.Buffer (the underlying raw byte slice will be used) or a raw byte | |||
// slice. As it is a reference type, and we will wrap it as needed by readers, | |||
// we can efficiently re-use the request body without needing to copy it. If an | |||
// io.Reader (such as a *bytes.Reader) is provided, the full body will be read | |||
// prior to the first request, and will be efficiently re-used for any retries. | |||
// ReadSeeker can be used, but some users have observed occasional data races | |||
// between the net/http library and the Seek functionality of some | |||
// implementations of ReadSeeker, so should be avoided if possible. | |||
package retryablehttp | |||
import ( | |||
"bytes" | |||
"context" | |||
"crypto/x509" | |||
"fmt" | |||
"io" | |||
"io/ioutil" | |||
"log" | |||
"math" | |||
"math/rand" | |||
"net/http" | |||
"net/url" | |||
"os" | |||
"regexp" | |||
"strings" | |||
"sync" | |||
"time" | |||
"github.com/hashicorp/go-cleanhttp" | |||
) | |||
var ( | |||
// Default retry configuration | |||
defaultRetryWaitMin = 1 * time.Second | |||
defaultRetryWaitMax = 30 * time.Second | |||
defaultRetryMax = 4 | |||
// defaultLogger is the logger provided with defaultClient | |||
defaultLogger = log.New(os.Stderr, "", log.LstdFlags) | |||
// defaultClient is used for performing requests without explicitly making | |||
// a new client. It is purposely private to avoid modifications. | |||
defaultClient = NewClient() | |||
// We need to consume response bodies to maintain http connections, but | |||
// limit the size we consume to respReadLimit. | |||
respReadLimit = int64(4096) | |||
// A regular expression to match the error returned by net/http when the | |||
// configured number of redirects is exhausted. This error isn't typed | |||
// specifically so we resort to matching on the error string. | |||
redirectsErrorRe = regexp.MustCompile(`stopped after \d+ redirects\z`) | |||
// A regular expression to match the error returned by net/http when the | |||
// scheme specified in the URL is invalid. This error isn't typed | |||
// specifically so we resort to matching on the error string. | |||
schemeErrorRe = regexp.MustCompile(`unsupported protocol scheme`) | |||
) | |||
// ReaderFunc is the type of function that can be given natively to NewRequest | |||
type ReaderFunc func() (io.Reader, error) | |||
// LenReader is an interface implemented by many in-memory io.Reader's. Used | |||
// for automatically sending the right Content-Length header when possible. | |||
type LenReader interface { | |||
Len() int | |||
} | |||
// Request wraps the metadata needed to create HTTP requests. | |||
type Request struct { | |||
// body is a seekable reader over the request body payload. This is | |||
// used to rewind the request data in between retries. | |||
body ReaderFunc | |||
// Embed an HTTP request directly. This makes a *Request act exactly | |||
// like an *http.Request so that all meta methods are supported. | |||
*http.Request | |||
} | |||
// WithContext returns wrapped Request with a shallow copy of underlying *http.Request | |||
// with its context changed to ctx. The provided ctx must be non-nil. | |||
func (r *Request) WithContext(ctx context.Context) *Request { | |||
r.Request = r.Request.WithContext(ctx) | |||
return r | |||
} | |||
// BodyBytes allows accessing the request body. It is an analogue to | |||
// http.Request's Body variable, but it returns a copy of the underlying data | |||
// rather than consuming it. | |||
// | |||
// This function is not thread-safe; do not call it at the same time as another | |||
// call, or at the same time this request is being used with Client.Do. | |||
func (r *Request) BodyBytes() ([]byte, error) { | |||
if r.body == nil { | |||
return nil, nil | |||
} | |||
body, err := r.body() | |||
if err != nil { | |||
return nil, err | |||
} | |||
buf := new(bytes.Buffer) | |||
_, err = buf.ReadFrom(body) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return buf.Bytes(), nil | |||
} | |||
// SetBody allows setting the request body. | |||
// | |||
// It is useful if a new body needs to be set without constructing a new Request. | |||
func (r *Request) SetBody(rawBody interface{}) error { | |||
bodyReader, contentLength, err := getBodyReaderAndContentLength(rawBody) | |||
if err != nil { | |||
return err | |||
} | |||
r.body = bodyReader | |||
r.ContentLength = contentLength | |||
return nil | |||
} | |||
// WriteTo allows copying the request body into a writer. | |||
// | |||
// It writes data to w until there's no more data to write or | |||
// when an error occurs. The return int64 value is the number of bytes | |||
// written. Any error encountered during the write is also returned. | |||
// The signature matches io.WriterTo interface. | |||
func (r *Request) WriteTo(w io.Writer) (int64, error) { | |||
body, err := r.body() | |||
if err != nil { | |||
return 0, err | |||
} | |||
if c, ok := body.(io.Closer); ok { | |||
defer c.Close() | |||
} | |||
return io.Copy(w, body) | |||
} | |||
func getBodyReaderAndContentLength(rawBody interface{}) (ReaderFunc, int64, error) { | |||
var bodyReader ReaderFunc | |||
var contentLength int64 | |||
switch body := rawBody.(type) { | |||
// If they gave us a function already, great! Use it. | |||
case ReaderFunc: | |||
bodyReader = body | |||
tmp, err := body() | |||
if err != nil { | |||
return nil, 0, err | |||
} | |||
if lr, ok := tmp.(LenReader); ok { | |||
contentLength = int64(lr.Len()) | |||
} | |||
if c, ok := tmp.(io.Closer); ok { | |||
c.Close() | |||
} | |||
case func() (io.Reader, error): | |||
bodyReader = body | |||
tmp, err := body() | |||
if err != nil { | |||
return nil, 0, err | |||
} | |||
if lr, ok := tmp.(LenReader); ok { | |||
contentLength = int64(lr.Len()) | |||
} | |||
if c, ok := tmp.(io.Closer); ok { | |||
c.Close() | |||
} | |||
// If a regular byte slice, we can read it over and over via new | |||
// readers | |||
case []byte: | |||
buf := body | |||
bodyReader = func() (io.Reader, error) { | |||
return bytes.NewReader(buf), nil | |||
} | |||
contentLength = int64(len(buf)) | |||
// If a bytes.Buffer we can read the underlying byte slice over and | |||
// over | |||
case *bytes.Buffer: | |||
buf := body | |||
bodyReader = func() (io.Reader, error) { | |||
return bytes.NewReader(buf.Bytes()), nil | |||
} | |||
contentLength = int64(buf.Len()) | |||
// We prioritize *bytes.Reader here because we don't really want to | |||
// deal with it seeking so want it to match here instead of the | |||
// io.ReadSeeker case. | |||
case *bytes.Reader: | |||
buf, err := ioutil.ReadAll(body) | |||
if err != nil { | |||
return nil, 0, err | |||
} | |||
bodyReader = func() (io.Reader, error) { | |||
return bytes.NewReader(buf), nil | |||
} | |||
contentLength = int64(len(buf)) | |||
// Compat case | |||
case io.ReadSeeker: | |||
raw := body | |||
bodyReader = func() (io.Reader, error) { | |||
_, err := raw.Seek(0, 0) | |||
return ioutil.NopCloser(raw), err | |||
} | |||
if lr, ok := raw.(LenReader); ok { | |||
contentLength = int64(lr.Len()) | |||
} | |||
// Read all in so we can reset | |||
case io.Reader: | |||
buf, err := ioutil.ReadAll(body) | |||
if err != nil { | |||
return nil, 0, err | |||
} | |||
bodyReader = func() (io.Reader, error) { | |||
return bytes.NewReader(buf), nil | |||
} | |||
contentLength = int64(len(buf)) | |||
// No body provided, nothing to do | |||
case nil: | |||
// Unrecognized type | |||
default: | |||
return nil, 0, fmt.Errorf("cannot handle type %T", rawBody) | |||
} | |||
return bodyReader, contentLength, nil | |||
} | |||
// FromRequest wraps an http.Request in a retryablehttp.Request | |||
func FromRequest(r *http.Request) (*Request, error) { | |||
bodyReader, _, err := getBodyReaderAndContentLength(r.Body) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// Could assert contentLength == r.ContentLength | |||
return &Request{bodyReader, r}, nil | |||
} | |||
// NewRequest creates a new wrapped request. | |||
func NewRequest(method, url string, rawBody interface{}) (*Request, error) { | |||
bodyReader, contentLength, err := getBodyReaderAndContentLength(rawBody) | |||
if err != nil { | |||
return nil, err | |||
} | |||
httpReq, err := http.NewRequest(method, url, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
httpReq.ContentLength = contentLength | |||
return &Request{bodyReader, httpReq}, nil | |||
} | |||
// Logger interface allows to use other loggers than | |||
// standard log.Logger. | |||
type Logger interface { | |||
Printf(string, ...interface{}) | |||
} | |||
// LeveledLogger interface implements the basic methods that a logger library needs | |||
type LeveledLogger interface { | |||
Error(string, ...interface{}) | |||
Info(string, ...interface{}) | |||
Debug(string, ...interface{}) | |||
Warn(string, ...interface{}) | |||
} | |||
// hookLogger adapts an LeveledLogger to Logger for use by the existing hook functions | |||
// without changing the API. | |||
type hookLogger struct { | |||
LeveledLogger | |||
} | |||
func (h hookLogger) Printf(s string, args ...interface{}) { | |||
h.Info(fmt.Sprintf(s, args...)) | |||
} | |||
// RequestLogHook allows a function to run before each retry. The HTTP | |||
// request which will be made, and the retry number (0 for the initial | |||
// request) are available to users. The internal logger is exposed to | |||
// consumers. | |||
type RequestLogHook func(Logger, *http.Request, int) | |||
// ResponseLogHook is like RequestLogHook, but allows running a function | |||
// on each HTTP response. This function will be invoked at the end of | |||
// every HTTP request executed, regardless of whether a subsequent retry | |||
// needs to be performed or not. If the response body is read or closed | |||
// from this method, this will affect the response returned from Do(). | |||
type ResponseLogHook func(Logger, *http.Response) | |||
// CheckRetry specifies a policy for handling retries. It is called | |||
// following each request with the response and error values returned by | |||
// the http.Client. If CheckRetry returns false, the Client stops retrying | |||
// and returns the response to the caller. If CheckRetry returns an error, | |||
// that error value is returned in lieu of the error from the request. The | |||
// Client will close any response body when retrying, but if the retry is | |||
// aborted it is up to the CheckRetry callback to properly close any | |||
// response body before returning. | |||
type CheckRetry func(ctx context.Context, resp *http.Response, err error) (bool, error) | |||
// Backoff specifies a policy for how long to wait between retries. | |||
// It is called after a failing request to determine the amount of time | |||
// that should pass before trying again. | |||
type Backoff func(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration | |||
// ErrorHandler is called if retries are expired, containing the last status | |||
// from the http library. If not specified, default behavior for the library is | |||
// to close the body and return an error indicating how many tries were | |||
// attempted. If overriding this, be sure to close the body if needed. | |||
type ErrorHandler func(resp *http.Response, err error, numTries int) (*http.Response, error) | |||
// Client is used to make HTTP requests. It adds additional functionality | |||
// like automatic retries to tolerate minor outages. | |||
type Client struct { | |||
HTTPClient *http.Client // Internal HTTP client. | |||
Logger interface{} // Customer logger instance. Can be either Logger or LeveledLogger | |||
RetryWaitMin time.Duration // Minimum time to wait | |||
RetryWaitMax time.Duration // Maximum time to wait | |||
RetryMax int // Maximum number of retries | |||
// RequestLogHook allows a user-supplied function to be called | |||
// before each retry. | |||
RequestLogHook RequestLogHook | |||
// ResponseLogHook allows a user-supplied function to be called | |||
// with the response from each HTTP request executed. | |||
ResponseLogHook ResponseLogHook | |||
// CheckRetry specifies the policy for handling retries, and is called | |||
// after each request. The default policy is DefaultRetryPolicy. | |||
CheckRetry CheckRetry | |||
// Backoff specifies the policy for how long to wait between retries | |||
Backoff Backoff | |||
// ErrorHandler specifies the custom error handler to use, if any | |||
ErrorHandler ErrorHandler | |||
loggerInit sync.Once | |||
} | |||
// NewClient creates a new Client with default settings. | |||
func NewClient() *Client { | |||
return &Client{ | |||
HTTPClient: cleanhttp.DefaultPooledClient(), | |||
Logger: defaultLogger, | |||
RetryWaitMin: defaultRetryWaitMin, | |||
RetryWaitMax: defaultRetryWaitMax, | |||
RetryMax: defaultRetryMax, | |||
CheckRetry: DefaultRetryPolicy, | |||
Backoff: DefaultBackoff, | |||
} | |||
} | |||
func (c *Client) logger() interface{} { | |||
c.loggerInit.Do(func() { | |||
if c.Logger == nil { | |||
return | |||
} | |||
switch c.Logger.(type) { | |||
case Logger, LeveledLogger: | |||
// ok | |||
default: | |||
// This should happen in dev when they are setting Logger and work on code, not in prod. | |||
panic(fmt.Sprintf("invalid logger type passed, must be Logger or LeveledLogger, was %T", c.Logger)) | |||
} | |||
}) | |||
return c.Logger | |||
} | |||
// DefaultRetryPolicy provides a default callback for Client.CheckRetry, which | |||
// will retry on connection errors and server errors. | |||
func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) { | |||
// do not retry on context.Canceled or context.DeadlineExceeded | |||
if ctx.Err() != nil { | |||
return false, ctx.Err() | |||
} | |||
if err != nil { | |||
if v, ok := err.(*url.Error); ok { | |||
// Don't retry if the error was due to too many redirects. | |||
if redirectsErrorRe.MatchString(v.Error()) { | |||
return false, nil | |||
} | |||
// Don't retry if the error was due to an invalid protocol scheme. | |||
if schemeErrorRe.MatchString(v.Error()) { | |||
return false, nil | |||
} | |||
// Don't retry if the error was due to TLS cert verification failure. | |||
if _, ok := v.Err.(x509.UnknownAuthorityError); ok { | |||
return false, nil | |||
} | |||
} | |||
// The error is likely recoverable so retry. | |||
return true, nil | |||
} | |||
// Check the response code. We retry on 500-range responses to allow | |||
// the server time to recover, as 500's are typically not permanent | |||
// errors and may relate to outages on the server side. This will catch | |||
// invalid response codes as well, like 0 and 999. | |||
if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != 501) { | |||
return true, nil | |||
} | |||
return false, nil | |||
} | |||
// DefaultBackoff provides a default callback for Client.Backoff which | |||
// will perform exponential backoff based on the attempt number and limited | |||
// by the provided minimum and maximum durations. | |||
func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration { | |||
mult := math.Pow(2, float64(attemptNum)) * float64(min) | |||
sleep := time.Duration(mult) | |||
if float64(sleep) != mult || sleep > max { | |||
sleep = max | |||
} | |||
return sleep | |||
} | |||
// LinearJitterBackoff provides a callback for Client.Backoff which will | |||
// perform linear backoff based on the attempt number and with jitter to | |||
// prevent a thundering herd. | |||
// | |||
// min and max here are *not* absolute values. The number to be multiplied by | |||
// the attempt number will be chosen at random from between them, thus they are | |||
// bounding the jitter. | |||
// | |||
// For instance: | |||
// * To get strictly linear backoff of one second increasing each retry, set | |||
// both to one second (1s, 2s, 3s, 4s, ...) | |||
// * To get a small amount of jitter centered around one second increasing each | |||
// retry, set to around one second, such as a min of 800ms and max of 1200ms | |||
// (892ms, 2102ms, 2945ms, 4312ms, ...) | |||
// * To get extreme jitter, set to a very wide spread, such as a min of 100ms | |||
// and a max of 20s (15382ms, 292ms, 51321ms, 35234ms, ...) | |||
func LinearJitterBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration { | |||
// attemptNum always starts at zero but we want to start at 1 for multiplication | |||
attemptNum++ | |||
if max <= min { | |||
// Unclear what to do here, or they are the same, so return min * | |||
// attemptNum | |||
return min * time.Duration(attemptNum) | |||
} | |||
// Seed rand; doing this every time is fine | |||
rand := rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) | |||
// Pick a random number that lies somewhere between the min and max and | |||
// multiply by the attemptNum. attemptNum starts at zero so we always | |||
// increment here. We first get a random percentage, then apply that to the | |||
// difference between min and max, and add to min. | |||
jitter := rand.Float64() * float64(max-min) | |||
jitterMin := int64(jitter) + int64(min) | |||
return time.Duration(jitterMin * int64(attemptNum)) | |||
} | |||
// PassthroughErrorHandler is an ErrorHandler that directly passes through the | |||
// values from the net/http library for the final request. The body is not | |||
// closed. | |||
func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Response, error) { | |||
return resp, err | |||
} | |||
// Do wraps calling an HTTP method with retries. | |||
func (c *Client) Do(req *Request) (*http.Response, error) { | |||
if c.HTTPClient == nil { | |||
c.HTTPClient = cleanhttp.DefaultPooledClient() | |||
} | |||
logger := c.logger() | |||
if logger != nil { | |||
switch v := logger.(type) { | |||
case Logger: | |||
v.Printf("[DEBUG] %s %s", req.Method, req.URL) | |||
case LeveledLogger: | |||
v.Debug("performing request", "method", req.Method, "url", req.URL) | |||
} | |||
} | |||
var resp *http.Response | |||
var err error | |||
for i := 0; ; i++ { | |||
var code int // HTTP response code | |||
// Always rewind the request body when non-nil. | |||
if req.body != nil { | |||
body, err := req.body() | |||
if err != nil { | |||
c.HTTPClient.CloseIdleConnections() | |||
return resp, err | |||
} | |||
if c, ok := body.(io.ReadCloser); ok { | |||
req.Body = c | |||
} else { | |||
req.Body = ioutil.NopCloser(body) | |||
} | |||
} | |||
if c.RequestLogHook != nil { | |||
switch v := logger.(type) { | |||
case Logger: | |||
c.RequestLogHook(v, req.Request, i) | |||
case LeveledLogger: | |||
c.RequestLogHook(hookLogger{v}, req.Request, i) | |||
default: | |||
c.RequestLogHook(nil, req.Request, i) | |||
} | |||
} | |||
// Attempt the request | |||
resp, err = c.HTTPClient.Do(req.Request) | |||
if resp != nil { | |||
code = resp.StatusCode | |||
} | |||
// Check if we should continue with retries. | |||
checkOK, checkErr := c.CheckRetry(req.Context(), resp, err) | |||
if err != nil { | |||
switch v := logger.(type) { | |||
case Logger: | |||
v.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err) | |||
case LeveledLogger: | |||
v.Error("request failed", "error", err, "method", req.Method, "url", req.URL) | |||
} | |||
} else { | |||
// Call this here to maintain the behavior of logging all requests, | |||
// even if CheckRetry signals to stop. | |||
if c.ResponseLogHook != nil { | |||
// Call the response logger function if provided. | |||
switch v := logger.(type) { | |||
case Logger: | |||
c.ResponseLogHook(v, resp) | |||
case LeveledLogger: | |||
c.ResponseLogHook(hookLogger{v}, resp) | |||
default: | |||
c.ResponseLogHook(nil, resp) | |||
} | |||
} | |||
} | |||
// Now decide if we should continue. | |||
if !checkOK { | |||
if checkErr != nil { | |||
err = checkErr | |||
} | |||
c.HTTPClient.CloseIdleConnections() | |||
return resp, err | |||
} | |||
// We do this before drainBody because there's no need for the I/O if | |||
// we're breaking out | |||
remain := c.RetryMax - i | |||
if remain <= 0 { | |||
break | |||
} | |||
// We're going to retry, consume any response to reuse the connection. | |||
if err == nil && resp != nil { | |||
c.drainBody(resp.Body) | |||
} | |||
wait := c.Backoff(c.RetryWaitMin, c.RetryWaitMax, i, resp) | |||
desc := fmt.Sprintf("%s %s", req.Method, req.URL) | |||
if code > 0 { | |||
desc = fmt.Sprintf("%s (status: %d)", desc, code) | |||
} | |||
if logger != nil { | |||
switch v := logger.(type) { | |||
case Logger: | |||
v.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain) | |||
case LeveledLogger: | |||
v.Debug("retrying request", "request", desc, "timeout", wait, "remaining", remain) | |||
} | |||
} | |||
select { | |||
case <-req.Context().Done(): | |||
c.HTTPClient.CloseIdleConnections() | |||
return nil, req.Context().Err() | |||
case <-time.After(wait): | |||
} | |||
} | |||
if c.ErrorHandler != nil { | |||
c.HTTPClient.CloseIdleConnections() | |||
return c.ErrorHandler(resp, err, c.RetryMax+1) | |||
} | |||
// By default, we close the response body and return an error without | |||
// returning the response | |||
if resp != nil { | |||
resp.Body.Close() | |||
} | |||
c.HTTPClient.CloseIdleConnections() | |||
return nil, fmt.Errorf("%s %s giving up after %d attempts", | |||
req.Method, req.URL, c.RetryMax+1) | |||
} | |||
// Try to read the response body so we can reuse this connection. | |||
func (c *Client) drainBody(body io.ReadCloser) { | |||
defer body.Close() | |||
_, err := io.Copy(ioutil.Discard, io.LimitReader(body, respReadLimit)) | |||
if err != nil { | |||
if c.logger() != nil { | |||
switch v := c.logger().(type) { | |||
case Logger: | |||
v.Printf("[ERR] error reading response body: %v", err) | |||
case LeveledLogger: | |||
v.Error("error reading response body", "error", err) | |||
} | |||
} | |||
} | |||
} | |||
// Get is a shortcut for doing a GET request without making a new client. | |||
func Get(url string) (*http.Response, error) { | |||
return defaultClient.Get(url) | |||
} | |||
// Get is a convenience helper for doing simple GET requests. | |||
func (c *Client) Get(url string) (*http.Response, error) { | |||
req, err := NewRequest("GET", url, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return c.Do(req) | |||
} | |||
// Head is a shortcut for doing a HEAD request without making a new client. | |||
func Head(url string) (*http.Response, error) { | |||
return defaultClient.Head(url) | |||
} | |||
// Head is a convenience method for doing simple HEAD requests. | |||
func (c *Client) Head(url string) (*http.Response, error) { | |||
req, err := NewRequest("HEAD", url, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return c.Do(req) | |||
} | |||
// Post is a shortcut for doing a POST request without making a new client. | |||
func Post(url, bodyType string, body interface{}) (*http.Response, error) { | |||
return defaultClient.Post(url, bodyType, body) | |||
} | |||
// Post is a convenience method for doing simple POST requests. | |||
func (c *Client) Post(url, bodyType string, body interface{}) (*http.Response, error) { | |||
req, err := NewRequest("POST", url, body) | |||
if err != nil { | |||
return nil, err | |||
} | |||
req.Header.Set("Content-Type", bodyType) | |||
return c.Do(req) | |||
} | |||
// PostForm is a shortcut to perform a POST with form data without creating | |||
// a new client. | |||
func PostForm(url string, data url.Values) (*http.Response, error) { | |||
return defaultClient.PostForm(url, data) | |||
} | |||
// PostForm is a convenience method for doing simple POST operations using | |||
// pre-filled url.Values form data. | |||
func (c *Client) PostForm(url string, data url.Values) (*http.Response, error) { | |||
return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) | |||
} | |||
// StandardClient returns a stdlib *http.Client with a custom Transport, which | |||
// shims in a *retryablehttp.Client for added retries. | |||
func (c *Client) StandardClient() *http.Client { | |||
return &http.Client{ | |||
Transport: &RoundTripper{Client: c}, | |||
} | |||
} |
@ -0,0 +1,8 @@ | |||
module github.com/hashicorp/go-retryablehttp | |||
require ( | |||
github.com/hashicorp/go-cleanhttp v0.5.1 | |||
github.com/hashicorp/go-hclog v0.9.2 | |||
) | |||
go 1.13 |
@ -0,0 +1,10 @@ | |||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | |||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= | |||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= | |||
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= | |||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= | |||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | |||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | |||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= | |||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= |
@ -0,0 +1,43 @@ | |||
package retryablehttp | |||
import ( | |||
"net/http" | |||
"sync" | |||
) | |||
// RoundTripper implements the http.RoundTripper interface, using a retrying | |||
// HTTP client to execute requests. | |||
// | |||
// It is important to note that retryablehttp doesn't always act exactly as a | |||
// RoundTripper should. This is highly dependent on the retryable client's | |||
// configuration. | |||
type RoundTripper struct { | |||
// The client to use during requests. If nil, the default retryablehttp | |||
// client and settings will be used. | |||
Client *Client | |||
// once ensures that the logic to initialize the default client runs at | |||
// most once, in a single thread. | |||
once sync.Once | |||
} | |||
// init initializes the underlying retryable client. | |||
func (rt *RoundTripper) init() { | |||
if rt.Client == nil { | |||
rt.Client = NewClient() | |||
} | |||
} | |||
// RoundTrip satisfies the http.RoundTripper interface. | |||
func (rt *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { | |||
rt.once.Do(rt.init) | |||
// Convert the request to be retryable. | |||
retryableReq, err := FromRequest(req) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// Execute the request. | |||
return rt.Client.Do(retryableReq) | |||
} |
@ -0,0 +1,100 @@ | |||
// | |||
// Copyright 2017, Sander van Harmelen | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
package gitlab | |||
import "fmt" | |||
// ApplicationsService handles communication with administrables applications | |||
// of the Gitlab API. | |||
// | |||
// Gitlab API docs : https://docs.gitlab.com/ee/api/applications.html | |||
type ApplicationsService struct { | |||
client *Client | |||
} | |||
type Application struct { | |||
ID int `json:"id"` | |||
ApplicationID string `json:"application_id"` | |||
ApplicationName string `json:"application_name"` | |||
Secret string `json:"secret"` | |||
CallbackURL string `json:"callback_url"` | |||
Confidential bool `json:"confidential"` | |||
} | |||
// CreateApplicationOptions represents the available CreateApplication() options. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/applications.html#create-an-application | |||
type CreateApplicationOptions struct { | |||
Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
RedirectURI *string `url:"redirect_uri,omitempty" json:"redirect_uri,omitempty"` | |||
Scopes *string `url:"scopes,omitempty" json:"scopes,omitempty"` | |||
Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"` | |||
} | |||
// CreateApplication creates a new application owned by the authenticated user. | |||
// | |||
// Gitlab API docs : https://docs.gitlab.com/ce/api/applications.html#create-an-application | |||
func (s *ApplicationsService) CreateApplication(opt *CreateApplicationOptions, options ...RequestOptionFunc) (*Application, *Response, error) { | |||
req, err := s.client.NewRequest("POST", "applications", opt, options) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
a := new(Application) | |||
resp, err := s.client.Do(req, a) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return a, resp, err | |||
} | |||
type ListApplicationsOptions ListOptions | |||
// ListApplications get a list of administrables applications by the authenticated user | |||
// | |||
// Gitlab API docs : https://docs.gitlab.com/ce/api/applications.html#list-all-applications | |||
func (s *ApplicationsService) ListApplications(opt *ListApplicationsOptions, options ...RequestOptionFunc) ([]*Application, *Response, error) { | |||
req, err := s.client.NewRequest("GET", "applications", opt, options) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var as []*Application | |||
resp, err := s.client.Do(req, &as) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return as, resp, err | |||
} | |||
// DeleteApplication removes a specific application. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/applications.html#delete-an-application | |||
func (s *ApplicationsService) DeleteApplication(application int, options ...RequestOptionFunc) (*Response, error) { | |||
u := fmt.Sprintf("applications/%d", application) | |||
req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(req, nil) | |||
} |
@ -0,0 +1,49 @@ | |||
package gitlab | |||
import ( | |||
"net/http" | |||
retryablehttp "github.com/hashicorp/go-retryablehttp" | |||
) | |||
// ClientOptionFunc can be used customize a new GitLab API client. | |||
type ClientOptionFunc func(*Client) error | |||
// WithBaseURL sets the base URL for API requests to a custom endpoint. | |||
func WithBaseURL(urlStr string) ClientOptionFunc { | |||
return func(c *Client) error { | |||
return c.setBaseURL(urlStr) | |||
} | |||
} | |||
// WithCustomBackoff can be used to configure a custom backoff policy. | |||
func WithCustomBackoff(backoff retryablehttp.Backoff) ClientOptionFunc { | |||
return func(c *Client) error { | |||
c.client.Backoff = backoff | |||
return nil | |||
} | |||
} | |||
// WithCustomRetry can be used to configure a custom retry policy. | |||
func WithCustomRetry(checkRetry retryablehttp.CheckRetry) ClientOptionFunc { | |||
return func(c *Client) error { | |||
c.client.CheckRetry = checkRetry | |||
return nil | |||
} | |||
} | |||
// WithHTTPClient can be used to configure a custom HTTP client. | |||
func WithHTTPClient(httpClient *http.Client) ClientOptionFunc { | |||
return func(c *Client) error { | |||
c.client.HTTPClient = httpClient | |||
return nil | |||
} | |||
} | |||
// WithoutRetries disables the default retry logic. | |||
func WithoutRetries() ClientOptionFunc { | |||
return func(c *Client) error { | |||
c.disableRetries = true | |||
return nil | |||
} | |||
} |
@ -0,0 +1,221 @@ | |||
package gitlab | |||
import ( | |||
"fmt" | |||
"time" | |||
) | |||
// DeployTokensService handles communication with the deploy tokens related methods | |||
// of the GitLab API. | |||
// | |||
// GitLab API docs: https://docs.gitlab.com/ce/api/deploy_tokens.html | |||
type DeployTokensService struct { | |||
client *Client | |||
} | |||
// DeployToken represents a GitLab deploy token. | |||
type DeployToken struct { | |||
ID int `json:"id"` | |||
Name string `json:"name"` | |||
Username string `json:"username"` | |||
ExpiresAt *time.Time `json:"expires_at"` | |||
Token string `json:"token,omitempty"` | |||
Scopes []string `json:"scopes"` | |||
} | |||
func (k DeployToken) String() string { | |||
return Stringify(k) | |||
} | |||
// ListAllDeployTokens gets a list of all deploy tokens. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/deploy_tokens.html#list-all-deploy-tokens | |||
func (s *DeployTokensService) ListAllDeployTokens(options ...RequestOptionFunc) ([]*DeployToken, *Response, error) { | |||
req, err := s.client.NewRequest("GET", "deploy_tokens", nil, options) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var ts []*DeployToken | |||
resp, err := s.client.Do(req, &ts) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return ts, resp, err | |||
} | |||
// ListProjectDeployTokensOptions represents the available ListProjectDeployTokens() | |||
// options. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/deploy_tokens.html#list-project-deploy-tokens | |||
type ListProjectDeployTokensOptions ListOptions | |||
// ListProjectDeployTokens gets a list of a project's deploy tokens. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/deploy_tokens.html#list-project-deploy-tokens | |||
func (s *DeployTokensService) ListProjectDeployTokens(pid interface{}, opt *ListProjectDeployTokensOptions, options ...RequestOptionFunc) ([]*DeployToken, *Response, error) { | |||
project, err := parseID(pid) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
u := fmt.Sprintf("projects/%s/deploy_tokens", pathEscape(project)) | |||
req, err := s.client.NewRequest("GET", u, opt, options) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var ts []*DeployToken | |||
resp, err := s.client.Do(req, &ts) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return ts, resp, err | |||
} | |||
// CreateProjectDeployTokenOptions represents the available CreateProjectDeployToken() options. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/deploy_tokens.html#create-a-project-deploy-token | |||
type CreateProjectDeployTokenOptions struct { | |||
Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
ExpiresAt *time.Time `url:"expires_at,omitempty" json:"expires_at,omitempty"` | |||
Username *string `url:"username,omitempty" json:"username,omitempty"` | |||
Scopes []string `url:"scopes,omitempty" json:"scopes,omitempty"` | |||
} | |||
// CreateProjectDeployToken creates a new deploy token for a project. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/deploy_tokens.html#create-a-project-deploy-token | |||
func (s *DeployTokensService) CreateProjectDeployToken(pid interface{}, opt *CreateProjectDeployTokenOptions, options ...RequestOptionFunc) (*DeployToken, *Response, error) { | |||
project, err := parseID(pid) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
u := fmt.Sprintf("projects/%s/deploy_tokens", pathEscape(project)) | |||
req, err := s.client.NewRequest("POST", u, opt, options) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
t := new(DeployToken) | |||
resp, err := s.client.Do(req, t) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return t, resp, err | |||
} | |||
// DeleteProjectDeployToken removes a deploy token from the project. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/deploy_tokens.html#delete-a-project-deploy-token | |||
func (s *DeployTokensService) DeleteProjectDeployToken(pid interface{}, deployToken int, options ...RequestOptionFunc) (*Response, error) { | |||
project, err := parseID(pid) | |||
if err != nil { | |||
return nil, err | |||
} | |||
u := fmt.Sprintf("projects/%s/deploy_tokens/%d", pathEscape(project), deployToken) | |||
req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(req, nil) | |||
} | |||
// ListGroupDeployTokensOptions represents the available ListGroupDeployTokens() | |||
// options. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/deploy_tokens.html#list-group-deploy-deploy-tokens | |||
type ListGroupDeployTokensOptions ListOptions | |||
// ListGroupDeployTokens gets a list of a group’s deploy tokens. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/deploy_tokens.html#list-project-deploy-tokens | |||
func (s *DeployTokensService) ListGroupDeployTokens(gid interface{}, opt *ListGroupDeployTokensOptions, options ...RequestOptionFunc) ([]*DeployToken, *Response, error) { | |||
group, err := parseID(gid) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
u := fmt.Sprintf("groups/%s/deploy_tokens", pathEscape(group)) | |||
req, err := s.client.NewRequest("GET", u, opt, options) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var ts []*DeployToken | |||
resp, err := s.client.Do(req, &ts) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return ts, resp, err | |||
} | |||
// CreateGroupDeployTokenOptions represents the available CreateGroupDeployToken() options. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/deploy_tokens.html#create-a-group-deploy-token | |||
type CreateGroupDeployTokenOptions struct { | |||
Name *string `url:"name,omitempty" json:"name,omitempty"` | |||
ExpiresAt *time.Time `url:"expires_at,omitempty" json:"expires_at,omitempty"` | |||
Username *string `url:"username,omitempty" json:"username,omitempty"` | |||
Scopes []string `url:"scopes,omitempty" json:"scopes,omitempty"` | |||
} | |||
// CreateGroupDeployToken creates a new deploy token for a group. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/deploy_tokens.html#create-a-group-deploy-token | |||
func (s *DeployTokensService) CreateGroupDeployToken(gid interface{}, opt *CreateGroupDeployTokenOptions, options ...RequestOptionFunc) (*DeployToken, *Response, error) { | |||
group, err := parseID(gid) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
u := fmt.Sprintf("groups/%s/deploy_tokens", pathEscape(group)) | |||
req, err := s.client.NewRequest("POST", u, opt, options) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
t := new(DeployToken) | |||
resp, err := s.client.Do(req, t) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return t, resp, err | |||
} | |||
// DeleteGroupDeployToken removes a deploy token from the group. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/deploy_tokens.html#delete-a-group-deploy-token | |||
func (s *DeployTokensService) DeleteGroupDeployToken(gid interface{}, deployToken int, options ...RequestOptionFunc) (*Response, error) { | |||
group, err := parseID(gid) | |||
if err != nil { | |||
return nil, err | |||
} | |||
u := fmt.Sprintf("groups/%s/deploy_tokens/%d", pathEscape(group), deployToken) | |||
req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(req, nil) | |||
} |
@ -0,0 +1,133 @@ | |||
package gitlab | |||
// systemHookEvent is used to pre-process events to determine the | |||
// system hook event type. | |||
type systemHookEvent struct { | |||
BaseSystemEvent | |||
ObjectKind string `json:"object_kind"` | |||
} | |||
// BaseSystemEvent contains system hook's common properties. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
type BaseSystemEvent struct { | |||
EventName string `json:"event_name"` | |||
CreatedAt string `json:"created_at"` | |||
UpdatedAt string `json:"updated_at"` | |||
} | |||
// ProjectSystemEvent represents a project system event. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
type ProjectSystemEvent struct { | |||
BaseSystemEvent | |||
Name string `json:"name"` | |||
Path string `json:"path"` | |||
PathWithNamespace string `json:"path_with_namespace"` | |||
ProjectID int `json:"project_id"` | |||
OwnerName string `json:"owner_name"` | |||
OwnerEmail string `json:"owner_email"` | |||
ProjectVisibility string `json:"project_visibility"` | |||
OldPathWithNamespace string `json:"old_path_with_namespace,omitempty"` | |||
} | |||
// GroupSystemEvent represents a group system event. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
type GroupSystemEvent struct { | |||
BaseSystemEvent | |||
Name string `json:"name"` | |||
Path string `json:"path"` | |||
PathWithNamespace string `json:"full_path"` | |||
GroupID int `json:"group_id"` | |||
OwnerName string `json:"owner_name"` | |||
OwnerEmail string `json:"owner_email"` | |||
ProjectVisibility string `json:"project_visibility"` | |||
OldPath string `json:"old_path,omitempty"` | |||
OldPathWithNamespace string `json:"old_full_path,omitempty"` | |||
} | |||
// KeySystemEvent represents a key system event. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
type KeySystemEvent struct { | |||
BaseSystemEvent | |||
ID int `json:"id"` | |||
Username string `json:"username"` | |||
Key string `json:"key"` | |||
} | |||
// UserSystemEvent represents a user system event. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
type UserSystemEvent struct { | |||
BaseSystemEvent | |||
ID int `json:"user_id"` | |||
Name string `json:"name"` | |||
Username string `json:"username"` | |||
OldUsername string `json:"old_username,omitempty"` | |||
Email string `json:"email"` | |||
} | |||
// UserGroupSystemEvent represents a user group system event. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
type UserGroupSystemEvent struct { | |||
BaseSystemEvent | |||
ID int `json:"user_id"` | |||
Name string `json:"user_name"` | |||
Username string `json:"user_username"` | |||
Email string `json:"user_email"` | |||
GroupID int `json:"group_id"` | |||
GroupName string `json:"group_name"` | |||
GroupPath string `json:"group_path"` | |||
GroupAccess string `json:"group_access"` | |||
} | |||
// UserTeamSystemEvent represents a user team system event. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
type UserTeamSystemEvent struct { | |||
BaseSystemEvent | |||
ID int `json:"user_id"` | |||
Name string `json:"user_name"` | |||
Username string `json:"user_username"` | |||
Email string `json:"user_email"` | |||
ProjectID int `json:"project_id"` | |||
ProjectName string `json:"project_name"` | |||
ProjectPath string `json:"project_path"` | |||
ProjectPathWithNamespace string `json:"project_path_with_namespace"` | |||
ProjectVisibility string `json:"project_visibility"` | |||
AccessLevel string `json:"access_level"` | |||
} | |||
// PushSystemEvent represents a push system event. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
type PushSystemEvent struct { | |||
BaseSystemEvent | |||
} | |||
// TagPushSystemEvent represents a tag push system event. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
type TagPushSystemEvent struct { | |||
BaseSystemEvent | |||
} | |||
// RepositoryUpdateSystemEvent represents a repository updated system event. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ee/system_hooks/system_hooks.html | |||
type RepositoryUpdateSystemEvent struct { | |||
BaseSystemEvent | |||
} |
@ -0,0 +1,199 @@ | |||
// | |||
// Copyright 2020, Eric Stevens | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
package gitlab | |||
import ( | |||
"fmt" | |||
"time" | |||
) | |||
// GroupHook represents a GitLab group hook. | |||
// | |||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-group-hooks | |||
type GroupHook struct { | |||
ID int `json:"id"` | |||
URL string `json:"url"` | |||
GroupID int `json:"group_id"` | |||
PushEvents bool `json:"push_events"` | |||
IssuesEvents bool `json:"issues_events"` | |||
ConfidentialIssuesEvents bool `json:"confidential_issues_events"` | |||
ConfidentialNoteEvents bool `json:"confidential_note_events"` | |||
MergeRequestsEvents bool `json:"merge_requests_events"` | |||
TagPushEvents bool `json:"tag_push_events"` | |||
NoteEvents bool `json:"note_events"` | |||
JobEvents bool `json:"job_events"` | |||
PipelineEvents bool `json:"pipeline_events"` | |||
WikiPageEvents bool `json:"wiki_page_events"` | |||
EnableSSLVerification bool `json:"enable_ssl_verification"` | |||
CreatedAt *time.Time `json:"created_at"` | |||
} | |||
// ListGroupHooks gets a list of group hooks. | |||
// | |||
// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-group-hooks | |||
func (s *GroupsService) ListGroupHooks(gid interface{}) ([]*GroupHook, *Response, error) { | |||
group, err := parseID(gid) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
u := fmt.Sprintf("groups/%s/hooks", pathEscape(group)) | |||
req, err := s.client.NewRequest("GET", u, nil, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var gh []*GroupHook | |||
resp, err := s.client.Do(req, &gh) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return gh, resp, err | |||
} | |||
// GetGroupHook gets a specific hook for a group. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/groups.html#get-group-hook | |||
func (s *GroupsService) GetGroupHook(pid interface{}, hook int, options ...RequestOptionFunc) (*GroupHook, *Response, error) { | |||
group, err := parseID(pid) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
u := fmt.Sprintf("groups/%s/hooks/%d", pathEscape(group), hook) | |||
req, err := s.client.NewRequest("GET", u, nil, options) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
gh := new(GroupHook) | |||
resp, err := s.client.Do(req, gh) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return gh, resp, err | |||
} | |||
// AddGroupHookOptions represents the available AddGroupHook() options. | |||
// | |||
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#add-group-hook | |||
type AddGroupHookOptions struct { | |||
URL *string `url:"url,omitempty" json:"url,omitempty"` | |||
PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` | |||
IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` | |||
ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` | |||
ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` | |||
MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` | |||
TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` | |||
NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` | |||
JobEvents *bool `url:"job_events,omitempty" json:"job_events,omitempty"` | |||
PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` | |||
WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` | |||
EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"` | |||
Token *string `url:"token,omitempty" json:"token,omitempty"` | |||
} | |||
// AddGroupHook create a new group scoped webhook. | |||
// | |||
// GitLab API docs: https://docs.gitlab.com/ee/api/groups.html#add-group-hook | |||
func (s *GroupsService) AddGroupHook(gid interface{}, opt *AddGroupHookOptions, options ...RequestOptionFunc) (*GroupHook, *Response, error) { | |||
group, err := parseID(gid) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
u := fmt.Sprintf("groups/%s/hooks", pathEscape(group)) | |||
req, err := s.client.NewRequest("POST", u, opt, options) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
gh := new(GroupHook) | |||
resp, err := s.client.Do(req, gh) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return gh, resp, err | |||
} | |||
// EditGroupHookOptions represents the available EditGroupHook() options. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/groups.html#edit-group-hook | |||
type EditGroupHookOptions struct { | |||
URL *string `url:"url,omitempty" json:"url,omitempty"` | |||
PushEvents *bool `url:"push_events,omitempty" json:"push_events,omitempty"` | |||
IssuesEvents *bool `url:"issues_events,omitempty" json:"issues_events,omitempty"` | |||
ConfidentialIssuesEvents *bool `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"` | |||
ConfidentialNoteEvents *bool `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"` | |||
MergeRequestsEvents *bool `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"` | |||
TagPushEvents *bool `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"` | |||
NoteEvents *bool `url:"note_events,omitempty" json:"note_events,omitempty"` | |||
JobEvents *bool `url:"job_events,omitempty" json:"job_events,omitempty"` | |||
PipelineEvents *bool `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"` | |||
WikiPageEvents *bool `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"` | |||
EnableSSLVerification *bool `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"` | |||
Token *string `url:"token,omitempty" json:"token,omitempty"` | |||
} | |||
// EditGroupHook edits a hook for a specified group. | |||
// | |||
// Gitlab API docs: | |||
// https://docs.gitlab.com/ce/api/groups.html#edit-group-hook | |||
func (s *GroupsService) EditGroupHook(pid interface{}, hook int, opt *EditGroupHookOptions, options ...RequestOptionFunc) (*GroupHook, *Response, error) { | |||
group, err := parseID(pid) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
u := fmt.Sprintf("groups/%s/hooks/%d", pathEscape(group), hook) | |||
req, err := s.client.NewRequest("PUT", u, opt, options) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
gh := new(GroupHook) | |||
resp, err := s.client.Do(req, gh) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return gh, resp, err | |||
} | |||
// DeleteGroupHook removes a hook from a group. This is an idempotent | |||
// method and can be called multiple times. | |||
// | |||
// GitLab API docs: | |||
// https://docs.gitlab.com/ce/api/groups.html#delete-group-hook | |||
func (s *GroupsService) DeleteGroupHook(pid interface{}, hook int, options ...RequestOptionFunc) (*Response, error) { | |||
group, err := parseID(pid) | |||
if err != nil { | |||
return nil, err | |||
} | |||
u := fmt.Sprintf("groups/%s/hooks/%d", pathEscape(group), hook) | |||
req, err := s.client.NewRequest("DELETE", u, nil, options) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(req, nil) | |||
} |