// 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 protoapi contains the set of types referenced by generated messages. // // WARNING: This package should only ever be imported by generated messages. // The compatibility agreement covers nothing except for functionality needed // to keep existing generated messages operational. package protoapi import ( "fmt" "sync" "github.com/golang/protobuf/v2/reflect/protoreflect" ) // TODO: How to handle Registration during the v1 to v2 switchover? type ( Message interface { Reset() String() string ProtoMessage() } ExtensionRange struct { Start, End int32 // both inclusive } ExtensionDesc struct { // Type is the descriptor type for the extension field using the v2 API. // If populated, the information in this field takes precedence over // all other fields in ExtensionDesc. Type protoreflect.ExtensionType // ExtendedType is a typed nil-pointer to the parent message type that // is being extended. It is possible for this to be unpopulated in v2 // since the message may no longer implement the v1 Message interface. // // Deprecated: Use Type.ExtendedType instead. ExtendedType Message // ExtensionType is zero value of the extension type. // // For historical reasons, reflect.TypeOf(ExtensionType) and Type.GoType // may not be identical: // * for scalars (except []byte), where ExtensionType uses *T, // while Type.GoType uses T. // * for repeated fields, where ExtensionType uses []T, // while Type.GoType uses *[]T. // // Deprecated: Use Type.GoType instead. ExtensionType interface{} // Field is the field number of the extension. // // Deprecated: Use Type.Number instead. Field int32 // field number // Name is the fully qualified name of extension. // // Deprecated: Use Type.FullName instead. Name string // Tag is the protobuf struct tag used in the v1 API. // // Deprecated: Do not use. Tag string // Filename is the proto filename in which the extension is defined. // // Deprecated: Use Type.Parent to ascend to the top-most parent and use // protoreflect.FileDescriptor.Path. Filename string } ExtensionFields extensionFields ExtensionField extensionField XXX_InternalExtensions extensionSyncMap ) // ExtensionFieldsOf returns an ExtensionFields abstraction over various // internal representations of extension fields. func ExtensionFieldsOf(p interface{}) ExtensionFields { switch p := p.(type) { case *map[int32]ExtensionField: return (*extensionMap)(p) case *XXX_InternalExtensions: return (*extensionSyncMap)(p) default: panic(fmt.Sprintf("invalid extension fields type: %T", p)) } } type extensionFields interface { Len() int Has(protoreflect.FieldNumber) bool Get(protoreflect.FieldNumber) ExtensionField Set(protoreflect.FieldNumber, ExtensionField) Clear(protoreflect.FieldNumber) Range(f func(protoreflect.FieldNumber, ExtensionField) bool) // HasInit and Locker are used by v1 GetExtension to provide // an artificial degree of concurrent safety. HasInit() bool sync.Locker } type extensionField struct { // When an extension is stored in a message using SetExtension // only desc and value are set. When the message is marshaled // Raw will be set to the encoded form of the message. // // When a message is unmarshaled and contains extensions, each // extension will have only Raw set. When such an extension is // accessed using GetExtension (or GetExtensions) desc and value // will be set. Desc *ExtensionDesc // TODO: switch to protoreflect.ExtensionType // Value is a concrete value for the extension field. Let the type of // Desc.ExtensionType be the "API type" and the type of Value be the // "storage type". The API type and storage type are the same except: // * for scalars (except []byte), where the API type uses *T, // while the storage type uses T. // * for repeated fields, where the API type uses []T, // while the storage type uses *[]T. // // The reason for the divergence is so that the storage type more naturally // matches what is expected of when retrieving the values through the // protobuf reflection APIs. // // The Value may only be populated if Desc is also populated. Value interface{} // TODO: switch to protoreflect.Value // Raw is the raw encoded bytes for the extension field. // It is possible for Raw to be populated irrespective of whether the // other fields are populated. Raw []byte // TODO: switch to protoreflect.RawFields } type extensionSyncMap struct { p *struct { mu sync.Mutex m extensionMap } } func (m extensionSyncMap) Len() int { if m.p == nil { return 0 } return m.p.m.Len() } func (m extensionSyncMap) Has(n protoreflect.FieldNumber) bool { if m.p == nil { return false } return m.p.m.Has(n) } func (m extensionSyncMap) Get(n protoreflect.FieldNumber) ExtensionField { if m.p == nil { return ExtensionField{} } return m.p.m.Get(n) } func (m *extensionSyncMap) Set(n protoreflect.FieldNumber, x ExtensionField) { if m.p == nil { m.p = new(struct { mu sync.Mutex m extensionMap }) } m.p.m.Set(n, x) } func (m extensionSyncMap) Clear(n protoreflect.FieldNumber) { if m.p == nil { return } m.p.m.Clear(n) } func (m extensionSyncMap) Range(f func(protoreflect.FieldNumber, ExtensionField) bool) { if m.p == nil { return } m.p.m.Range(f) } func (m extensionSyncMap) HasInit() bool { return m.p != nil } func (m extensionSyncMap) Lock() { m.p.mu.Lock() } func (m extensionSyncMap) Unlock() { m.p.mu.Unlock() } type extensionMap map[int32]ExtensionField func (m extensionMap) Len() int { return len(m) } func (m extensionMap) Has(n protoreflect.FieldNumber) bool { _, ok := m[int32(n)] return ok } func (m extensionMap) Get(n protoreflect.FieldNumber) ExtensionField { return m[int32(n)] } func (m *extensionMap) Set(n protoreflect.FieldNumber, x ExtensionField) { if *m == nil { *m = make(map[int32]ExtensionField) } (*m)[int32(n)] = x } func (m *extensionMap) Clear(n protoreflect.FieldNumber) { delete(*m, int32(n)) } func (m extensionMap) Range(f func(protoreflect.FieldNumber, ExtensionField) bool) { for n, x := range m { if !f(protoreflect.FieldNumber(n), x) { return } } } var globalLock sync.Mutex func (m extensionMap) HasInit() bool { return m != nil } func (m extensionMap) Lock() { if !m.HasInit() { panic("cannot lock an uninitialized map") } globalLock.Lock() } func (m extensionMap) Unlock() { globalLock.Unlock() }