Skip to content

Commit 39a704e

Browse files
l46kokcopybara-github
authored andcommitted
Avoid copying submessage bytes on each lite field selection hop
PiperOrigin-RevId: 997141133
1 parent 715d34b commit 39a704e

3 files changed

Lines changed: 39 additions & 3 deletions

File tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ private Object mergeOrReadMessageField(
138138
if (existingValue instanceof RawProtoMessageLiteValue) {
139139
bytes = ((RawProtoMessageLiteValue) existingValue).toByteString().concat(bytes);
140140
}
141-
return RawProtoMessageLiteValue.create(bytes, fieldProtoTypeName, this);
141+
return RawProtoMessageLiteValue.create(bytes, fieldProtoTypeName, this, /* isView= */ true);
142142
}
143143
WellKnownProto wellKnownProto = WellKnownProto.getByTypeName(fieldProtoTypeName).orElse(null);
144144
if (isStructLike(wellKnownProto)) {
@@ -356,6 +356,8 @@ private Map.Entry<Object, Object> readSingleMapEntry(
356356
@Nullable FieldLiteDescriptor keyDescriptor,
357357
@Nullable FieldLiteDescriptor valueDescriptor) {
358358
CodedInputStream inputStream = bytes.newCodedInput();
359+
// Submessages alias bytes; unwrapping parses or copies them, so no view outlives evaluation.
360+
inputStream.enableAliasing(true);
359361
int targetFieldNumber = fieldDescriptor.getFieldNumber();
360362
Object fieldValue = null;
361363
try {

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,23 @@ abstract class RawProtoMessageLiteValue extends StructValue<String, WireMessageL
5050

5151
abstract ProtoLiteCelValueConverter protoLiteCelValueConverter();
5252

53+
// Set when toByteString() may alias a parent message's buffer.
54+
abstract boolean isView();
55+
5356
@Override
5457
public String protoTypeName() {
5558
return celType().name();
5659
}
5760

5861
@Override
5962
public WireMessageLite value() {
60-
return this;
63+
// A view escaping evaluation would pin the parent's whole buffer, so it leaves as a copy.
64+
return isView()
65+
? create(
66+
ByteString.copyFrom(toByteString().asReadOnlyByteBuffer()),
67+
protoTypeName(),
68+
protoLiteCelValueConverter())
69+
: this;
6170
}
6271

6372
@Override
@@ -136,14 +145,23 @@ static RawProtoMessageLiteValue create(
136145
ByteString rawWireBytes,
137146
String protoTypeName,
138147
ProtoLiteCelValueConverter protoLiteCelValueConverter) {
148+
return create(rawWireBytes, protoTypeName, protoLiteCelValueConverter, /* isView= */ false);
149+
}
150+
151+
static RawProtoMessageLiteValue create(
152+
ByteString rawWireBytes,
153+
String protoTypeName,
154+
ProtoLiteCelValueConverter protoLiteCelValueConverter,
155+
boolean isView) {
139156
checkNotNull(rawWireBytes);
140157
checkNotNull(protoTypeName);
141158
checkNotNull(protoLiteCelValueConverter);
142159
return new AutoValue_RawProtoMessageLiteValue(
143160
rawWireBytes,
144161
StructTypeReference.create(
145162
protoTypeName.isEmpty() ? UNKNOWN_MESSAGE_TYPE_NAME : protoTypeName),
146-
protoLiteCelValueConverter);
163+
protoLiteCelValueConverter,
164+
isView);
147165
}
148166

149167
RawProtoMessageLiteValue() {}

‎common/src/test/java/dev/cel/common/values/RawProtoMessageLiteValueTest.java‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,22 @@ public void create_accessorsAndType() {
6262
assertThat(value.celType().name()).isEqualTo("custom.Message");
6363
}
6464

65+
@Test
66+
public void value_selectedSubmessage_returnsCopy() throws Exception {
67+
ByteString submessage = encode(out -> out.writeInt32(1, 42));
68+
RawProtoMessageLiteValue raw =
69+
RawProtoMessageLiteValue.create(
70+
encode(out -> out.writeBytes(21, submessage)), EMPTY_CONVERTER);
71+
SelectField field =
72+
SelectField.create(21L, "single_nested_message", SelectField.MESSAGE_TYPE_CODE);
73+
RawProtoMessageLiteValue selected = (RawProtoMessageLiteValue) raw.selectByFieldNumber(field);
74+
75+
WireMessageLite value = selected.value();
76+
77+
assertThat(value).isNotSameInstanceAs(selected);
78+
assertThat(value.toByteString()).isEqualTo(submessage);
79+
}
80+
6581
@Test
6682
public void create_defaultsUnknownMessageTypeName() {
6783
ByteString bytes = ByteString.copyFromUtf8("test");

0 commit comments

Comments
 (0)