1616
1717import static com .google .common .base .Preconditions .checkNotNull ;
1818
19+ import com .google .common .annotations .VisibleForTesting ;
1920import com .google .common .base .Defaults ;
2021import com .google .common .collect .ImmutableList ;
2122import com .google .common .collect .ImmutableMap ;
2930import com .google .protobuf .MessageLite ;
3031import com .google .protobuf .WireFormat ;
3132import dev .cel .common .annotations .Internal ;
33+ import dev .cel .common .exceptions .CelAttributeNotFoundException ;
3234import dev .cel .common .internal .CelLiteDescriptorPool ;
3335import dev .cel .common .internal .WellKnownProto ;
3436import dev .cel .protobuf .CelLiteDescriptor .FieldLiteDescriptor ;
5961public final class ProtoLiteCelValueConverter extends BaseProtoCelValueConverter {
6062 private static final String MAP_KEY_FIELD_NAME = "key" ;
6163 private static final String MAP_VALUE_FIELD_NAME = "value" ;
64+ private static final int MAP_KEY_FIELD_NUMBER = 1 ;
65+ private static final int MAP_VALUE_FIELD_NUMBER = 2 ;
6266
6367 private final CelLiteDescriptorPool descriptorPool ;
6468
@@ -157,11 +161,6 @@ private static boolean isStructLike(@Nullable WellKnownProto wellKnownProto) {
157161 return wellKnownProto == null || wellKnownProto == WellKnownProto .FIELD_MASK ;
158162 }
159163
160- Object getDefaultCelValue (String protoTypeName , String fieldName ) {
161- MessageLiteDescriptor messageDescriptor = descriptorPool .getDescriptorOrThrow (protoTypeName );
162- return getDefaultCelValue (messageDescriptor .getByFieldNameOrThrow (fieldName ));
163- }
164-
165164 Object getDefaultCelValue (FieldLiteDescriptor fieldDescriptor ) {
166165 return toRuntimeValue (getDefaultValue (fieldDescriptor ));
167166 }
@@ -172,6 +171,12 @@ Optional<FieldLiteDescriptor> findFieldDescriptor(String protoTypeName, int fiel
172171 .flatMap (desc -> desc .findByFieldNumber (fieldNumber ));
173172 }
174173
174+ Optional <FieldLiteDescriptor > findFieldDescriptor (String protoTypeName , String fieldName ) {
175+ return descriptorPool
176+ .findDescriptor (protoTypeName )
177+ .flatMap (desc -> desc .findByFieldName (fieldName ));
178+ }
179+
175180 MessageLite parseMessageLite (ByteString bytes , String protoTypeName ) {
176181 MessageLiteDescriptor descriptor = descriptorPool .getDescriptorOrThrow (protoTypeName );
177182 return parseMessageLite (bytes , descriptor );
@@ -194,21 +199,6 @@ private static MessageLite mergeMessageLite(
194199 }
195200 }
196201
197- Optional <Object > tryDecodeProtoMessage (ByteString bytes , String protoTypeName ) {
198- return descriptorPool
199- .findDescriptor (protoTypeName )
200- .map (descriptor -> decodeProtoMessage (bytes , protoTypeName , descriptor ));
201- }
202-
203- private Object decodeProtoMessage (
204- ByteString bytes , String protoTypeName , MessageLiteDescriptor descriptor ) {
205- WellKnownProto wellKnownProto = WellKnownProto .getByTypeName (protoTypeName ).orElse (null );
206- if (isStructLike (wellKnownProto )) {
207- return ProtoMessageLiteValue .create (bytes , protoTypeName , this );
208- }
209- return fromWellKnownProto (parseMessageLite (bytes , descriptor ), checkNotNull (wellKnownProto ));
210- }
211-
212202 @ Override
213203 public Object toRuntimeValue (Object value ) {
214204 checkNotNull (value );
@@ -284,12 +274,10 @@ private Object getScalarDefaultValue(FieldLiteDescriptor fieldDescriptor) {
284274 }
285275
286276 private Map .Entry <Object , Object > readSingleMapEntry (
287- CodedInputStream inputStream , FieldLiteDescriptor fieldDescriptor ) throws IOException {
288- String entryTypeName = fieldDescriptor .getFieldProtoTypeName ();
289- MessageLiteDescriptor entryDescriptor = descriptorPool .getDescriptorOrThrow (entryTypeName );
290- FieldLiteDescriptor keyDescriptor = entryDescriptor .getByFieldNameOrThrow (MAP_KEY_FIELD_NAME );
291- FieldLiteDescriptor valueDescriptor =
292- entryDescriptor .getByFieldNameOrThrow (MAP_VALUE_FIELD_NAME );
277+ CodedInputStream inputStream ,
278+ FieldLiteDescriptor keyDescriptor ,
279+ FieldLiteDescriptor valueDescriptor )
280+ throws IOException {
293281 int length = inputStream .readInt32 ();
294282 int oldLimit = inputStream .pushLimit (length );
295283 Object key = null ;
@@ -317,6 +305,11 @@ private Map.Entry<Object, Object> readSingleMapEntry(
317305 return new AbstractMap .SimpleImmutableEntry <>(key , value );
318306 }
319307
308+ /**
309+ * Decodes only {@code fieldDescriptor}'s occurrences in {@code bytes} into its CEL value,
310+ * skipping every other field. Returns null if the field is absent (or a repeated field has only
311+ * empty packed records on the wire).
312+ */
320313 @ Nullable Object readSingleField (ByteString bytes , FieldLiteDescriptor fieldDescriptor )
321314 throws IOException {
322315 CodedInputStream inputStream = bytes .newCodedInput ();
@@ -331,20 +324,13 @@ private Map.Entry<Object, Object> readSingleMapEntry(
331324 int tagWireType = WireFormat .getTagWireType (tag );
332325 fieldValue = readFieldValue (tagWireType , inputStream , fieldDescriptor , fieldValue );
333326 }
334- // Only this field is decoded, so unlike readAllFields, a failed conversion can't affect access
335- // to any other field.
336327 return fieldValue == null ? null : resolveFieldValue (finalizeFieldValue (fieldValue ));
337328 }
338329
339330 boolean hasSingleField (ByteString bytes , FieldLiteDescriptor fieldDescriptor ) throws IOException {
340- return hasSingleField (
341- bytes ,
342- fieldDescriptor .getFieldNumber (),
343- fieldDescriptor .getEncodingType ().equals (EncodingType .LIST ) && isPackable (fieldDescriptor ));
344- }
345-
346- static boolean hasSingleField (ByteString bytes , int targetFieldNumber , boolean isPackableList )
347- throws IOException {
331+ int targetFieldNumber = fieldDescriptor .getFieldNumber ();
332+ boolean isPackableList =
333+ fieldDescriptor .getEncodingType ().equals (EncodingType .LIST ) && isPackable (fieldDescriptor );
348334 CodedInputStream inputStream = bytes .newCodedInput ();
349335 for (int tag = inputStream .readTag (); tag != 0 ; tag = inputStream .readTag ()) {
350336 int fieldNumber = WireFormat .getTagFieldNumber (tag );
@@ -371,48 +357,127 @@ static boolean hasSingleField(ByteString bytes, int targetFieldNumber, boolean i
371357 }
372358
373359 /**
374- * Decodes every known field in {@code bytes}, keyed by field name. Each value must be passed to
375- * {@link #resolveFieldValue} to obtain its CEL value.
360+ * Selects {@code field} from {@code bytes}, decoding it by the type information in {@code field}
361+ * for fields missing from the descriptor pool. Returns the field's default value if it's absent.
362+ * Throws {@link CelAttributeNotFoundException} if {@code field} has no type code.
376363 */
377- ImmutableMap <String , Object > readAllFields (ByteString bytes , String protoTypeName )
378- throws IOException {
379- MessageLiteDescriptor messageDescriptor = descriptorPool .getDescriptorOrThrow (protoTypeName );
380- if (bytes .isEmpty ()) {
381- return ImmutableMap .of ();
364+ Object selectByFieldNumber (ByteString bytes , SelectField field ) throws IOException {
365+ if (field .typeCode () == SelectField .NO_TYPE_CODE ) {
366+ throw CelAttributeNotFoundException .forFieldResolution (field .fieldName ());
367+ }
368+ Object fieldValue = readFieldByNumber (bytes , field );
369+ if (fieldValue != null ) {
370+ return fieldValue ;
382371 }
383- return readAllFields (bytes .newCodedInput (), messageDescriptor );
372+ if (field .defaultValue () != null ) {
373+ return field .defaultValue ();
374+ }
375+ return getDefaultCelValue (newFieldDescriptor (field ));
376+ }
377+
378+ /**
379+ * Finds {@code field} in {@code bytes}, decoding it by the type information in {@code field} for
380+ * fields missing from the descriptor pool. A field without a type code is decoded as a message.
381+ */
382+ Optional <Object > findByFieldNumber (ByteString bytes , SelectField field ) throws IOException {
383+ return Optional .ofNullable (readFieldByNumber (bytes , field ));
384+ }
385+
386+ /** Returns whether {@code field} is present in {@code bytes}. */
387+ boolean hasFieldByNumber (ByteString bytes , SelectField field ) throws IOException {
388+ return hasSingleField (bytes , newFieldDescriptor (field ));
384389 }
385390
386- private ImmutableMap <String , Object > readAllFields (
387- CodedInputStream inputStream , MessageLiteDescriptor messageDescriptor ) throws IOException {
388- Map <String , Object > fieldValues = new LinkedHashMap <>();
391+ private @ Nullable Object readFieldByNumber (ByteString bytes , SelectField field )
392+ throws IOException {
393+ FieldLiteDescriptor fieldDescriptor = newFieldDescriptor (field );
394+ SelectField .MapEntrySpec mapEntrySpec = field .mapEntrySpec ();
395+ if (mapEntrySpec == null ) {
396+ return readSingleField (bytes , fieldDescriptor );
397+ }
398+ // The map entry type has no descriptor either, so its key and value are described by the spec.
399+ FieldLiteDescriptor keyDescriptor =
400+ newFieldDescriptor (
401+ MAP_KEY_FIELD_NUMBER ,
402+ MAP_KEY_FIELD_NAME ,
403+ EncodingType .SINGULAR ,
404+ mapEntrySpec .keyTypeCode (),
405+ /* protoTypeName= */ "" );
406+ FieldLiteDescriptor valueDescriptor =
407+ newFieldDescriptor (
408+ MAP_VALUE_FIELD_NUMBER ,
409+ MAP_VALUE_FIELD_NAME ,
410+ EncodingType .SINGULAR ,
411+ mapEntrySpec .valueTypeCode (),
412+ field .protoTypeName ());
413+ CodedInputStream inputStream = bytes .newCodedInput ();
414+ Map <Object , Object > mapValues = null ;
389415 for (int tag = inputStream .readTag (); tag != 0 ; tag = inputStream .readTag ()) {
390- int tagWireType = WireFormat .getTagWireType (tag );
391- int fieldNumber = WireFormat .getTagFieldNumber (tag );
392- FieldLiteDescriptor fieldDescriptor =
393- messageDescriptor .findByFieldNumber (fieldNumber ).orElse (null );
394- if (fieldDescriptor == null ) {
416+ if (WireFormat .getTagFieldNumber (tag ) != field .fieldNumber ()) {
395417 skipWireField (tag , inputStream );
396418 continue ;
397419 }
398-
399- String fieldName = fieldDescriptor .getFieldName ();
400- Object fieldValue =
401- readFieldValue (tagWireType , inputStream , fieldDescriptor , fieldValues .get (fieldName ));
402- if (fieldValue != null ) {
403- fieldValues .put (fieldName , fieldValue );
404- }
405- }
406-
407- fieldValues .replaceAll ((fieldName , fieldValue ) -> finalizeFieldValue (fieldValue ));
408- return ImmutableMap .copyOf (fieldValues );
409- }
410-
411- /**
412- * Returns the CEL value of a field decoded by {@link #readAllFields}, completing any conversion
413- * that was deferred.
414- */
415- Object resolveFieldValue (Object fieldValue ) {
420+ mapValues =
421+ readMapField (
422+ WireFormat .getTagWireType (tag ),
423+ inputStream ,
424+ fieldDescriptor ,
425+ keyDescriptor ,
426+ valueDescriptor ,
427+ mapValues );
428+ }
429+ return mapValues == null ? null : resolveFieldValue (finalizeFieldValue (mapValues ));
430+ }
431+
432+ /** Describes {@code field} by the type information it carries. */
433+ private static FieldLiteDescriptor newFieldDescriptor (SelectField field ) {
434+ if (field .mapEntrySpec () != null ) {
435+ // SelectField doesn't name the map entry type; its protoTypeName() is the map's value type.
436+ return newFieldDescriptor (
437+ field .fieldNumber (),
438+ field .fieldName (),
439+ EncodingType .MAP ,
440+ SelectField .MESSAGE_TYPE_CODE ,
441+ /* protoTypeName= */ "" );
442+ }
443+ if (field .typeCode () == SelectField .NO_TYPE_CODE ) {
444+ // Only presence tests omit the type code. Their presence check doesn't depend on the type,
445+ // and the fields they navigate through are always messages.
446+ return newFieldDescriptor (
447+ field .fieldNumber (),
448+ field .fieldName (),
449+ EncodingType .SINGULAR ,
450+ SelectField .MESSAGE_TYPE_CODE ,
451+ /* protoTypeName= */ "" );
452+ }
453+ return newFieldDescriptor (
454+ field .fieldNumber (),
455+ field .fieldName (),
456+ field .defaultValue () instanceof List ? EncodingType .LIST : EncodingType .SINGULAR ,
457+ field .typeCode (),
458+ field .protoTypeName ());
459+ }
460+
461+ private static FieldLiteDescriptor newFieldDescriptor (
462+ int fieldNumber ,
463+ String fieldName ,
464+ EncodingType encodingType ,
465+ int typeCode ,
466+ String protoTypeName ) {
467+ FieldLiteDescriptor .Type protoFieldType = FieldLiteDescriptor .Type .forNumber (typeCode );
468+ return new FieldLiteDescriptor (
469+ fieldNumber ,
470+ fieldName ,
471+ // FieldLiteDescriptor.JavaType's constants match WireFormat.JavaType's by name.
472+ JavaType .valueOf (protoFieldType .toWireFormatFieldType ().getJavaType ().name ()),
473+ encodingType ,
474+ protoFieldType ,
475+ /* isPacked= */ false ,
476+ protoTypeName );
477+ }
478+
479+ /** Returns the CEL value of a scanned field, completing any conversion that was deferred. */
480+ private Object resolveFieldValue (Object fieldValue ) {
416481 if (fieldValue instanceof DeferredConversion ) {
417482 return toRuntimeValue (((DeferredConversion ) fieldValue ).value );
418483 }
@@ -520,18 +585,38 @@ private Object readSingularField(
520585 return repeatedValues ;
521586 }
522587
588+ private Map <Object , Object > readMapField (
589+ int tagWireType ,
590+ CodedInputStream inputStream ,
591+ FieldLiteDescriptor fieldDescriptor ,
592+ @ Nullable Object existingValue )
593+ throws IOException {
594+ MessageLiteDescriptor entryDescriptor =
595+ descriptorPool .getDescriptorOrThrow (fieldDescriptor .getFieldProtoTypeName ());
596+ return readMapField (
597+ tagWireType ,
598+ inputStream ,
599+ fieldDescriptor ,
600+ entryDescriptor .getByFieldNameOrThrow (MAP_KEY_FIELD_NAME ),
601+ entryDescriptor .getByFieldNameOrThrow (MAP_VALUE_FIELD_NAME ),
602+ existingValue );
603+ }
604+
523605 // Safe because MAP fields only ever store a LinkedHashMap as their accumulated value.
524606 @ SuppressWarnings ("unchecked" )
525607 private Map <Object , Object > readMapField (
526608 int tagWireType ,
527609 CodedInputStream inputStream ,
528610 FieldLiteDescriptor fieldDescriptor ,
611+ FieldLiteDescriptor keyDescriptor ,
612+ FieldLiteDescriptor valueDescriptor ,
529613 @ Nullable Object existingValue )
530614 throws IOException {
531615 checkWireType (tagWireType , fieldDescriptor );
532616 Map <Object , Object > mapValues =
533617 existingValue != null ? (Map <Object , Object >) existingValue : new LinkedHashMap <>();
534- Map .Entry <Object , Object > mapEntry = readSingleMapEntry (inputStream , fieldDescriptor );
618+ Map .Entry <Object , Object > mapEntry =
619+ readSingleMapEntry (inputStream , keyDescriptor , valueDescriptor );
535620 mapValues .put (mapEntry .getKey (), mapEntry .getValue ());
536621 return mapValues ;
537622 }
@@ -565,6 +650,7 @@ private static boolean isPackable(FieldLiteDescriptor fieldDescriptor) {
565650 return fieldDescriptor .getProtoFieldType ().toWireFormatFieldType ().isPackable ();
566651 }
567652
653+ @ VisibleForTesting
568654 static void skipWireField (int tag , CodedInputStream inputStream ) throws IOException {
569655 int tagWireType = WireFormat .getTagWireType (tag );
570656 switch (tagWireType ) {
@@ -582,25 +668,6 @@ static void skipWireField(int tag, CodedInputStream inputStream) throws IOExcept
582668 }
583669 }
584670
585- static Object readUnknownField (int tagWireType , CodedInputStream inputStream ) throws IOException {
586- switch (tagWireType ) {
587- case WireFormat .WIRETYPE_VARINT :
588- return inputStream .readInt64 ();
589- case WireFormat .WIRETYPE_FIXED64 :
590- return inputStream .readFixed64 ();
591- case WireFormat .WIRETYPE_LENGTH_DELIMITED :
592- return inputStream .readBytes ();
593- case WireFormat .WIRETYPE_FIXED32 :
594- return inputStream .readFixed32 ();
595- case WireFormat .WIRETYPE_START_GROUP :
596- case WireFormat .WIRETYPE_END_GROUP :
597- // TODO: Support groups
598- throw new UnsupportedOperationException ("Groups are not supported" );
599- default :
600- throw new IllegalArgumentException ("Unknown wire type: " + tagWireType );
601- }
602- }
603-
604671 /**
605672 * A field value holding well-known type messages, whose conversion to CEL values is deferred
606673 * until {@link #resolveFieldValue}.
0 commit comments