Skip to content

Commit

Permalink
Improve write of arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
Hartigan committed Mar 17, 2024
1 parent 8d8e71b commit ab8bd18
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 8 deletions.
187 changes: 183 additions & 4 deletions opentelemetry-datadog/src/exporter/intern.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use indexmap::set::IndexSet;
use opentelemetry::Value;
use opentelemetry::{StringValue, Value};
use rmp::encode::{RmpWrite, ValueWriteError};
use std::hash::Hash;

Expand All @@ -18,32 +18,131 @@ impl<'a> Hash for InternValue<'a> {
Value::I64(x) => x.hash(state),
Value::String(x) => x.hash(state),
Value::F64(x) => x.to_bits().hash(state),
Value::Array(x) => std::ptr::hash(std::ptr::addr_of!(x), state),
Value::Array(a) => match a {
opentelemetry::Array::Bool(x) => x.hash(state),
opentelemetry::Array::I64(x) => x.hash(state),
opentelemetry::Array::F64(floats) => {
for f in floats {
f.to_bits().hash(state);
}

Check warning on line 27 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L21-L27

Added lines #L21 - L27 were not covered by tests
}
opentelemetry::Array::String(x) => x.hash(state),

Check warning on line 29 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L29

Added line #L29 was not covered by tests
},
},
}
}
}

impl<'a> Eq for InternValue<'a> {}

const BOOLEAN_TRUE: &str = "true";
const BOOLEAN_FALSE: &str = "false";
const LEFT_SQUARE_BRACKET: u8 = 0x5b; // '['
const RIGHT_SQUARE_BRACKET: u8 = 0x5d; // ']'
const COMMA: u8 = 0x2c; // ','
const DOUBLE_QUOTE: u8 = 0x22; // '"'
const EMPTY_ARRAY: &str = "[]";

trait WriteAsLiteral {
fn write_to(&self, buffer: &mut Vec<u8>);
}

impl WriteAsLiteral for bool {
fn write_to(&self, buffer: &mut Vec<u8>) {
buffer.extend_from_slice(if *self { BOOLEAN_TRUE } else { BOOLEAN_FALSE }.as_bytes());
}

Check warning on line 53 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L51-L53

Added lines #L51 - L53 were not covered by tests
}

impl WriteAsLiteral for i64 {
fn write_to(&self, buffer: &mut Vec<u8>) {
buffer.extend_from_slice(itoa::Buffer::new().format(*self).as_bytes());
}

Check warning on line 59 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L57-L59

Added lines #L57 - L59 were not covered by tests
}

impl WriteAsLiteral for f64 {
fn write_to(&self, buffer: &mut Vec<u8>) {
buffer.extend_from_slice(ryu::Buffer::new().format(*self).as_bytes());
}

Check warning on line 65 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L63-L65

Added lines #L63 - L65 were not covered by tests
}

impl WriteAsLiteral for StringValue {
fn write_to(&self, buffer: &mut Vec<u8>) {
buffer.push(DOUBLE_QUOTE);
buffer.extend_from_slice(self.as_str().as_bytes());
buffer.push(DOUBLE_QUOTE);
}

Check warning on line 73 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L69-L73

Added lines #L69 - L73 were not covered by tests
}

impl<'a> InternValue<'a> {
pub(crate) fn write_as_str<W: RmpWrite>(
&self,
payload: &mut W,
reusable_buffer: &mut Vec<u8>,
) -> Result<(), ValueWriteError<W::Error>> {
match self {
InternValue::RegularString(x) => rmp::encode::write_str(payload, x),
InternValue::OpenTelemetryValue(v) => match v {
Value::Bool(x) => {
rmp::encode::write_str(payload, if *x { "true" } else { "false" })
rmp::encode::write_str(payload, if *x { BOOLEAN_TRUE } else { BOOLEAN_FALSE })

Check warning on line 86 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L85-L86

Added lines #L85 - L86 were not covered by tests
}
Value::I64(x) => rmp::encode::write_str(payload, itoa::Buffer::new().format(*x)),
Value::F64(x) => rmp::encode::write_str(payload, ryu::Buffer::new().format(*x)),

Check warning on line 89 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L88-L89

Added lines #L88 - L89 were not covered by tests
Value::String(x) => rmp::encode::write_str(payload, x.as_ref()),
x => rmp::encode::write_str(payload, x.as_str().as_ref()),
Value::Array(array) => match array {
opentelemetry::Array::Bool(x) => {
Self::write_generic_array(payload, reusable_buffer, x)

Check warning on line 93 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L91-L93

Added lines #L91 - L93 were not covered by tests
}
opentelemetry::Array::I64(x) => {
Self::write_generic_array(payload, reusable_buffer, x)

Check warning on line 96 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L95-L96

Added lines #L95 - L96 were not covered by tests
}
opentelemetry::Array::F64(x) => {
Self::write_generic_array(payload, reusable_buffer, x)

Check warning on line 99 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L98-L99

Added lines #L98 - L99 were not covered by tests
}
opentelemetry::Array::String(x) => {
Self::write_generic_array(payload, reusable_buffer, x)

Check warning on line 102 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L101-L102

Added lines #L101 - L102 were not covered by tests
}
},
},
}
}

fn write_empty_array<W: RmpWrite>(payload: &mut W) -> Result<(), ValueWriteError<W::Error>> {
rmp::encode::write_str(payload, EMPTY_ARRAY)
}

Check warning on line 111 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L109-L111

Added lines #L109 - L111 were not covered by tests

fn write_buffer_as_string<W: RmpWrite>(
payload: &mut W,
reusable_buffer: &[u8],
) -> Result<(), ValueWriteError<W::Error>> {
rmp::encode::write_str_len(payload, reusable_buffer.len() as u32)?;
payload
.write_bytes(reusable_buffer)
.map_err(ValueWriteError::InvalidDataWrite)
}

Check warning on line 121 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L113-L121

Added lines #L113 - L121 were not covered by tests

fn write_generic_array<W: RmpWrite, T: WriteAsLiteral>(
payload: &mut W,
reusable_buffer: &mut Vec<u8>,
array: &Vec<T>,

Check failure on line 126 in opentelemetry-datadog/src/exporter/intern.rs

View workflow job for this annotation

GitHub Actions / lint

writing `&Vec` instead of `&[_]` involves a new object where a slice will do
) -> Result<(), ValueWriteError<W::Error>> {
if array.is_empty() {
return Self::write_empty_array(payload);
}

reusable_buffer.clear();
reusable_buffer.push(LEFT_SQUARE_BRACKET);

array[0].write_to(reusable_buffer);

Check warning on line 135 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L123-L135

Added lines #L123 - L135 were not covered by tests

for value in array[1..].iter() {
reusable_buffer.push(COMMA);
value.write_to(reusable_buffer);
}

Check warning on line 140 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L137-L140

Added lines #L137 - L140 were not covered by tests

reusable_buffer.push(RIGHT_SQUARE_BRACKET);

Self::write_buffer_as_string(payload, reusable_buffer)
}

Check warning on line 145 in opentelemetry-datadog/src/exporter/intern.rs

View check run for this annotation

Codecov / codecov/patch

opentelemetry-datadog/src/exporter/intern.rs#L142-L145

Added lines #L142 - L145 were not covered by tests
}

