Skip to content

Commit ce0449c

Browse files
l46kokcopybara-github
authored andcommitted
Pass over CodedInputStream once and lazily slice ByteString submessages
PiperOrigin-RevId: 995328207
1 parent 19bc020 commit ce0449c

4 files changed

Lines changed: 434 additions & 77 deletions

File tree

‎common/src/main/java/dev/cel/common/values/ProtoLiteCelValueConverter.java‎

Lines changed: 120 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import java.util.Map;
4747
import java.util.Optional;
4848
import java.util.TreeMap;
49+
import org.jspecify.annotations.Nullable;
4950

5051
/**
5152
* {@code ProtoLiteCelValueConverter} handles bidirectional conversion between native Java and
@@ -60,8 +61,8 @@
6061
@Immutable
6162
@Internal
6263
public final class ProtoLiteCelValueConverter extends BaseProtoCelValueConverter {
63-
static final String MAP_KEY_FIELD_NAME = "key";
64-
static final String MAP_VALUE_FIELD_NAME = "value";
64+
private static final String MAP_KEY_FIELD_NAME = "key";
65+
private static final String MAP_VALUE_FIELD_NAME = "value";
6566

6667
private final CelLiteDescriptorPool descriptorPool;
6768

@@ -134,29 +135,58 @@ private static Object readFixed64BitField(
134135
}
135136

136137
private Object readLengthDelimitedField(
137-
CodedInputStream inputStream, FieldLiteDescriptor fieldDescriptor) throws IOException {
138+
CodedInputStream inputStream,
139+
FieldLiteDescriptor fieldDescriptor,
140+
@Nullable Object existingValue)
141+
throws IOException {
138142
FieldLiteDescriptor.Type fieldType = fieldDescriptor.getProtoFieldType();
139143

140144
switch (fieldType) {
141145
case BYTES:
142146
return inputStream.readBytes();
143147
case MESSAGE:
144-
String fieldProtoTypeName = fieldDescriptor.getFieldProtoTypeName();
145-
MessageLiteDescriptor descriptor =
146-
descriptorPool.findDescriptor(fieldProtoTypeName).orElse(null);
147-
if (descriptor == null) {
148-
return RawProtoMessageLiteValue.create(inputStream.readBytes(), fieldProtoTypeName, this);
149-
}
150-
MessageLite.Builder builder = descriptor.newMessageBuilder();
151-
inputStream.readMessage(builder, ExtensionRegistryLite.getEmptyRegistry());
152-
return builder.build();
148+
return mergeOrReadMessageField(
149+
inputStream.readBytes(), fieldDescriptor.getFieldProtoTypeName(), existingValue);
153150
case STRING:
154151
return inputStream.readStringRequireUtf8();
155152
default:
156153
throw new IllegalStateException("Unexpected field type: " + fieldType);
157154
}
158155
}
159156

157+
private Object readMessageField(ByteString bytes, String fieldProtoTypeName) {
158+
return mergeOrReadMessageField(bytes, fieldProtoTypeName, /* existingValue= */ null);
159+
}
160+
161+
private Object mergeOrReadMessageField(
162+
ByteString bytes, String fieldProtoTypeName, @Nullable Object existingValue) {
163+
MessageLiteDescriptor descriptor =
164+
descriptorPool.findDescriptor(fieldProtoTypeName).orElse(null);
165+
if (descriptor == null) {
166+
if (existingValue instanceof RawProtoMessageLiteValue) {
167+
bytes = ((RawProtoMessageLiteValue) existingValue).toByteString().concat(bytes);
168+
}
169+
return RawProtoMessageLiteValue.create(bytes, fieldProtoTypeName, this);
170+
}
171+
WellKnownProto wellKnownProto = WellKnownProto.getByTypeName(fieldProtoTypeName).orElse(null);
172+
if (isStructLike(wellKnownProto)) {
173+
if (existingValue instanceof ProtoMessageLiteValue) {
174+
bytes = ((ProtoMessageLiteValue) existingValue).toByteString().concat(bytes);
175+
}
176+
return ProtoMessageLiteValue.create(bytes, fieldProtoTypeName, this);
177+
}
178+
if (existingValue instanceof MessageLite) {
179+
return mergeMessageLite(((MessageLite) existingValue).toBuilder(), bytes, fieldProtoTypeName);
180+
}
181+
return parseMessageLite(bytes, descriptor);
182+
}
183+
184+
// Unlike other WellKnownProtos (which unbox to CEL scalars/containers), FieldMask is
185+
// represented as a standard struct message so field selection (e.g., mask.paths) works.
186+
private static boolean isStructLike(@Nullable WellKnownProto wellKnownProto) {
187+
return wellKnownProto == null || wellKnownProto == WellKnownProto.FIELD_MASK;
188+
}
189+
160190
Object getDefaultCelValue(String protoTypeName, String fieldName) {
161191
MessageLiteDescriptor messageDescriptor = descriptorPool.getDescriptorOrThrow(protoTypeName);
162192
return getDefaultCelValue(messageDescriptor.getByFieldNameOrThrow(fieldName));
@@ -172,6 +202,28 @@ Optional<FieldLiteDescriptor> findFieldDescriptor(String protoTypeName, int fiel
172202
.flatMap(desc -> desc.findByFieldNumber(fieldNumber));
173203
}
174204

205+
MessageLite parseMessageLite(ByteString bytes, String protoTypeName) {
206+
MessageLiteDescriptor descriptor = descriptorPool.getDescriptorOrThrow(protoTypeName);
207+
return parseMessageLite(bytes, descriptor);
208+
}
209+
210+
private static MessageLite parseMessageLite(ByteString bytes, MessageLiteDescriptor descriptor) {
211+
if (bytes.isEmpty()) {
212+
return descriptor.newMessageBuilder().getDefaultInstanceForType();
213+
}
214+
return mergeMessageLite(descriptor.newMessageBuilder(), bytes, descriptor.getProtoTypeName());
215+
}
216+
217+
private static MessageLite mergeMessageLite(
218+
MessageLite.Builder builder, ByteString bytes, String protoTypeName) {
219+
try {
220+
return builder.mergeFrom(bytes, ExtensionRegistryLite.getEmptyRegistry()).build();
221+
} catch (IOException e) {
222+
throw new IllegalArgumentException(
223+
"Failed to decode proto message of type: " + protoTypeName, e);
224+
}
225+
}
226+
175227
Optional<Object> tryDecodeProtoMessage(ByteString bytes, String protoTypeName) {
176228
return descriptorPool
177229
.findDescriptor(protoTypeName)
@@ -180,14 +232,11 @@ Optional<Object> tryDecodeProtoMessage(ByteString bytes, String protoTypeName) {
180232

181233
private Object decodeProtoMessage(
182234
ByteString bytes, String protoTypeName, MessageLiteDescriptor descriptor) {
183-
try {
184-
MessageLite.Builder builder =
185-
descriptor.newMessageBuilder().mergeFrom(bytes, ExtensionRegistryLite.getEmptyRegistry());
186-
return toRuntimeValue(builder.build(), descriptor);
187-
} catch (IOException e) {
188-
throw new IllegalArgumentException(
189-
"Failed to decode proto message of type: " + protoTypeName, e);
235+
WellKnownProto wellKnownProto = WellKnownProto.getByTypeName(protoTypeName).orElse(null);
236+
if (isStructLike(wellKnownProto)) {
237+
return ProtoMessageLiteValue.create(bytes, protoTypeName, this);
190238
}
239+
return fromWellKnownProto(parseMessageLite(bytes, descriptor), checkNotNull(wellKnownProto));
191240
}
192241

193242
@Override
@@ -209,12 +258,11 @@ public Object toRuntimeValue(Object value) {
209258
private Object toRuntimeValue(MessageLite msg, MessageLiteDescriptor descriptor) {
210259
WellKnownProto wellKnownProto =
211260
WellKnownProto.getByTypeName(descriptor.getProtoTypeName()).orElse(null);
212-
213-
if (wellKnownProto == null || wellKnownProto == WellKnownProto.FIELD_MASK) {
261+
if (isStructLike(wellKnownProto)) {
214262
return ProtoMessageLiteValue.create(msg, descriptor.getProtoTypeName(), this);
215263
}
216264

217-
return fromWellKnownProto(msg, wellKnownProto);
265+
return fromWellKnownProto(msg, checkNotNull(wellKnownProto));
218266
}
219267

220268
private Object getDefaultValue(FieldLiteDescriptor fieldDescriptor) {
@@ -260,12 +308,7 @@ private Object getScalarDefaultValue(FieldLiteDescriptor fieldDescriptor) {
260308
if (WellKnownProto.isWrapperType(fieldProtoTypeName)) {
261309
return NullValue.NULL_VALUE;
262310
}
263-
MessageLiteDescriptor descriptor =
264-
descriptorPool.findDescriptor(fieldProtoTypeName).orElse(null);
265-
if (descriptor == null) {
266-
return RawProtoMessageLiteValue.create(ByteString.EMPTY, fieldProtoTypeName, this);
267-
}
268-
return descriptor.newMessageBuilder().build();
311+
return readMessageField(ByteString.EMPTY, fieldProtoTypeName);
269312
}
270313
throw new IllegalStateException("Unexpected java type: " + type);
271314
}
@@ -286,7 +329,7 @@ private Map.Entry<Object, Object> readSingleMapEntry(
286329
CodedInputStream inputStream, FieldLiteDescriptor fieldDescriptor) throws IOException {
287330
String entryTypeName = fieldDescriptor.getFieldProtoTypeName();
288331
ImmutableMap<String, Object> singleMapEntry =
289-
readAllFields(inputStream.readByteArray(), entryTypeName).values();
332+
readAllFields(inputStream.readBytes(), entryTypeName).values();
290333
Object key = singleMapEntry.get(MAP_KEY_FIELD_NAME);
291334
if (key == null) {
292335
key = getDefaultCelValue(entryTypeName, MAP_KEY_FIELD_NAME);
@@ -299,25 +342,32 @@ private Map.Entry<Object, Object> readSingleMapEntry(
299342
return new AbstractMap.SimpleEntry<>(key, value);
300343
}
301344

302-
MessageFields readAllFields(byte[] bytes, String protoTypeName) throws IOException {
345+
MessageFields readAllFields(ByteString bytes, String protoTypeName) throws IOException {
303346
MessageLiteDescriptor messageDescriptor = descriptorPool.getDescriptorOrThrow(protoTypeName);
304-
CodedInputStream inputStream = CodedInputStream.newInstance(bytes);
347+
if (bytes.isEmpty()) {
348+
return MessageFields.EMPTY;
349+
}
350+
return readAllFields(bytes.newCodedInput(), messageDescriptor);
351+
}
305352

306-
Multimap<Integer, Object> unknownFields =
307-
Multimaps.newMultimap(new TreeMap<>(), ArrayList::new);
308-
ImmutableMap.Builder<String, Object> fieldValues = ImmutableMap.builder();
309-
Map<Integer, List<Object>> repeatedFieldValues = new LinkedHashMap<>();
310-
Map<Integer, Map<Object, Object>> mapFieldValues = new LinkedHashMap<>();
353+
private MessageFields readAllFields(
354+
CodedInputStream inputStream, MessageLiteDescriptor messageDescriptor) throws IOException {
355+
Multimap<Integer, Object> unknownFields = null;
356+
Map<String, Object> fieldValues = new LinkedHashMap<>();
311357
for (int tag = inputStream.readTag(); tag != 0; tag = inputStream.readTag()) {
312358
int tagWireType = WireFormat.getTagWireType(tag);
313359
int fieldNumber = WireFormat.getTagFieldNumber(tag);
314360
FieldLiteDescriptor fieldDescriptor =
315361
messageDescriptor.findByFieldNumber(fieldNumber).orElse(null);
316362
if (fieldDescriptor == null) {
363+
if (unknownFields == null) {
364+
unknownFields = Multimaps.newMultimap(new TreeMap<>(), ArrayList::new);
365+
}
317366
unknownFields.put(fieldNumber, readUnknownField(tagWireType, inputStream));
318367
continue;
319368
}
320369

370+
String fieldName = fieldDescriptor.getFieldName();
321371
Object payload;
322372
switch (tagWireType) {
323373
case WireFormat.WIRETYPE_VARINT:
@@ -347,18 +397,24 @@ MessageFields readAllFields(byte[] bytes, String protoTypeName) throws IOExcepti
347397
+ protoFieldType);
348398
}
349399

350-
payload = readLengthDelimitedField(inputStream, fieldDescriptor);
400+
payload =
401+
readLengthDelimitedField(
402+
inputStream, fieldDescriptor, /* existingValue= */ null);
351403
}
352404
break;
353405
case MAP:
406+
// Safe because MAP fields only ever store a LinkedHashMap in fieldValues.
407+
@SuppressWarnings("unchecked")
354408
Map<Object, Object> fieldMap =
355-
mapFieldValues.computeIfAbsent(fieldNumber, (unused) -> new LinkedHashMap<>());
409+
(Map<Object, Object>)
410+
fieldValues.computeIfAbsent(fieldName, (unused) -> new LinkedHashMap<>());
356411
Map.Entry<Object, Object> mapEntry = readSingleMapEntry(inputStream, fieldDescriptor);
357412
fieldMap.put(mapEntry.getKey(), mapEntry.getValue());
358-
payload = fieldMap;
359-
break;
413+
continue;
360414
default:
361-
payload = readLengthDelimitedField(inputStream, fieldDescriptor);
415+
payload =
416+
readLengthDelimitedField(
417+
inputStream, fieldDescriptor, fieldValues.get(fieldName));
362418
break;
363419
}
364420
break;
@@ -371,30 +427,27 @@ MessageFields readAllFields(byte[] bytes, String protoTypeName) throws IOExcepti
371427
}
372428

373429
if (fieldDescriptor.getEncodingType().equals(EncodingType.LIST)) {
374-
String fieldName = fieldDescriptor.getFieldName();
375-
List<Object> repeatedValues =
376-
repeatedFieldValues.computeIfAbsent(fieldNumber, (unused) -> new ArrayList<>());
377-
378430
if (payload instanceof Collection) {
379-
repeatedValues.addAll((Collection<?>) payload);
431+
Collection<?> elements = (Collection<?>) payload;
432+
if (!elements.isEmpty()) {
433+
getOrCreateRepeatedList(fieldValues, fieldName).addAll(elements);
434+
}
380435
} else {
381-
repeatedValues.add(payload);
382-
}
383-
if (!repeatedValues.isEmpty()) {
384-
fieldValues.put(fieldName, repeatedValues);
436+
getOrCreateRepeatedList(fieldValues, fieldName).add(payload);
385437
}
386438
} else {
387-
fieldValues.put(fieldDescriptor.getFieldName(), payload);
439+
fieldValues.put(fieldName, payload);
388440
}
389441
}
390442

391-
// Protobuf encoding follows a "last one wins" semantics. This means for duplicated fields,
392-
// we accept the last value encountered.
393-
return MessageFields.create(fieldValues.buildKeepingLast(), unknownFields);
443+
return MessageFields.create(ImmutableMap.copyOf(fieldValues), unknownFields);
394444
}
395445

396-
MessageFields readMessageFields(MessageLite msg, String protoTypeName) throws IOException {
397-
return readAllFields(msg.toByteArray(), protoTypeName);
446+
// Safe because LIST fields only ever store an ArrayList in fieldValues.
447+
@SuppressWarnings("unchecked")
448+
private static List<Object> getOrCreateRepeatedList(
449+
Map<String, Object> fieldValues, String fieldName) {
450+
return (List<Object>) fieldValues.computeIfAbsent(fieldName, (unused) -> new ArrayList<>());
398451
}
399452

400453
static Object readUnknownField(int tagWireType, CodedInputStream inputStream) throws IOException {
@@ -421,19 +474,26 @@ static Object readUnknownField(int tagWireType, CodedInputStream inputStream) th
421474
@Immutable
422475
@SuppressWarnings("Immutable") // Safe immutable fields
423476
abstract static class MessageFields {
477+
static final MessageFields EMPTY =
478+
new AutoValue_ProtoLiteCelValueConverter_MessageFields(
479+
ImmutableMap.of(), ImmutableListMultimap.of());
424480

425481
abstract ImmutableMap<String, Object> values();
426482

427483
abstract ImmutableListMultimap<Integer, Object> unknowns();
428484

429-
static MessageFields create(
430-
ImmutableMap<String, Object> fieldValues, Multimap<Integer, Object> unknownFields) {
485+
private static MessageFields create(
486+
ImmutableMap<String, Object> fieldValues,
487+
@Nullable Multimap<Integer, Object> unknownFields) {
431488
return new AutoValue_ProtoLiteCelValueConverter_MessageFields(
432-
fieldValues, ImmutableListMultimap.copyOf(unknownFields));
489+
fieldValues,
490+
unknownFields == null
491+
? ImmutableListMultimap.of()
492+
: ImmutableListMultimap.copyOf(unknownFields));
433493
}
434494
}
435495

436496
private ProtoLiteCelValueConverter(CelLiteDescriptorPool celLiteDescriptorPool) {
437-
this.descriptorPool = celLiteDescriptorPool;
497+
this.descriptorPool = checkNotNull(celLiteDescriptorPool);
438498
}
439499
}

0 commit comments

Comments
 (0)