// 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 messageset encodes and decodes the obsolete MessageSet wire format.
package messageset import ( ) // The MessageSet wire format is equivalent to a message defined as follows, // where each Item defines an extension field with a field number of 'type_id' // and content of 'message'. MessageSet extensions must be non-repeated message // fields. // // message MessageSet { // repeated group Item = 1 { // required int32 type_id = 2; // required string message = 3; // } // } const ( FieldItem = protowire.Number(1) FieldTypeID = protowire.Number(2) FieldMessage = protowire.Number(3) ) // ExtensionName is the field name for extensions of MessageSet. // // A valid MessageSet extension must be of the form: // // message MyMessage { // extend proto2.bridge.MessageSet { // optional MyMessage message_set_extension = 1234; // } // ... // } const ExtensionName = "message_set_extension" // IsMessageSet returns whether the message uses the MessageSet wire format. func ( protoreflect.MessageDescriptor) bool { , := .(interface{ () bool }) return && .() } // IsMessageSetExtension reports this field properly extends a MessageSet. func ( protoreflect.FieldDescriptor) bool { switch { case .Name() != ExtensionName: return false case !IsMessageSet(.ContainingMessage()): return false case .FullName().Parent() != .Message().FullName(): return false } return true } // SizeField returns the size of a MessageSet item field containing an extension // with the given field number, not counting the contents of the message subfield. func ( protowire.Number) int { return 2*protowire.SizeTag(FieldItem) + protowire.SizeTag(FieldTypeID) + protowire.SizeVarint(uint64()) } // Unmarshal parses a MessageSet. // // It calls fn with the type ID and value of each item in the MessageSet. // Unknown fields are discarded. // // If wantLen is true, the item values include the varint length prefix. // This is ugly, but simplifies the fast-path decoder in internal/impl. func ( []byte, bool, func( protowire.Number, []byte) error) error { for len() > 0 { , , := protowire.ConsumeTag() if < 0 { return protowire.ParseError() } = [:] if != FieldItem || != protowire.StartGroupType { := protowire.ConsumeFieldValue(, , ) if < 0 { return protowire.ParseError() } = [:] continue } , , , := ConsumeFieldValue(, ) if != nil { return } = [:] if == 0 { continue } if := (, ); != nil { return } } return nil } // ConsumeFieldValue parses b as a MessageSet item field value until and including // the trailing end group marker. It assumes the start group tag has already been parsed. // It returns the contents of the type_id and message subfields and the total // item length. // // If wantLen is true, the returned message value includes the length prefix. func ( []byte, bool) ( protowire.Number, []byte, int, error) { := len() for { , , := protowire.ConsumeTag() if < 0 { return 0, nil, 0, protowire.ParseError() } = [:] switch { case == FieldItem && == protowire.EndGroupType: if && len() == 0 { // The message field was missing, which should never happen. // Be prepared for this case anyway. = protowire.AppendVarint(, 0) } return , , - len(), nil case == FieldTypeID && == protowire.VarintType: , := protowire.ConsumeVarint() if < 0 { return 0, nil, 0, protowire.ParseError() } = [:] if < 1 || > math.MaxInt32 { return 0, nil, 0, errors.New("invalid type_id in message set") } = protowire.Number() case == FieldMessage && == protowire.BytesType: , := protowire.ConsumeBytes() if < 0 { return 0, nil, 0, protowire.ParseError() } if == nil { if { = [::] } else { = [:len():len()] } } else { // This case should never happen in practice, but handle it for // correctness: The MessageSet item contains multiple message // fields, which need to be merged. // // In the case where we're returning the length, this becomes // quite inefficient since we need to strip the length off // the existing data and reconstruct it with the combined length. if { , := protowire.ConsumeVarint() := [:] = nil = protowire.AppendVarint(, uint64(len()+len())) = append(, ...) = append(, ...) } else { = append(, ...) } } = [:] default: // We have no place to put it, so we just ignore unknown fields. := protowire.ConsumeFieldValue(, , ) if < 0 { return 0, nil, 0, protowire.ParseError() } = [:] } } } // AppendFieldStart appends the start of a MessageSet item field containing // an extension with the given number. The caller must add the message // subfield (including the tag). func ( []byte, protowire.Number) []byte { = protowire.AppendTag(, FieldItem, protowire.StartGroupType) = protowire.AppendTag(, FieldTypeID, protowire.VarintType) = protowire.AppendVarint(, uint64()) return } // AppendFieldEnd appends the trailing end group marker for a MessageSet item field. func ( []byte) []byte { return protowire.AppendTag(, FieldItem, protowire.EndGroupType) } // SizeUnknown returns the size of an unknown fields section in MessageSet format. // // See AppendUnknown. func ( []byte) ( int) { for len() > 0 { , , := protowire.ConsumeTag() if < 0 || != protowire.BytesType { return 0 } = [:] _, = protowire.ConsumeBytes() if < 0 { return 0 } = [:] += SizeField() + protowire.SizeTag(FieldMessage) + } return } // AppendUnknown appends unknown fields to b in MessageSet format. // // For historic reasons, unresolved items in a MessageSet are stored in a // message's unknown fields section in non-MessageSet format. That is, an // unknown item with typeID T and value V appears in the unknown fields as // a field with number T and value V. // // This function converts the unknown fields back into MessageSet form. func (, []byte) ([]byte, error) { for len() > 0 { , , := protowire.ConsumeTag() if < 0 || != protowire.BytesType { return nil, errors.New("invalid data in message set unknown fields") } = [:] _, = protowire.ConsumeBytes() if < 0 { return nil, errors.New("invalid data in message set unknown fields") } = AppendFieldStart(, ) = protowire.AppendTag(, FieldMessage, protowire.BytesType) = append(, [:]...) = AppendFieldEnd() = [:] } return , nil }