pub(crate) struct StringInterner<'a> {
Expand Down Expand Up @@ -83,6 +182,20 @@ impl<'a> StringInterner<'a> {
pub(crate) fn len(&self) -> u32 {
self.data.len() as u32
}

pub(crate) fn write_dictionary<W: RmpWrite>(
&self,
payload: &mut W,
) -> Result<(), ValueWriteError<W::Error>> {
let mut reusable_buffer: Vec<u8> = vec![];

rmp::encode::write_array_len(payload, self.len())?;
for data in self.iter() {
data.write_as_str(payload, &mut reusable_buffer)?;
}

Ok(())
}
}

#[cfg(test)]
Expand All @@ -108,4 +221,70 @@ mod tests {
assert_eq!(d_idx, a_idx);
assert_eq!(e_idx, c_idx);
}

#[test]
fn test_intern_bool() {
let a = Value::Bool(true);
let b = Value::Bool(false);
let c = "c";

let mut intern = StringInterner::new();
let a_idx = intern.intern_value(&a);
let b_idx = intern.intern_value(&b);
let c_idx = intern.intern(c);
let d_idx = intern.intern_value(&a);
let e_idx = intern.intern(c);

assert_eq!(a_idx, 0);
assert_eq!(b_idx, 1);
assert_eq!(c_idx, 2);
assert_eq!(d_idx, a_idx);
assert_eq!(e_idx, c_idx);
}

#[test]
fn test_intern_i64() {
let a = Value::I64(1234567890);
let b = Value::I64(-1234567890);
let c = "c";
let d = Value::I64(1234567890);

let mut intern = StringInterner::new();
let a_idx = intern.intern_value(&a);
let b_idx = intern.intern_value(&b);
let c_idx = intern.intern(c);
let d_idx = intern.intern_value(&a);
let e_idx = intern.intern(c);
let f_idx = intern.intern_value(&d);

assert_eq!(a_idx, 0);
assert_eq!(b_idx, 1);
assert_eq!(c_idx, 2);
assert_eq!(d_idx, a_idx);
assert_eq!(e_idx, c_idx);
assert_eq!(f_idx, a_idx);
}

#[test]
fn test_intern_f64() {
let a = Value::F64(123456.7890);
let b = Value::F64(-1234567.890);
let c = "c";
let d = Value::F64(-1234567.890);

let mut intern = StringInterner::new();
let a_idx = intern.intern_value(&a);
let b_idx = intern.intern_value(&b);
let c_idx = intern.intern(c);
let d_idx = intern.intern_value(&a);
let e_idx = intern.intern(c);
let f_idx = intern.intern_value(&d);

assert_eq!(a_idx, 0);
assert_eq!(b_idx, 1);
assert_eq!(c_idx, 2);
assert_eq!(d_idx, a_idx);
assert_eq!(e_idx, c_idx);
assert_eq!(b_idx, f_idx);
}
}
5 changes: 1 addition & 4 deletions opentelemetry-datadog/src/exporter/model/v05.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,7 @@ where
let mut payload = Vec::new();
rmp::encode::write_array_len(&mut payload, 2)?;

rmp::encode::write_array_len(&mut payload, interner.len())?;
for data in interner.iter() {
data.write_as_str(&mut payload)?;
}
interner.write_dictionary(&mut payload)?;

payload.append(&mut encoded_traces);

Expand Down

0 comments on commit ab8bd18

Please sign in to comment.