blob: e2100861f465a352d54f7362c42f718acc883daf [file] [log] [blame]
Damien Neilb0c26f12019-12-16 09:37:59 -08001// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package impl
6
7import (
8 "fmt"
9 "math"
10 "math/bits"
11 "reflect"
12 "unicode/utf8"
13
14 "google.golang.org/protobuf/internal/encoding/wire"
Damien Neil0bf97b72020-01-24 09:00:33 -080015 "google.golang.org/protobuf/internal/flags"
Damien Neilb0c26f12019-12-16 09:37:59 -080016 "google.golang.org/protobuf/internal/strs"
17 pref "google.golang.org/protobuf/reflect/protoreflect"
18 preg "google.golang.org/protobuf/reflect/protoregistry"
19 piface "google.golang.org/protobuf/runtime/protoiface"
20)
21
22// ValidationStatus is the result of validating the wire-format encoding of a message.
23type ValidationStatus int
24
25const (
26 // ValidationUnknown indicates that unmarshaling the message might succeed or fail.
27 // The validator was unable to render a judgement.
28 //
29 // The only causes of this status are an aberrant message type appearing somewhere
30 // in the message or a failure in the extension resolver.
31 ValidationUnknown ValidationStatus = iota + 1
32
33 // ValidationInvalid indicates that unmarshaling the message will fail.
34 ValidationInvalid
35
Damien Neilcadb4ab2020-02-03 16:17:31 -080036 // ValidationValid indicates that unmarshaling the message will succeed.
37 ValidationValid
Damien Neilb0c26f12019-12-16 09:37:59 -080038)
39
40func (v ValidationStatus) String() string {
41 switch v {
42 case ValidationUnknown:
43 return "ValidationUnknown"
44 case ValidationInvalid:
45 return "ValidationInvalid"
Damien Neilcadb4ab2020-02-03 16:17:31 -080046 case ValidationValid:
47 return "ValidationValid"
Damien Neilb0c26f12019-12-16 09:37:59 -080048 default:
49 return fmt.Sprintf("ValidationStatus(%d)", int(v))
50 }
51}
52
53// Validate determines whether the contents of the buffer are a valid wire encoding
54// of the message type.
55//
56// This function is exposed for testing.
Damien Neilcadb4ab2020-02-03 16:17:31 -080057func Validate(b []byte, mt pref.MessageType, opts piface.UnmarshalOptions) (out piface.UnmarshalOutput, _ ValidationStatus) {
Damien Neilb0c26f12019-12-16 09:37:59 -080058 mi, ok := mt.(*MessageInfo)
59 if !ok {
Damien Neilcadb4ab2020-02-03 16:17:31 -080060 return out, ValidationUnknown
Damien Neilb0c26f12019-12-16 09:37:59 -080061 }
Damien Neilcadb4ab2020-02-03 16:17:31 -080062 o, st := mi.validate(b, 0, unmarshalOptions(opts))
63 out.Initialized = o.initialized
64 return out, st
Damien Neilb0c26f12019-12-16 09:37:59 -080065}
66
67type validationInfo struct {
68 mi *MessageInfo
69 typ validationType
70 keyType, valType validationType
71
Damien Neil170b2bf2020-01-24 16:42:42 -080072 // For non-required fields, requiredBit is 0.
Damien Neilb0c26f12019-12-16 09:37:59 -080073 //
Damien Neil170b2bf2020-01-24 16:42:42 -080074 // For required fields, requiredBit's nth bit is set, where n is a
75 // unique index in the range [0, MessageInfo.numRequiredFields).
76 //
77 // If there are more than 64 required fields, requiredBit is 0.
78 requiredBit uint64
Damien Neilb0c26f12019-12-16 09:37:59 -080079}
80
81type validationType uint8
82
83const (
84 validationTypeOther validationType = iota
85 validationTypeMessage
86 validationTypeGroup
87 validationTypeMap
88 validationTypeRepeatedVarint
89 validationTypeRepeatedFixed32
90 validationTypeRepeatedFixed64
91 validationTypeVarint
92 validationTypeFixed32
93 validationTypeFixed64
94 validationTypeBytes
95 validationTypeUTF8String
96)
97
98func newFieldValidationInfo(mi *MessageInfo, si structInfo, fd pref.FieldDescriptor, ft reflect.Type) validationInfo {
99 var vi validationInfo
100 switch {
101 case fd.ContainingOneof() != nil:
102 switch fd.Kind() {
103 case pref.MessageKind:
104 vi.typ = validationTypeMessage
105 if ot, ok := si.oneofWrappersByNumber[fd.Number()]; ok {
106 vi.mi = getMessageInfo(ot.Field(0).Type)
107 }
108 case pref.GroupKind:
109 vi.typ = validationTypeGroup
110 if ot, ok := si.oneofWrappersByNumber[fd.Number()]; ok {
111 vi.mi = getMessageInfo(ot.Field(0).Type)
112 }
113 case pref.StringKind:
114 if strs.EnforceUTF8(fd) {
115 vi.typ = validationTypeUTF8String
116 }
117 }
118 default:
119 vi = newValidationInfo(fd, ft)
120 }
121 if fd.Cardinality() == pref.Required {
122 // Avoid overflow. The required field check is done with a 64-bit mask, with
123 // any message containing more than 64 required fields always reported as
124 // potentially uninitialized, so it is not important to get a precise count
125 // of the required fields past 64.
126 if mi.numRequiredFields < math.MaxUint8 {
127 mi.numRequiredFields++
Damien Neil170b2bf2020-01-24 16:42:42 -0800128 vi.requiredBit = 1 << (mi.numRequiredFields - 1)
Damien Neilb0c26f12019-12-16 09:37:59 -0800129 }
130 }
131 return vi
132}
133
134func newValidationInfo(fd pref.FieldDescriptor, ft reflect.Type) validationInfo {
135 var vi validationInfo
136 switch {
137 case fd.IsList():
138 switch fd.Kind() {
139 case pref.MessageKind:
140 vi.typ = validationTypeMessage
141 if ft.Kind() == reflect.Slice {
142 vi.mi = getMessageInfo(ft.Elem())
143 }
144 case pref.GroupKind:
145 vi.typ = validationTypeGroup
146 if ft.Kind() == reflect.Slice {
147 vi.mi = getMessageInfo(ft.Elem())
148 }
149 case pref.StringKind:
150 vi.typ = validationTypeBytes
151 if strs.EnforceUTF8(fd) {
152 vi.typ = validationTypeUTF8String
153 }
154 default:
155 switch wireTypes[fd.Kind()] {
156 case wire.VarintType:
157 vi.typ = validationTypeRepeatedVarint
158 case wire.Fixed32Type:
159 vi.typ = validationTypeRepeatedFixed32
160 case wire.Fixed64Type:
161 vi.typ = validationTypeRepeatedFixed64
162 }
163 }
164 case fd.IsMap():
165 vi.typ = validationTypeMap
166 switch fd.MapKey().Kind() {
167 case pref.StringKind:
168 if strs.EnforceUTF8(fd) {
169 vi.keyType = validationTypeUTF8String
170 }
171 }
172 switch fd.MapValue().Kind() {
173 case pref.MessageKind:
174 vi.valType = validationTypeMessage
175 if ft.Kind() == reflect.Map {
176 vi.mi = getMessageInfo(ft.Elem())
177 }
178 case pref.StringKind:
179 if strs.EnforceUTF8(fd) {
180 vi.valType = validationTypeUTF8String
181 }
182 }
183 default:
184 switch fd.Kind() {
185 case pref.MessageKind:
186 vi.typ = validationTypeMessage
187 if !fd.IsWeak() {
188 vi.mi = getMessageInfo(ft)
189 }
190 case pref.GroupKind:
191 vi.typ = validationTypeGroup
192 vi.mi = getMessageInfo(ft)
193 case pref.StringKind:
194 vi.typ = validationTypeBytes
195 if strs.EnforceUTF8(fd) {
196 vi.typ = validationTypeUTF8String
197 }
198 default:
199 switch wireTypes[fd.Kind()] {
200 case wire.VarintType:
201 vi.typ = validationTypeVarint
202 case wire.Fixed32Type:
203 vi.typ = validationTypeFixed32
204 case wire.Fixed64Type:
205 vi.typ = validationTypeFixed64
Damien Neil6635e7d2020-01-15 15:08:57 -0800206 case wire.BytesType:
207 vi.typ = validationTypeBytes
Damien Neilb0c26f12019-12-16 09:37:59 -0800208 }
209 }
210 }
211 return vi
212}
213
Damien Neilcadb4ab2020-02-03 16:17:31 -0800214func (mi *MessageInfo) validate(b []byte, groupTag wire.Number, opts unmarshalOptions) (out unmarshalOutput, result ValidationStatus) {
Damien Neilcb0bfd02020-01-28 09:11:12 -0800215 mi.init()
Damien Neilb0c26f12019-12-16 09:37:59 -0800216 type validationState struct {
217 typ validationType
218 keyType, valType validationType
219 endGroup wire.Number
220 mi *MessageInfo
221 tail []byte
222 requiredMask uint64
223 }
224
225 // Pre-allocate some slots to avoid repeated slice reallocation.
226 states := make([]validationState, 0, 16)
227 states = append(states, validationState{
228 typ: validationTypeMessage,
229 mi: mi,
230 })
231 if groupTag > 0 {
232 states[0].typ = validationTypeGroup
233 states[0].endGroup = groupTag
234 }
235 initialized := true
Damien Neilcadb4ab2020-02-03 16:17:31 -0800236 start := len(b)
Damien Neilb0c26f12019-12-16 09:37:59 -0800237State:
238 for len(states) > 0 {
239 st := &states[len(states)-1]
240 if st.mi != nil {
Damien Neil0bf97b72020-01-24 09:00:33 -0800241 if flags.ProtoLegacy && st.mi.isMessageSet {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800242 return out, ValidationUnknown
Damien Neil0bf97b72020-01-24 09:00:33 -0800243 }
Damien Neilb0c26f12019-12-16 09:37:59 -0800244 }
Damien Neilb0c26f12019-12-16 09:37:59 -0800245 for len(b) > 0 {
Damien Neil5d828832020-01-28 08:06:12 -0800246 // Parse the tag (field number and wire type).
247 var tag uint64
248 if b[0] < 0x80 {
249 tag = uint64(b[0])
250 b = b[1:]
251 } else if len(b) >= 2 && b[1] < 128 {
252 tag = uint64(b[0]&0x7f) + uint64(b[1])<<7
253 b = b[2:]
254 } else {
255 var n int
256 tag, n = wire.ConsumeVarint(b)
257 if n < 0 {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800258 return out, ValidationInvalid
Damien Neil5d828832020-01-28 08:06:12 -0800259 }
260 b = b[n:]
Damien Neilb0c26f12019-12-16 09:37:59 -0800261 }
Damien Neil5d828832020-01-28 08:06:12 -0800262 var num wire.Number
263 if n := tag >> 3; n < uint64(wire.MinValidNumber) || n > uint64(wire.MaxValidNumber) {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800264 return out, ValidationInvalid
Damien Neil5d828832020-01-28 08:06:12 -0800265 } else {
266 num = wire.Number(n)
Damien Neilb0c26f12019-12-16 09:37:59 -0800267 }
Damien Neil5d828832020-01-28 08:06:12 -0800268 wtyp := wire.Type(tag & 7)
269
Damien Neilb0c26f12019-12-16 09:37:59 -0800270 if wtyp == wire.EndGroupType {
271 if st.endGroup == num {
272 goto PopState
273 }
Damien Neilcadb4ab2020-02-03 16:17:31 -0800274 return out, ValidationInvalid
Damien Neilb0c26f12019-12-16 09:37:59 -0800275 }
276 var vi validationInfo
277 switch st.typ {
278 case validationTypeMap:
279 switch num {
280 case 1:
281 vi.typ = st.keyType
282 case 2:
283 vi.typ = st.valType
284 vi.mi = st.mi
Damien Neil170b2bf2020-01-24 16:42:42 -0800285 vi.requiredBit = 1
Damien Neilb0c26f12019-12-16 09:37:59 -0800286 }
287 default:
288 var f *coderFieldInfo
289 if int(num) < len(st.mi.denseCoderFields) {
290 f = st.mi.denseCoderFields[num]
291 } else {
292 f = st.mi.coderFields[num]
293 }
294 if f != nil {
295 vi = f.validation
296 if vi.typ == validationTypeMessage && vi.mi == nil {
297 // Probable weak field.
298 //
299 // TODO: Consider storing the results of this lookup somewhere
300 // rather than recomputing it on every validation.
301 fd := st.mi.Desc.Fields().ByNumber(num)
302 if fd == nil || !fd.IsWeak() {
303 break
304 }
305 messageName := fd.Message().FullName()
306 messageType, err := preg.GlobalTypes.FindMessageByName(messageName)
307 switch err {
308 case nil:
309 vi.mi, _ = messageType.(*MessageInfo)
310 case preg.NotFound:
311 vi.typ = validationTypeBytes
312 default:
Damien Neilcadb4ab2020-02-03 16:17:31 -0800313 return out, ValidationUnknown
Damien Neilb0c26f12019-12-16 09:37:59 -0800314 }
315 }
316 break
317 }
318 // Possible extension field.
319 //
320 // TODO: We should return ValidationUnknown when:
321 // 1. The resolver is not frozen. (More extensions may be added to it.)
322 // 2. The resolver returns preg.NotFound.
323 // In this case, a type added to the resolver in the future could cause
324 // unmarshaling to begin failing. Supporting this requires some way to
325 // determine if the resolver is frozen.
Damien Neil524c6062020-01-28 13:32:01 -0800326 xt, err := opts.Resolver.FindExtensionByNumber(st.mi.Desc.FullName(), num)
Damien Neilb0c26f12019-12-16 09:37:59 -0800327 if err != nil && err != preg.NotFound {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800328 return out, ValidationUnknown
Damien Neilb0c26f12019-12-16 09:37:59 -0800329 }
330 if err == nil {
331 vi = getExtensionFieldInfo(xt).validation
332 }
333 }
Damien Neil170b2bf2020-01-24 16:42:42 -0800334 if vi.requiredBit != 0 {
Damien Neilb0c26f12019-12-16 09:37:59 -0800335 // Check that the field has a compatible wire type.
336 // We only need to consider non-repeated field types,
337 // since repeated fields (and maps) can never be required.
338 ok := false
339 switch vi.typ {
340 case validationTypeVarint:
341 ok = wtyp == wire.VarintType
342 case validationTypeFixed32:
343 ok = wtyp == wire.Fixed32Type
344 case validationTypeFixed64:
345 ok = wtyp == wire.Fixed64Type
346 case validationTypeBytes, validationTypeUTF8String, validationTypeMessage, validationTypeGroup:
347 ok = wtyp == wire.BytesType
348 }
349 if ok {
Damien Neil170b2bf2020-01-24 16:42:42 -0800350 st.requiredMask |= vi.requiredBit
Damien Neilb0c26f12019-12-16 09:37:59 -0800351 }
352 }
Damien Neil8fa11b12020-01-28 08:31:04 -0800353
354 switch wtyp {
355 case wire.VarintType:
Damien Neil0f783d82020-02-05 07:34:41 -0800356 if len(b) >= 10 {
Damien Neil8fa11b12020-01-28 08:31:04 -0800357 switch {
358 case b[0] < 0x80:
359 b = b[1:]
360 case b[1] < 0x80:
361 b = b[2:]
362 case b[2] < 0x80:
363 b = b[3:]
364 case b[3] < 0x80:
365 b = b[4:]
366 case b[4] < 0x80:
367 b = b[5:]
368 case b[5] < 0x80:
369 b = b[6:]
370 case b[6] < 0x80:
371 b = b[7:]
372 case b[7] < 0x80:
373 b = b[8:]
374 case b[8] < 0x80:
375 b = b[9:]
Damien Neil4d918162020-02-01 10:39:11 -0800376 case b[9] < 0x80 && b[9] < 2:
Damien Neil8fa11b12020-01-28 08:31:04 -0800377 b = b[10:]
378 default:
Damien Neilcadb4ab2020-02-03 16:17:31 -0800379 return out, ValidationInvalid
Damien Neil8fa11b12020-01-28 08:31:04 -0800380 }
381 } else {
382 switch {
383 case len(b) > 0 && b[0] < 0x80:
384 b = b[1:]
385 case len(b) > 1 && b[1] < 0x80:
386 b = b[2:]
387 case len(b) > 2 && b[2] < 0x80:
388 b = b[3:]
389 case len(b) > 3 && b[3] < 0x80:
390 b = b[4:]
391 case len(b) > 4 && b[4] < 0x80:
392 b = b[5:]
393 case len(b) > 5 && b[5] < 0x80:
394 b = b[6:]
395 case len(b) > 6 && b[6] < 0x80:
396 b = b[7:]
397 case len(b) > 7 && b[7] < 0x80:
398 b = b[8:]
399 case len(b) > 8 && b[8] < 0x80:
400 b = b[9:]
Damien Neil4d918162020-02-01 10:39:11 -0800401 case len(b) > 9 && b[9] < 2:
Damien Neil8fa11b12020-01-28 08:31:04 -0800402 b = b[10:]
403 default:
Damien Neilcadb4ab2020-02-03 16:17:31 -0800404 return out, ValidationInvalid
Damien Neil8fa11b12020-01-28 08:31:04 -0800405 }
Damien Neilb0c26f12019-12-16 09:37:59 -0800406 }
Damien Neilb0c26f12019-12-16 09:37:59 -0800407 continue State
Damien Neil8fa11b12020-01-28 08:31:04 -0800408 case wire.BytesType:
409 var size uint64
Damien Neil6f297792020-01-29 15:55:53 -0800410 if len(b) >= 1 && b[0] < 0x80 {
Damien Neil8fa11b12020-01-28 08:31:04 -0800411 size = uint64(b[0])
412 b = b[1:]
413 } else if len(b) >= 2 && b[1] < 128 {
414 size = uint64(b[0]&0x7f) + uint64(b[1])<<7
415 b = b[2:]
416 } else {
417 var n int
418 size, n = wire.ConsumeVarint(b)
Damien Neilb0c26f12019-12-16 09:37:59 -0800419 if n < 0 {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800420 return out, ValidationInvalid
Damien Neilb0c26f12019-12-16 09:37:59 -0800421 }
Damien Neil8fa11b12020-01-28 08:31:04 -0800422 b = b[n:]
Damien Neilb0c26f12019-12-16 09:37:59 -0800423 }
Damien Neil8fa11b12020-01-28 08:31:04 -0800424 if size > uint64(len(b)) {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800425 return out, ValidationInvalid
Damien Neilb0c26f12019-12-16 09:37:59 -0800426 }
Damien Neil8fa11b12020-01-28 08:31:04 -0800427 v := b[:size]
428 b = b[size:]
429 switch vi.typ {
Damien Neilcb0bfd02020-01-28 09:11:12 -0800430 case validationTypeMessage:
431 if vi.mi == nil {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800432 return out, ValidationUnknown
Damien Neil8fa11b12020-01-28 08:31:04 -0800433 }
Damien Neilcb0bfd02020-01-28 09:11:12 -0800434 vi.mi.init()
435 fallthrough
436 case validationTypeMap:
Damien Neil8fa11b12020-01-28 08:31:04 -0800437 states = append(states, validationState{
438 typ: vi.typ,
439 keyType: vi.keyType,
440 valType: vi.valType,
441 mi: vi.mi,
442 tail: b,
443 })
444 b = v
445 continue State
446 case validationTypeRepeatedVarint:
447 // Packed field.
448 for len(v) > 0 {
449 _, n := wire.ConsumeVarint(v)
450 if n < 0 {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800451 return out, ValidationInvalid
Damien Neil8fa11b12020-01-28 08:31:04 -0800452 }
453 v = v[n:]
454 }
455 case validationTypeRepeatedFixed32:
456 // Packed field.
457 if len(v)%4 != 0 {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800458 return out, ValidationInvalid
Damien Neil8fa11b12020-01-28 08:31:04 -0800459 }
460 case validationTypeRepeatedFixed64:
461 // Packed field.
462 if len(v)%8 != 0 {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800463 return out, ValidationInvalid
Damien Neil8fa11b12020-01-28 08:31:04 -0800464 }
465 case validationTypeUTF8String:
466 if !utf8.Valid(v) {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800467 return out, ValidationInvalid
Damien Neil8fa11b12020-01-28 08:31:04 -0800468 }
Damien Neilb0c26f12019-12-16 09:37:59 -0800469 }
Damien Neil8fa11b12020-01-28 08:31:04 -0800470 case wire.Fixed32Type:
471 if len(b) < 4 {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800472 return out, ValidationInvalid
Damien Neilb0c26f12019-12-16 09:37:59 -0800473 }
Damien Neil8fa11b12020-01-28 08:31:04 -0800474 b = b[4:]
475 case wire.Fixed64Type:
476 if len(b) < 8 {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800477 return out, ValidationInvalid
Damien Neilb0c26f12019-12-16 09:37:59 -0800478 }
Damien Neil8fa11b12020-01-28 08:31:04 -0800479 b = b[8:]
480 case wire.StartGroupType:
481 switch vi.typ {
482 case validationTypeGroup:
483 if vi.mi == nil {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800484 return out, ValidationUnknown
Damien Neil8fa11b12020-01-28 08:31:04 -0800485 }
Damien Neilcb0bfd02020-01-28 09:11:12 -0800486 vi.mi.init()
Damien Neil8fa11b12020-01-28 08:31:04 -0800487 states = append(states, validationState{
488 typ: validationTypeGroup,
489 mi: vi.mi,
490 endGroup: num,
491 })
492 continue State
493 default:
494 n := wire.ConsumeFieldValue(num, wtyp, b)
495 if n < 0 {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800496 return out, ValidationInvalid
Damien Neil8fa11b12020-01-28 08:31:04 -0800497 }
498 b = b[n:]
499 }
500 default:
Damien Neilcadb4ab2020-02-03 16:17:31 -0800501 return out, ValidationInvalid
Damien Neilb0c26f12019-12-16 09:37:59 -0800502 }
Damien Neilb0c26f12019-12-16 09:37:59 -0800503 }
504 if st.endGroup != 0 {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800505 return out, ValidationInvalid
Damien Neilb0c26f12019-12-16 09:37:59 -0800506 }
507 if len(b) != 0 {
Damien Neilcadb4ab2020-02-03 16:17:31 -0800508 return out, ValidationInvalid
Damien Neilb0c26f12019-12-16 09:37:59 -0800509 }
510 b = st.tail
511 PopState:
Damien Neil54a0a042020-01-08 17:53:16 -0800512 numRequiredFields := 0
Damien Neilb0c26f12019-12-16 09:37:59 -0800513 switch st.typ {
514 case validationTypeMessage, validationTypeGroup:
Damien Neil54a0a042020-01-08 17:53:16 -0800515 numRequiredFields = int(st.mi.numRequiredFields)
516 case validationTypeMap:
517 // If this is a map field with a message value that contains
518 // required fields, require that the value be present.
519 if st.mi != nil && st.mi.numRequiredFields > 0 {
520 numRequiredFields = 1
Damien Neilb0c26f12019-12-16 09:37:59 -0800521 }
522 }
Damien Neil54a0a042020-01-08 17:53:16 -0800523 // If there are more than 64 required fields, this check will
524 // always fail and we will report that the message is potentially
525 // uninitialized.
526 if numRequiredFields > 0 && bits.OnesCount64(st.requiredMask) != numRequiredFields {
527 initialized = false
528 }
Damien Neilb0c26f12019-12-16 09:37:59 -0800529 states = states[:len(states)-1]
530 }
Damien Neilcadb4ab2020-02-03 16:17:31 -0800531 out.n = start - len(b)
532 if initialized {
533 out.initialized = true
Damien Neilb0c26f12019-12-16 09:37:59 -0800534 }
Damien Neilcadb4ab2020-02-03 16:17:31 -0800535 return out, ValidationValid
Damien Neilb0c26f12019-12-16 09:37:59 -0800536}