Skip to content

Commit

Permalink
Makes it possible to implement a protobuf3 span encoder
Browse files Browse the repository at this point in the history
While we haven't yet defined a proto3 format, this allows one to be
defined. This also allows us to refine how stackdriver (or any future
translated format) works in zipkin reporter.

Implementation is relatively simple: this assumes a buffer of spans is
analogous to a proto3 repeated field, and that the field name itself is
ordinal <=127.

Fixes openzipkin#1942
  • Loading branch information
Adrian Cole authored and abesto committed Sep 10, 2019
1 parent fb711b7 commit 075af99
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 9 deletions.
41 changes: 40 additions & 1 deletion zipkin2/src/main/java/zipkin2/codec/Encoding.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2015-2017 The OpenZipkin Authors
* Copyright 2015-2018 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
Expand Down Expand Up @@ -31,6 +31,45 @@ public enum Encoding {
}
return sizeInBytes;
}
},
/**
* Repeated (type 2) fields are length-prefixed, the value is a concatenation with no additional
* overhead.
*
* <p>See https://developers.google.com/protocol-buffers/docs/encoding#optional
*/
PROTO3 {
/** Returns the size of a length-prefixed field in a protobuf message */
@Override public int listSizeInBytes(int encodedSizeInBytes) {
return 1 // assumes field number <= 127
+ unsignedVarint(encodedSizeInBytes) // bytes to encode the length
+ encodedSizeInBytes; // the actual length
}

/** Returns a concatenation of length-prefixed size for each value */
@Override public int listSizeInBytes(List<byte[]> values) {
int sizeInBytes = 0;
for (int i = 0, length = values.size(); i < length; ) {
sizeInBytes += listSizeInBytes(values.get(i++).length);
}
return sizeInBytes;
}

/**
* A base 128 varint encodes 7 bits at a time, this checks how many bytes are needed to
* represent the value.
*
* <p>See https://developers.google.com/protocol-buffers/docs/encoding#varints
*
* <p>This logic is the same as {@code com.squareup.wire.WireOutput.varint32Size} v2.3.0
*/
int unsignedVarint(int value) {
if ((value & (0xffffffff << 7)) == 0) return 1;
if ((value & (0xffffffff << 14)) == 0) return 2;
if ((value & (0xffffffff << 21)) == 0) return 3;
if ((value & (0xffffffff << 28)) == 0) return 4;
return 5;
}
};

/** Like {@link #listSizeInBytes(List)}, except for a single element. */
Expand Down
51 changes: 43 additions & 8 deletions zipkin2/src/test/java/zipkin2/codec/EncodingTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2015-2017 The OpenZipkin Authors
* Copyright 2015-2018 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
Expand All @@ -13,7 +13,6 @@
*/
package zipkin2.codec;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
Expand All @@ -22,13 +21,13 @@

public class EncodingTest {

@Test public void emptyList_json() throws IOException {
@Test public void emptyList_json() {
List<byte[]> encoded = Arrays.asList();
assertThat(Encoding.JSON.listSizeInBytes(encoded))
.isEqualTo(2 /* [] */);
}

@Test public void singletonList_json() throws IOException {
@Test public void singletonList_json() {
List<byte[]> encoded = Arrays.asList(new byte[10]);

assertThat(Encoding.JSON.listSizeInBytes(encoded.get(0).length))
Expand All @@ -37,10 +36,46 @@ public class EncodingTest {
.isEqualTo(2 /* [] */ + 10);
}


@Test public void multiItemList_json() throws IOException {
List<byte[]> encoded = Arrays.asList(new byte[3], new byte[4], new byte[5]);
@Test public void multiItemList_json() {
List<byte[]> encoded = Arrays.asList(new byte[3], new byte[4], new byte[128]);
assertThat(Encoding.JSON.listSizeInBytes(encoded))
.isEqualTo(2 /* [] */ + 3 + 1 /* , */ + 4 + 1 /* , */ + 5);
.isEqualTo(2 /* [] */ + 3 + 1 /* , */ + 4 + 1 /* , */ + 128);
}

@Test public void emptyList_proto3() {
List<byte[]> encoded = Arrays.asList();
assertThat(Encoding.PROTO3.listSizeInBytes(encoded))
.isEqualTo(0);
}

@Test public void singletonList_proto3() {
List<byte[]> encoded = Arrays.asList(new byte[10]);

assertThat(Encoding.PROTO3.listSizeInBytes(encoded.get(0).length))
.isEqualTo(1 + 1 /* tag, length */ + 10);
assertThat(Encoding.PROTO3.listSizeInBytes(encoded))
.isEqualTo(1 + 1 /* tag, length */ + 10);
}

@Test public void multiItemList_proto3() {
List<byte[]> encoded = Arrays.asList(new byte[3], new byte[4], new byte[128]);
assertThat(Encoding.PROTO3.listSizeInBytes(encoded))
.isEqualTo(0
+ (1 + 1 /* tag, length */ + 3)
+ (1 + 1 /* tag, length */ + 4)
+ (1 + 2 /* tag, length */ + 128)
);
}

@Test public void singletonList_proto3_big() {
// Not good to have a 5MiB span, but lets test the length prefix
int bigSpan = 5 * 1024 * 1024;
assertThat(Encoding.PROTO3.listSizeInBytes(bigSpan))
.isEqualTo(1 + 4 /* tag, length */ + bigSpan);

// Terrible value in real life as this would be a 536 meg span!
int twentyNineBitNumber = 536870911;
assertThat(Encoding.PROTO3.listSizeInBytes(twentyNineBitNumber))
.isEqualTo(1 + 5 /* tag, length */ + twentyNineBitNumber);
}
}

0 comments on commit 075af99

Please sign in to comment.