Fix repeated string serialization for JSON. (#6888)

Co-authored-by: Jack Berg <jberg@newrelic.com>
This commit is contained in:
Josh Suereth 2024-11-25 10:19:35 -05:00 committed by GitHub
parent 6487ac2976
commit 8a3329be4d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 55 additions and 7 deletions

View File

@ -122,6 +122,22 @@ final class JsonSerializer extends Serializer {
generator.writeString(string);
}
@Override
public void writeRepeatedString(ProtoFieldInfo field, byte[][] utf8Bytes) throws IOException {
generator.writeArrayFieldStart(field.getJsonName());
for (byte[] value : utf8Bytes) {
// Marshalers encoded String into UTF-8 bytes to optimize for binary serialization where
// we are able to avoid the encoding process happening twice, one for size computation and one
// for actual writing. JsonGenerator actually has a writeUTF8String that would be able to
// accept
// this, but it only works when writing to an OutputStream, but not to a String like we do for
// writing to logs. It's wasteful to take a String, convert it to bytes, and convert back to
// the same String but we can see if this can be improved in the future.
generator.writeString(new String(value, StandardCharsets.UTF_8));
}
generator.writeEndArray();
}
@Override
public void writeBytes(ProtoFieldInfo field, byte[] value) throws IOException {
generator.writeBinaryField(field.getJsonName(), value);

View File

@ -110,6 +110,16 @@ public final class MarshalerUtil {
return size;
}
/** Returns the size of a repeated string field. */
@SuppressWarnings("AvoidObjectArrays")
public static int sizeRepeatedString(ProtoFieldInfo field, byte[][] utf8Bytes) {
int size = 0;
for (byte[] i : utf8Bytes) {
size += MarshalerUtil.sizeBytes(field, i);
}
return size;
}
/**
* Returns the size of a repeated uint64 field.
*

View File

@ -163,6 +163,13 @@ final class ProtoSerializer extends Serializer implements AutoCloseable {
StatelessMarshalerUtil.writeUtf8(output, string, utf8Length, context);
}
@Override
public void writeRepeatedString(ProtoFieldInfo field, byte[][] utf8Bytes) throws IOException {
for (byte[] value : utf8Bytes) {
writeString(field, value);
}
}
@Override
public void writeBytes(ProtoFieldInfo field, byte[] value) throws IOException {
output.writeUInt32NoTag(field.getTag());

View File

@ -220,6 +220,18 @@ public abstract class Serializer implements AutoCloseable {
writeString(field, utf8Bytes);
}
/**
* Serializes a protobuf {@code repeated string} field. {@code utf8Bytes} is the UTF8 encoded
* bytes of the strings to serialize.
*/
@SuppressWarnings("AvoidObjectArrays")
public void serializeRepeatedString(ProtoFieldInfo field, byte[][] utf8Bytes) throws IOException {
if (utf8Bytes.length == 0) {
return;
}
writeRepeatedString(field, utf8Bytes);
}
/**
* Serializes a protobuf {@code string} field. {@code string} is the value to be serialized and
* {@code utf8Length} is the length of the string after it is encoded in UTF8. This method reads
@ -246,6 +258,11 @@ public abstract class Serializer implements AutoCloseable {
ProtoFieldInfo field, String string, int utf8Length, MarshalerContext context)
throws IOException;
/** Writes a protobuf {@code repeated string} field, even if it matches the default value. */
@SuppressWarnings("AvoidObjectArrays")
public abstract void writeRepeatedString(ProtoFieldInfo field, byte[][] utf8Bytes)
throws IOException;
/** Serializes a protobuf {@code bytes} field. */
public void serializeBytes(ProtoFieldInfo field, byte[] value) throws IOException {
if (value.length == 0) {

View File

@ -149,9 +149,7 @@ final class ProfileMarshaler extends MarshalerWithSize {
output.serializeRepeatedMessage(Profile.ATTRIBUTE_TABLE, attributeMarshalers);
output.serializeRepeatedMessage(Profile.ATTRIBUTE_UNITS, attributeUnitMarshalers);
output.serializeRepeatedMessage(Profile.LINK_TABLE, linkMarshalers);
for (byte[] i : stringTable) {
output.serializeString(Profile.STRING_TABLE, i);
}
output.serializeRepeatedString(Profile.STRING_TABLE, stringTable);
output.serializeInt64(Profile.DROP_FRAMES, dropFrames);
output.serializeInt64(Profile.KEEP_FRAMES, keepFrames);
output.serializeInt64(Profile.TIME_NANOS, timeNanos);
@ -192,9 +190,7 @@ final class ProfileMarshaler extends MarshalerWithSize {
size += MarshalerUtil.sizeRepeatedMessage(Profile.ATTRIBUTE_TABLE, attributeMarshalers);
size += MarshalerUtil.sizeRepeatedMessage(Profile.ATTRIBUTE_UNITS, attributeUnitMarshalers);
size += MarshalerUtil.sizeRepeatedMessage(Profile.LINK_TABLE, linkMarshalers);
for (byte[] i : stringTable) {
size += MarshalerUtil.sizeBytes(Profile.STRING_TABLE, i);
}
size += MarshalerUtil.sizeRepeatedString(Profile.STRING_TABLE, stringTable);
size += MarshalerUtil.sizeInt64(Profile.DROP_FRAMES, dropFrames);
size += MarshalerUtil.sizeInt64(Profile.KEEP_FRAMES, keepFrames);
size += MarshalerUtil.sizeInt64(Profile.TIME_NANOS, timeNanos);

View File

@ -275,7 +275,7 @@ public class ProfilesRequestMarshalerTest {
Attributes.empty(),
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList(),
listOf("foo", "bar"),
3,
4,
5,
@ -304,6 +304,8 @@ public class ProfilesRequestMarshalerTest {
.AGGREGATION_TEMPORALITY_CUMULATIVE)
.build())
.addAllComment(listOf(8L, 9L))
.addStringTable("foo")
.addStringTable("bar")
.setDefaultSampleType(10);
}