use serde::de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor};
use base64::Engine as _;
use super::decode::{
reject_container_at, reject_non_null_scalars_at, reject_scalars_at, DecodedValue, KeySeed,
Members, SkipSeed, SpanAttrs, St,
};
use super::limits::{OtlpIngestError, OtlpLimitDimension, ShapeSite};
fn charge_value<E: de::Error>(st: St<'_>, budget: &mut u64, bytes: u64) -> Result<(), E> {
*budget = budget.saturating_add(bytes);
let mut state = st.borrow_mut();
if *budget > state.limits.max_attribute_value_bytes {
let max = state.limits.max_attribute_value_bytes;
return Err(state.limit(OtlpLimitDimension::AttributeValueBytes, max));
}
Ok(())
}
fn reject_value_scalar<E: de::Error, T>(st: St<'_>, budget: &mut u64, bytes: u64) -> Result<T, E> {
st.borrow_mut().charge(bytes)?;
charge_value(st, budget, bytes)?;
Err(st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
macro_rules! reject_value_non_null_scalars {
() => {
fn visit_bool<E: de::Error>(self, _: bool) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 1)
}
fn visit_i64<E: de::Error>(self, _: i64) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 8)
}
fn visit_u64<E: de::Error>(self, _: u64) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 8)
}
fn visit_f64<E: de::Error>(self, _: f64) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 8)
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, value.len() as u64)
}
};
}
macro_rules! reject_value_scalars {
() => {
reject_value_non_null_scalars!();
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 1)
}
};
}
pub(super) struct AttributeListSeed<'a, 'v> {
pub(super) st: St<'a>,
pub(super) depth: u64,
pub(super) extract: Option<&'v mut SpanAttrs>,
}
impl<'de> DeserializeSeed<'de> for AttributeListSeed<'_, '_> {
type Value = ();
fn deserialize<D: de::Deserializer<'de>>(self, de: D) -> Result<(), D::Error> {
de.deserialize_any(self)
}
}
impl<'de> Visitor<'de> for AttributeListSeed<'_, '_> {
type Value = ();
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("an attribute list")
}
reject_non_null_scalars_at!(ShapeSite::AttributeList);
reject_container_at!(map, ShapeSite::AttributeList);
fn visit_unit<E: de::Error>(self) -> Result<(), E> {
self.st.borrow_mut().charge(1)?;
Ok(())
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<(), A::Error> {
self.st.borrow_mut().enter(self.depth)?;
let mut extract = self.extract;
let mut count: u64 = 0;
let mut keys = std::collections::HashSet::new();
while let Some((key, value)) = seq.next_element_seed(AttributeEntrySeed {
st: self.st,
depth: self.depth + 1,
count: &mut count,
})? {
if !keys.insert(key.clone()) {
return Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::DuplicateAttributeKey));
}
if let Some(attrs) = extract.as_deref_mut() {
attrs.apply(self.st, &key, value)?;
}
}
Ok(())
}
}
struct AttributeEntrySeed<'a, 'c> {
st: St<'a>,
depth: u64,
count: &'c mut u64,
}
impl<'de> DeserializeSeed<'de> for AttributeEntrySeed<'_, '_> {
type Value = (String, DecodedValue);
fn deserialize<D: de::Deserializer<'de>>(self, de: D) -> Result<Self::Value, D::Error> {
let mut state = self.st.borrow_mut();
*self.count += 1;
if *self.count > state.limits.max_attribute_count {
let max = state.limits.max_attribute_count;
return Err(state.limit(OtlpLimitDimension::AttributeCount, max));
}
drop(state);
de.deserialize_any(self)
}
}
impl<'de> Visitor<'de> for AttributeEntrySeed<'_, '_> {
type Value = (String, DecodedValue);
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("an attribute entry object")
}
reject_scalars_at!(ShapeSite::AttributeEntry);
reject_container_at!(seq, ShapeSite::AttributeEntry);
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
self.st.borrow_mut().enter(self.depth)?;
let mut members = Members::new(ShapeSite::AttributeEntry);
let mut key: Option<String> = None;
let mut value: Option<DecodedValue> = None;
let mut budget: u64 = 0;
while let Some(member) = map.next_key_seed(KeySeed { st: self.st })? {
members.admit(self.st, &member)?;
match member.as_str() {
"key" => {
key = Some(map.next_value_seed(AttrKeySeed {
st: self.st,
depth: self.depth + 1,
})?)
}
"value" => {
value = Some(map.next_value_seed(AnyValueSeed {
st: self.st,
depth: self.depth + 1,
budget: &mut budget,
allow_null: true,
})?);
}
_ => map.next_value_seed(SkipSeed {
st: self.st,
depth: self.depth + 1,
})?,
}
}
match key {
Some(key) => Ok((key, value.unwrap_or(DecodedValue::Other))),
None => Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeEntry))),
}
}
}
struct AttrKeySeed<'a> {
st: St<'a>,
depth: u64,
}
impl<'de> DeserializeSeed<'de> for AttrKeySeed<'_> {
type Value = String;
fn deserialize<D: de::Deserializer<'de>>(self, de: D) -> Result<String, D::Error> {
de.deserialize_any(self)
}
}
impl<'de> Visitor<'de> for AttrKeySeed<'_> {
type Value = String;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("an attribute key string")
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<String, E> {
let mut state = self.st.borrow_mut();
state.charge(s.len() as u64)?;
let max = state.limits.max_attribute_key_bytes;
if s.len() as u64 > max {
return Err(state.limit(OtlpLimitDimension::AttributeKeyBytes, max));
}
drop(state);
Ok(s.to_owned())
}
fn visit_bool<E: de::Error>(self, _: bool) -> Result<String, E> {
self.st.borrow_mut().charge(1)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeEntry)))
}
fn visit_i64<E: de::Error>(self, _: i64) -> Result<String, E> {
self.st.borrow_mut().charge(8)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeEntry)))
}
fn visit_u64<E: de::Error>(self, _: u64) -> Result<String, E> {
self.st.borrow_mut().charge(8)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeEntry)))
}
fn visit_f64<E: de::Error>(self, _: f64) -> Result<String, E> {
self.st.borrow_mut().charge(8)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeEntry)))
}
fn visit_unit<E: de::Error>(self) -> Result<String, E> {
self.st.borrow_mut().charge(1)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeEntry)))
}
fn visit_seq<A: SeqAccess<'de>>(self, _: A) -> Result<String, A::Error> {
self.st.borrow_mut().enter(self.depth)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeEntry)))
}
fn visit_map<A: MapAccess<'de>>(self, _: A) -> Result<String, A::Error> {
self.st.borrow_mut().enter(self.depth)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeEntry)))
}
}
struct AnyValueSeed<'a, 'b> {
st: St<'a>,
depth: u64,
budget: &'b mut u64,
allow_null: bool,
}
impl<'de> DeserializeSeed<'de> for AnyValueSeed<'_, '_> {
type Value = DecodedValue;
fn deserialize<D: de::Deserializer<'de>>(self, de: D) -> Result<Self::Value, D::Error> {
de.deserialize_any(self)
}
}
impl<'de> Visitor<'de> for AnyValueSeed<'_, '_> {
type Value = DecodedValue;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("an AnyValue object")
}
reject_container_at!(seq, ShapeSite::AttributeValue);
fn visit_bool<E: de::Error>(self, _: bool) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(1)?;
charge_value(self.st, self.budget, 1)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
fn visit_i64<E: de::Error>(self, _: i64) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(8)?;
charge_value(self.st, self.budget, 8)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
fn visit_u64<E: de::Error>(self, _: u64) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(8)?;
charge_value(self.st, self.budget, 8)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
fn visit_f64<E: de::Error>(self, _: f64) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(8)?;
charge_value(self.st, self.budget, 8)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(value.len() as u64)?;
charge_value(self.st, self.budget, value.len() as u64)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(1)?;
charge_value(self.st, self.budget, 1)?;
if self.allow_null {
Ok(DecodedValue::Other)
} else {
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
}
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
self.st.borrow_mut().enter(self.depth)?;
let mut decoded: Option<DecodedValue> = None;
let mut members = Members::new(ShapeSite::AttributeValue);
while let Some(member) = map.next_key_seed(KeySeed { st: self.st })? {
let recognized = matches!(
member.as_str(),
"stringValue"
| "intValue"
| "doubleValue"
| "boolValue"
| "bytesValue"
| "arrayValue"
| "kvlistValue"
);
if !recognized {
charge_value(self.st, self.budget, member.len() as u64)?;
}
members.admit(self.st, &member)?;
let value = match member.as_str() {
"stringValue" => map
.next_value_seed(ValueStrSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
})?
.map(DecodedValue::Str),
"intValue" => map
.next_value_seed(ValueIntSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
})?
.map(|_| DecodedValue::Other),
"doubleValue" => map
.next_value_seed(ValueScalarSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
kind: ScalarKind::Number,
})?
.map(|_| DecodedValue::Other),
"boolValue" => map
.next_value_seed(ValueScalarSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
kind: ScalarKind::Bool,
})?
.map(|_| DecodedValue::Other),
"bytesValue" => map
.next_value_seed(ValueScalarSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
kind: ScalarKind::Base64,
})?
.map(|_| DecodedValue::Other),
"arrayValue" => map
.next_value_seed(ValueListSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
kind: ListKind::Array,
})?
.map(|_| DecodedValue::Other),
"kvlistValue" => map
.next_value_seed(ValueListSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
kind: ListKind::Kv,
})?
.map(|_| DecodedValue::Other),
_ => {
map.next_value_seed(ValueSkipSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
})?;
None
}
};
if recognized {
if let Some(value) = value {
if decoded.is_some() {
return Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::ConflictingAttributeValue));
}
decoded = Some(value);
}
}
}
Ok(decoded.unwrap_or(DecodedValue::Other))
}
}
struct ValueSkipSeed<'a, 'b> {
st: St<'a>,
depth: u64,
budget: &'b mut u64,
}
impl<'de> DeserializeSeed<'de> for ValueSkipSeed<'_, '_> {
type Value = ();
fn deserialize<D: de::Deserializer<'de>>(self, de: D) -> Result<(), D::Error> {
de.deserialize_any(self)
}
}
impl<'de> Visitor<'de> for ValueSkipSeed<'_, '_> {
type Value = ();
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("a forward-compatible attribute value")
}
fn visit_bool<E: de::Error>(self, _: bool) -> Result<(), E> {
self.st.borrow_mut().charge(1)?;
charge_value(self.st, self.budget, 1)
}
fn visit_i64<E: de::Error>(self, _: i64) -> Result<(), E> {
self.st.borrow_mut().charge(8)?;
charge_value(self.st, self.budget, 8)
}
fn visit_u64<E: de::Error>(self, _: u64) -> Result<(), E> {
self.st.borrow_mut().charge(8)?;
charge_value(self.st, self.budget, 8)
}
fn visit_f64<E: de::Error>(self, _: f64) -> Result<(), E> {
self.st.borrow_mut().charge(8)?;
charge_value(self.st, self.budget, 8)
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<(), E> {
let bytes = value.len() as u64;
self.st.borrow_mut().charge(bytes)?;
charge_value(self.st, self.budget, bytes)
}
fn visit_unit<E: de::Error>(self) -> Result<(), E> {
self.st.borrow_mut().charge(1)?;
charge_value(self.st, self.budget, 1)
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<(), A::Error> {
self.st.borrow_mut().enter(self.depth)?;
while seq
.next_element_seed(ValueSkipSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
})?
.is_some()
{}
Ok(())
}
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<(), A::Error> {
self.st.borrow_mut().enter(self.depth)?;
let mut members = Members::new(ShapeSite::AttributeValue);
while let Some(key) = map.next_key_seed(KeySeed { st: self.st })? {
charge_value(self.st, self.budget, key.len() as u64)?;
members.admit(self.st, &key)?;
map.next_value_seed(ValueSkipSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
})?;
}
Ok(())
}
}
struct ValueStrSeed<'a, 'b> {
st: St<'a>,
depth: u64,
budget: &'b mut u64,
}
impl<'de> DeserializeSeed<'de> for ValueStrSeed<'_, '_> {
type Value = Option<String>;
fn deserialize<D: de::Deserializer<'de>>(self, de: D) -> Result<Self::Value, D::Error> {
de.deserialize_any(self)
}
}
impl<'de> Visitor<'de> for ValueStrSeed<'_, '_> {
type Value = Option<String>;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("a string value")
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(s.len() as u64)?;
charge_value(self.st, self.budget, s.len() as u64)?;
Ok(Some(s.to_owned()))
}
fn visit_bool<E: de::Error>(self, _: bool) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 1)
}
fn visit_i64<E: de::Error>(self, _: i64) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 8)
}
fn visit_u64<E: de::Error>(self, _: u64) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 8)
}
fn visit_f64<E: de::Error>(self, _: f64) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 8)
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(1)?;
charge_value(self.st, self.budget, 1)?;
Ok(None)
}
fn visit_seq<A: SeqAccess<'de>>(self, _: A) -> Result<Self::Value, A::Error> {
self.st.borrow_mut().enter(self.depth)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
fn visit_map<A: MapAccess<'de>>(self, _: A) -> Result<Self::Value, A::Error> {
self.st.borrow_mut().enter(self.depth)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
}
struct ValueIntSeed<'a, 'b> {
st: St<'a>,
depth: u64,
budget: &'b mut u64,
}
impl<'de> DeserializeSeed<'de> for ValueIntSeed<'_, '_> {
type Value = Option<i64>;
fn deserialize<D: de::Deserializer<'de>>(self, de: D) -> Result<Self::Value, D::Error> {
de.deserialize_any(self)
}
}
impl<'de> Visitor<'de> for ValueIntSeed<'_, '_> {
type Value = Option<i64>;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("an int64 value")
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(s.len() as u64)?;
charge_value(self.st, self.budget, s.len() as u64)?;
let parsed = parse_decimal_i64(s);
parsed.map(Some).ok_or_else(|| {
self.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue))
})
}
fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(8)?;
charge_value(self.st, self.budget, 8)?;
integral_i64(v as f64).map(Some).ok_or_else(|| {
self.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue))
})
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(8)?;
charge_value(self.st, self.budget, 8)?;
integral_i64(v as f64).map(Some).ok_or_else(|| {
self.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue))
})
}
fn visit_bool<E: de::Error>(self, _: bool) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 1)
}
fn visit_f64<E: de::Error>(self, value: f64) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(8)?;
charge_value(self.st, self.budget, 8)?;
integral_i64(value).map(Some).ok_or_else(|| {
self.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue))
})
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(1)?;
charge_value(self.st, self.budget, 1)?;
Ok(None)
}
fn visit_seq<A: SeqAccess<'de>>(self, _: A) -> Result<Self::Value, A::Error> {
self.st.borrow_mut().enter(self.depth)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
fn visit_map<A: MapAccess<'de>>(self, _: A) -> Result<Self::Value, A::Error> {
self.st.borrow_mut().enter(self.depth)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
}
fn integral_i64(value: f64) -> Option<i64> {
const I64_UPPER_EXCLUSIVE: f64 = 9_223_372_036_854_775_808.0;
const I64_LOWER_INCLUSIVE: f64 = -9_223_372_036_854_775_808.0;
(value.is_finite()
&& value.fract() == 0.0
&& (I64_LOWER_INCLUSIVE..I64_UPPER_EXCLUSIVE).contains(&value))
.then_some(value as i64)
}
fn parse_decimal_i64(value: &str) -> Option<i64> {
if !is_json_number(value) {
return None;
}
let (negative, unsigned) = match value.as_bytes().first() {
Some(b'-') => (true, &value[1..]),
Some(b'+') | None => return None,
_ => (false, value),
};
let (mantissa, exponent_text) = match unsigned.find(['e', 'E']) {
Some(index) => {
let exponent_text = &unsigned[index + 1..];
if exponent_text.is_empty() || unsigned[index + 1..].contains(['e', 'E']) {
return None;
}
(&unsigned[..index], Some(exponent_text))
}
None => (unsigned, None),
};
let (integer, fraction) = match mantissa.split_once('.') {
Some((integer, fraction)) if !integer.is_empty() && !fraction.is_empty() => {
(integer, fraction)
}
Some(_) => return None,
None if !mantissa.is_empty() => (mantissa, ""),
None => return None,
};
if !integer.bytes().all(|b| b.is_ascii_digit()) || !fraction.bytes().all(|b| b.is_ascii_digit())
{
return None;
}
let mut digits = Vec::with_capacity(integer.len().saturating_add(fraction.len()));
digits.extend(integer.bytes());
digits.extend(fraction.bytes());
if digits.iter().all(|digit| *digit == b'0') {
return Some(0);
}
let exponent = exponent_text.map_or(Some(0), |text| text.parse::<i64>().ok())?;
let scale = exponent.checked_sub(i64::try_from(fraction.len()).ok()?)?;
if scale < 0 {
let remove = usize::try_from(scale.unsigned_abs()).ok()?;
if remove > digits.len() {
return digits.iter().all(|digit| *digit == b'0').then_some(0);
}
let split = digits.len() - remove;
if !digits[split..].iter().all(|digit| *digit == b'0') {
return None;
}
digits.truncate(split);
}
let first_nonzero = digits.iter().position(|digit| *digit != b'0');
let Some(first_nonzero) = first_nonzero else {
return Some(0);
};
let significant = &digits[first_nonzero..];
let appended_zeros = usize::try_from(scale.max(0)).ok()?;
let effective_len = significant.len().checked_add(appended_zeros)?;
if effective_len > 19 {
return None;
}
let mut magnitude = 0_u64;
for digit in significant
.iter()
.copied()
.chain(std::iter::repeat_n(b'0', appended_zeros))
{
magnitude = magnitude
.checked_mul(10)?
.checked_add(u64::from(digit - b'0'))?;
}
if negative {
const MIN_MAGNITUDE: u64 = i64::MAX as u64 + 1;
if magnitude == MIN_MAGNITUDE {
Some(i64::MIN)
} else if magnitude <= i64::MAX as u64 {
Some(-(magnitude as i64))
} else {
None
}
} else {
i64::try_from(magnitude).ok()
}
}
fn is_json_number(value: &str) -> bool {
let bytes = value.as_bytes();
let mut index = usize::from(bytes.first() == Some(&b'-'));
if index == bytes.len() {
return false;
}
match bytes[index] {
b'0' => index += 1,
b'1'..=b'9' => {
index += 1;
while bytes.get(index).is_some_and(u8::is_ascii_digit) {
index += 1;
}
}
_ => return false,
}
if bytes.get(index) == Some(&b'.') {
index += 1;
let fraction_start = index;
while bytes.get(index).is_some_and(u8::is_ascii_digit) {
index += 1;
}
if index == fraction_start {
return false;
}
}
if matches!(bytes.get(index), Some(b'e' | b'E')) {
index += 1;
if matches!(bytes.get(index), Some(b'+' | b'-')) {
index += 1;
}
let exponent_start = index;
while bytes.get(index).is_some_and(u8::is_ascii_digit) {
index += 1;
}
if index == exponent_start {
return false;
}
}
index == bytes.len()
}
enum ScalarKind {
Number,
Bool,
Base64,
}
struct ValueScalarSeed<'a, 'b> {
st: St<'a>,
depth: u64,
budget: &'b mut u64,
kind: ScalarKind,
}
impl<'de> DeserializeSeed<'de> for ValueScalarSeed<'_, '_> {
type Value = Option<()>;
fn deserialize<D: de::Deserializer<'de>>(self, de: D) -> Result<Self::Value, D::Error> {
de.deserialize_any(self)
}
}
impl<'de> Visitor<'de> for ValueScalarSeed<'_, '_> {
type Value = Option<()>;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("a scalar AnyValue member")
}
fn visit_bool<E: de::Error>(self, _: bool) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(1)?;
charge_value(self.st, self.budget, 1)?;
if matches!(self.kind, ScalarKind::Bool) {
Ok(Some(()))
} else {
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
}
fn visit_f64<E: de::Error>(self, _: f64) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(8)?;
charge_value(self.st, self.budget, 8)?;
if matches!(self.kind, ScalarKind::Number) {
Ok(Some(()))
} else {
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
}
fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
self.visit_f64(v as f64)
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
self.visit_f64(v as f64)
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(s.len() as u64)?;
charge_value(self.st, self.budget, s.len() as u64)?;
let valid = match self.kind {
ScalarKind::Number => {
matches!(s, "NaN" | "Infinity" | "-Infinity")
|| (is_json_number(s) && s.parse::<f64>().is_ok_and(|value| value.is_finite()))
}
ScalarKind::Base64 => valid_base64(s),
ScalarKind::Bool => false,
};
if valid {
Ok(Some(()))
} else {
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(1)?;
charge_value(self.st, self.budget, 1)?;
Ok(None)
}
fn visit_seq<A: SeqAccess<'de>>(self, _: A) -> Result<Self::Value, A::Error> {
self.st.borrow_mut().enter(self.depth)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
fn visit_map<A: MapAccess<'de>>(self, _: A) -> Result<Self::Value, A::Error> {
self.st.borrow_mut().enter(self.depth)?;
Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::UnexpectedShape(ShapeSite::AttributeValue)))
}
}
fn valid_base64(value: &str) -> bool {
use base64::engine::general_purpose::{STANDARD, STANDARD_NO_PAD, URL_SAFE, URL_SAFE_NO_PAD};
let mut scratch = vec![0; base64::decoded_len_estimate(value.len())];
[&STANDARD, &STANDARD_NO_PAD, &URL_SAFE, &URL_SAFE_NO_PAD]
.into_iter()
.any(|engine| engine.decode_slice(value, &mut scratch).is_ok())
}
enum ListKind {
Array,
Kv,
}
struct ValueListSeed<'a, 'b> {
st: St<'a>,
depth: u64,
budget: &'b mut u64,
kind: ListKind,
}
impl<'de> DeserializeSeed<'de> for ValueListSeed<'_, '_> {
type Value = Option<()>;
fn deserialize<D: de::Deserializer<'de>>(self, de: D) -> Result<Self::Value, D::Error> {
de.deserialize_any(self)
}
}
impl<'de> Visitor<'de> for ValueListSeed<'_, '_> {
type Value = Option<()>;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("an AnyValue list wrapper")
}
reject_container_at!(seq, ShapeSite::AttributeValue);
reject_value_non_null_scalars!();
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
self.st.borrow_mut().enter(self.depth)?;
let mut members = Members::new(ShapeSite::AttributeValue);
while let Some(member) = map.next_key_seed(KeySeed { st: self.st })? {
match member.as_str() {
"values" => {
members.admit(self.st, &member)?;
map.next_value_seed(ValueElementsSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
kind: &self.kind,
})?;
}
_ => {
charge_value(self.st, self.budget, member.len() as u64)?;
members.admit(self.st, &member)?;
map.next_value_seed(ValueSkipSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
})?;
}
}
}
Ok(Some(()))
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(1)?;
charge_value(self.st, self.budget, 1)?;
Ok(None)
}
}
struct ValueElementsSeed<'a, 'b> {
st: St<'a>,
depth: u64,
budget: &'b mut u64,
kind: &'b ListKind,
}
impl<'de> DeserializeSeed<'de> for ValueElementsSeed<'_, '_> {
type Value = ();
fn deserialize<D: de::Deserializer<'de>>(self, de: D) -> Result<(), D::Error> {
de.deserialize_any(self)
}
}
impl<'de> Visitor<'de> for ValueElementsSeed<'_, '_> {
type Value = ();
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("an AnyValue values array")
}
reject_value_non_null_scalars!();
reject_container_at!(map, ShapeSite::AttributeValue);
fn visit_unit<E: de::Error>(self) -> Result<(), E> {
self.st.borrow_mut().charge(1)?;
charge_value(self.st, self.budget, 1)?;
Ok(())
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<(), A::Error> {
self.st.borrow_mut().enter(self.depth)?;
match self.kind {
ListKind::Array => {
while seq
.next_element_seed(AnyValueSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
allow_null: false,
})?
.is_some()
{}
}
ListKind::Kv => {
let mut keys = std::collections::HashSet::new();
while let Some(key) = seq.next_element_seed(KvEntrySeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
})? {
if !keys.insert(key) {
return Err(self
.st
.borrow_mut()
.fail(OtlpIngestError::DuplicateField(ShapeSite::AttributeValue)));
}
}
}
}
Ok(())
}
}
struct KvEntrySeed<'a, 'b> {
st: St<'a>,
depth: u64,
budget: &'b mut u64,
}
struct KvEntryValueKeySeed<'a, 'b> {
st: St<'a>,
depth: u64,
budget: &'b mut u64,
}
impl<'de> DeserializeSeed<'de> for KvEntryValueKeySeed<'_, '_> {
type Value = String;
fn deserialize<D: de::Deserializer<'de>>(self, de: D) -> Result<Self::Value, D::Error> {
de.deserialize_any(self)
}
}
impl<'de> Visitor<'de> for KvEntryValueKeySeed<'_, '_> {
type Value = String;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("a nested KeyValue key string")
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<String, E> {
self.st.borrow_mut().charge(value.len() as u64)?;
charge_value(self.st, self.budget, value.len() as u64)?;
Ok(value.to_owned())
}
fn visit_bool<E: de::Error>(self, _: bool) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 1)
}
fn visit_i64<E: de::Error>(self, _: i64) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 8)
}
fn visit_u64<E: de::Error>(self, _: u64) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 8)
}
fn visit_f64<E: de::Error>(self, _: f64) -> Result<Self::Value, E> {
reject_value_scalar(self.st, self.budget, 8)
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
self.st.borrow_mut().charge(1)?;
charge_value(self.st, self.budget, 1)?;
Ok(String::new())
}
reject_container_at!(seq, ShapeSite::AttributeValue);
reject_container_at!(map, ShapeSite::AttributeValue);
}
impl<'de> DeserializeSeed<'de> for KvEntrySeed<'_, '_> {
type Value = String;
fn deserialize<D: de::Deserializer<'de>>(self, de: D) -> Result<String, D::Error> {
de.deserialize_any(self)
}
}
impl<'de> Visitor<'de> for KvEntrySeed<'_, '_> {
type Value = String;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("a kvlist entry object")
}
reject_value_scalars!();
reject_container_at!(seq, ShapeSite::AttributeValue);
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<String, A::Error> {
self.st.borrow_mut().enter(self.depth)?;
let mut members = Members::new(ShapeSite::AttributeValue);
let mut key = None;
while let Some(member) = map.next_key_seed(KeySeed { st: self.st })? {
match member.as_str() {
"key" => {
members.admit(self.st, &member)?;
key = Some(map.next_value_seed(KvEntryValueKeySeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
})?);
}
"value" => {
members.admit(self.st, &member)?;
map.next_value_seed(AnyValueSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
allow_null: true,
})?;
}
_ => {
charge_value(self.st, self.budget, member.len() as u64)?;
members.admit(self.st, &member)?;
map.next_value_seed(ValueSkipSeed {
st: self.st,
depth: self.depth + 1,
budget: self.budget,
})?;
}
}
}
Ok(key.unwrap_or_default())
}
}