aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go
blob: 7352926cab472f9ade24ee8e9f1a8929477a6525 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// 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 protodesc

import (
	_ "embed"
	"fmt"
	"os"
	"sync"

	"google.golang.org/protobuf/internal/filedesc"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/descriptorpb"
)

const (
	SupportedEditionsMinimum = descriptorpb.Edition_EDITION_PROTO2
	SupportedEditionsMaximum = descriptorpb.Edition_EDITION_2023
)

//go:embed editions_defaults.binpb
var binaryEditionDefaults []byte
var defaults = &descriptorpb.FeatureSetDefaults{}
var defaultsCacheMu sync.Mutex
var defaultsCache = make(map[filedesc.Edition]*descriptorpb.FeatureSet)

func init() {
	err := proto.Unmarshal(binaryEditionDefaults, defaults)
	if err != nil {
		fmt.Fprintf(os.Stderr, "unmarshal editions defaults: %v\n", err)
		os.Exit(1)
	}
}

func fromEditionProto(epb descriptorpb.Edition) filedesc.Edition {
	return filedesc.Edition(epb)
}

func toEditionProto(ed filedesc.Edition) descriptorpb.Edition {
	switch ed {
	case filedesc.EditionUnknown:
		return descriptorpb.Edition_EDITION_UNKNOWN
	case filedesc.EditionProto2:
		return descriptorpb.Edition_EDITION_PROTO2
	case filedesc.EditionProto3:
		return descriptorpb.Edition_EDITION_PROTO3
	case filedesc.Edition2023:
		return descriptorpb.Edition_EDITION_2023
	default:
		panic(fmt.Sprintf("unknown value for edition: %v", ed))
	}
}

func getFeatureSetFor(ed filedesc.Edition) *descriptorpb.FeatureSet {
	defaultsCacheMu.Lock()
	defer defaultsCacheMu.Unlock()
	if def, ok := defaultsCache[ed]; ok {
		return def
	}
	edpb := toEditionProto(ed)
	if defaults.GetMinimumEdition() > edpb || defaults.GetMaximumEdition() < edpb {
		// This should never happen protodesc.(FileOptions).New would fail when
		// initializing the file descriptor.
		// This most likely means the embedded defaults were not updated.
		fmt.Fprintf(os.Stderr, "internal error: unsupported edition %v (did you forget to update the embedded defaults (i.e. the bootstrap descriptor proto)?)\n", edpb)
		os.Exit(1)
	}
	fs := defaults.GetDefaults()[0].GetFeatures()
	// Using a linear search for now.
	// Editions are guaranteed to be sorted and thus we could use a binary search.
	// Given that there are only a handful of editions (with one more per year)
	// there is not much reason to use a binary search.
	for _, def := range defaults.GetDefaults() {
		if def.GetEdition() <= edpb {
			fs = def.GetFeatures()
		} else {
			break
		}
	}
	defaultsCache[ed] = fs
	return fs
}

func resolveFeatureHasFieldPresence(fileDesc *filedesc.File, fieldDesc *descriptorpb.FieldDescriptorProto) bool {
	fs := fieldDesc.GetOptions().GetFeatures()
	if fs == nil || fs.FieldPresence == nil {
		return fileDesc.L1.EditionFeatures.IsFieldPresence
	}
	return fs.GetFieldPresence() == descriptorpb.FeatureSet_LEGACY_REQUIRED ||
		fs.GetFieldPresence() == descriptorpb.FeatureSet_EXPLICIT
}

func resolveFeatureRepeatedFieldEncodingPacked(fileDesc *filedesc.File, fieldDesc *descriptorpb.FieldDescriptorProto) bool {
	fs := fieldDesc.GetOptions().GetFeatures()
	if fs == nil || fs.RepeatedFieldEncoding == nil {
		return fileDesc.L1.EditionFeatures.IsPacked
	}
	return fs.GetRepeatedFieldEncoding() == descriptorpb.FeatureSet_PACKED
}

func resolveFeatureEnforceUTF8(fileDesc *filedesc.File, fieldDesc *descriptorpb.FieldDescriptorProto) bool {
	fs := fieldDesc.GetOptions().GetFeatures()
	if fs == nil || fs.Utf8Validation == nil {
		return fileDesc.L1.EditionFeatures.IsUTF8Validated
	}
	return fs.GetUtf8Validation() == descriptorpb.FeatureSet_VERIFY
}

func resolveFeatureDelimitedEncoding(fileDesc *filedesc.File, fieldDesc *descriptorpb.FieldDescriptorProto) bool {
	fs := fieldDesc.GetOptions().GetFeatures()
	if fs == nil || fs.MessageEncoding == nil {
		return fileDesc.L1.EditionFeatures.IsDelimitedEncoded
	}
	return fs.GetMessageEncoding() == descriptorpb.FeatureSet_DELIMITED
}

// initFileDescFromFeatureSet initializes editions related fields in fd based
// on fs. If fs is nil it is assumed to be an empty featureset and all fields
// will be initialized with the appropriate default. fd.L1.Edition must be set
// before calling this function.
func initFileDescFromFeatureSet(fd *filedesc.File, fs *descriptorpb.FeatureSet) {
	dfs := getFeatureSetFor(fd.L1.Edition)
	if fs == nil {
		fs = &descriptorpb.FeatureSet{}
	}

	var fieldPresence descriptorpb.FeatureSet_FieldPresence
	if fp := fs.FieldPresence; fp != nil {
		fieldPresence = *fp
	} else {
		fieldPresence = *dfs.FieldPresence
	}
	fd.L1.EditionFeatures.IsFieldPresence = fieldPresence == descriptorpb.FeatureSet_LEGACY_REQUIRED ||
		fieldPresence == descriptorpb.FeatureSet_EXPLICIT

	var enumType descriptorpb.FeatureSet_EnumType
	if et := fs.EnumType; et != nil {
		enumType = *et
	} else {
		enumType = *dfs.EnumType
	}
	fd.L1.EditionFeatures.IsOpenEnum = enumType == descriptorpb.FeatureSet_OPEN

	var respeatedFieldEncoding descriptorpb.FeatureSet_RepeatedFieldEncoding
	if rfe := fs.RepeatedFieldEncoding; rfe != nil {
		respeatedFieldEncoding = *rfe
	} else {
		respeatedFieldEncoding = *dfs.RepeatedFieldEncoding
	}
	fd.L1.EditionFeatures.IsPacked = respeatedFieldEncoding == descriptorpb.FeatureSet_PACKED

	var isUTF8Validated descriptorpb.FeatureSet_Utf8Validation
	if utf8val := fs.Utf8Validation; utf8val != nil {
		isUTF8Validated = *utf8val
	} else {
		isUTF8Validated = *dfs.Utf8Validation
	}
	fd.L1.EditionFeatures.IsUTF8Validated = isUTF8Validated == descriptorpb.FeatureSet_VERIFY

	var messageEncoding descriptorpb.FeatureSet_MessageEncoding
	if me := fs.MessageEncoding; me != nil {
		messageEncoding = *me
	} else {
		messageEncoding = *dfs.MessageEncoding
	}
	fd.L1.EditionFeatures.IsDelimitedEncoded = messageEncoding == descriptorpb.FeatureSet_DELIMITED

	var jsonFormat descriptorpb.FeatureSet_JsonFormat
	if jf := fs.JsonFormat; jf != nil {
		jsonFormat = *jf
	} else {
		jsonFormat = *dfs.JsonFormat
	}
	fd.L1.EditionFeatures.IsJSONCompliant = jsonFormat == descriptorpb.FeatureSet_ALLOW
}