use std::borrow::Cow;
use crate::codec::primitives::{Reader, Writer};
use crate::error::{DecodeError, EncodeError};
use crate::limits::{
MAX_BYTES_LEN, MAX_EMBEDDING_BYTES, MAX_EMBEDDING_DIMS, MAX_POSITION_LEN, MAX_STRING_LEN,
};
use crate::model::{
DataType, DecimalMantissa, DictionaryBuilder, EmbeddingSubType, PropertyValue, Value,
WireDictionaries,
};
use crate::util::{
format_date_rfc3339, format_datetime_rfc3339, format_time_rfc3339, parse_date_rfc3339,
parse_datetime_rfc3339, parse_time_rfc3339,
};
const DECIMAL_EXPONENT_OUT_OF_RANGE: &str = "DECIMAL exponent outside int32 range";
const DECIMAL_EMPTY_BIG_MANTISSA: &str = "DECIMAL mantissa bytes must not be empty";
const DECIMAL_NORMALIZATION_STEP_LIMIT_EXCEEDED: &str =
"DECIMAL normalization exceeds maximum steps";
const MAX_DECIMAL_NORMALIZATION_STEPS: usize = 4096;
#[derive(Clone, Copy)]
enum DecimalNormalizeError {
ExponentOutOfRange,
EmptyBigMantissa,
StepLimitExceeded,
}
impl DecimalNormalizeError {
fn into_decode_error(self) -> DecodeError {
match self {
DecimalNormalizeError::ExponentOutOfRange => DecodeError::MalformedEncoding {
context: DECIMAL_EXPONENT_OUT_OF_RANGE,
},
DecimalNormalizeError::EmptyBigMantissa => DecodeError::MalformedEncoding {
context: DECIMAL_EMPTY_BIG_MANTISSA,
},
DecimalNormalizeError::StepLimitExceeded => DecodeError::MalformedEncoding {
context: DECIMAL_NORMALIZATION_STEP_LIMIT_EXCEEDED,
},
}
}
fn into_encode_error(self) -> EncodeError {
match self {
DecimalNormalizeError::ExponentOutOfRange => EncodeError::InvalidInput {
context: DECIMAL_EXPONENT_OUT_OF_RANGE,
},
DecimalNormalizeError::EmptyBigMantissa => EncodeError::InvalidInput {
context: DECIMAL_EMPTY_BIG_MANTISSA,
},
DecimalNormalizeError::StepLimitExceeded => EncodeError::InvalidInput {
context: DECIMAL_NORMALIZATION_STEP_LIMIT_EXCEEDED,
},
}
}
}
pub fn decode_value<'a>(
reader: &mut Reader<'a>,
data_type: DataType,
dicts: &WireDictionaries,
) -> Result<Value<'a>, DecodeError> {
match data_type {
DataType::Boolean => decode_boolean(reader),
DataType::Integer => decode_integer(reader, dicts),
DataType::Float => decode_float(reader, dicts),
DataType::Decimal => decode_decimal(reader, dicts),
DataType::Text => decode_text(reader, dicts),
DataType::Bytes => decode_bytes(reader),
DataType::Date => decode_date(reader),
DataType::Time => decode_time(reader),
DataType::Datetime => decode_datetime(reader),
DataType::Schedule => decode_schedule(reader),
DataType::Point => decode_point(reader),
DataType::Rect => decode_rect(reader),
DataType::Embedding => decode_embedding(reader),
}
}
fn decode_boolean<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
let byte = reader.read_byte("boolean")?;
match byte {
0x00 => Ok(Value::Boolean(false)),
0x01 => Ok(Value::Boolean(true)),
_ => Err(DecodeError::InvalidBoolean { value: byte }),
}
}
fn decode_integer<'a>(
reader: &mut Reader<'a>,
dicts: &WireDictionaries,
) -> Result<Value<'a>, DecodeError> {
let value = reader.read_signed_varint("integer")?;
let unit_index = reader.read_varint("integer.unit")? as usize;
let unit = if unit_index == 0 {
None
} else {
let idx = unit_index - 1;
if idx >= dicts.units.len() {
return Err(DecodeError::IndexOutOfBounds {
dict: "units",
index: unit_index,
size: dicts.units.len() + 1,
});
}
Some(dicts.units[idx])
};
Ok(Value::Integer { value, unit })
}
fn decode_float<'a>(
reader: &mut Reader<'a>,
dicts: &WireDictionaries,
) -> Result<Value<'a>, DecodeError> {
let value = reader.read_f64("float")?;
let unit_index = reader.read_varint("float.unit")? as usize;
let unit = if unit_index == 0 {
None
} else {
let idx = unit_index - 1;
if idx >= dicts.units.len() {
return Err(DecodeError::IndexOutOfBounds {
dict: "units",
index: unit_index,
size: dicts.units.len() + 1,
});
}
Some(dicts.units[idx])
};
Ok(Value::Float { value, unit })
}
fn decode_decimal<'a>(
reader: &mut Reader<'a>,
dicts: &WireDictionaries,
) -> Result<Value<'a>, DecodeError> {
let exponent = i32::try_from(reader.read_signed_varint("decimal.exponent")?)
.map_err(|_| DecodeError::MalformedEncoding {
context: DECIMAL_EXPONENT_OUT_OF_RANGE,
})?;
let mantissa_type = reader.read_byte("decimal.mantissa_type")?;
let mantissa = match mantissa_type {
0x00 => {
let v = reader.read_signed_varint("decimal.mantissa")?;
DecimalMantissa::I64(v)
}
0x01 => {
let len = reader.read_varint("decimal.mantissa_len")? as usize;
if len > MAX_BYTES_LEN {
return Err(DecodeError::LengthExceedsLimit {
field: "decimal.mantissa",
len,
max: MAX_BYTES_LEN,
});
}
let bytes = reader.read_bytes(len, "decimal.mantissa_bytes")?;
if bytes.is_empty() {
return Err(DecodeError::DecimalMantissaNotMinimal);
}
let first = bytes[0];
if bytes.len() > 1 {
let second = bytes[1];
if (first == 0x00 && (second & 0x80) == 0)
|| (first == 0xFF && (second & 0x80) != 0)
{
return Err(DecodeError::DecimalMantissaNotMinimal);
}
}
DecimalMantissa::Big(Cow::Borrowed(bytes))
}
_ => {
return Err(DecodeError::MalformedEncoding {
context: "invalid decimal mantissa type",
});
}
};
let (exponent, mantissa) = normalize_decimal_for_decode(exponent, mantissa)?;
let unit_index = reader.read_varint("decimal.unit")? as usize;
let unit = if unit_index == 0 {
None
} else {
let idx = unit_index - 1;
if idx >= dicts.units.len() {
return Err(DecodeError::IndexOutOfBounds {
dict: "units",
index: unit_index,
size: dicts.units.len() + 1,
});
}
Some(dicts.units[idx])
};
Ok(Value::Decimal {
exponent,
mantissa,
unit,
})
}
fn is_big_mantissa_zero(bytes: &[u8]) -> bool {
!bytes.is_empty() && bytes.iter().all(|&b| b == 0)
}
fn has_redundant_sign_extension(bytes: &[u8]) -> bool {
bytes.len() > 1
&& ((bytes[0] == 0x00 && (bytes[1] & 0x80) == 0)
|| (bytes[0] == 0xFF && (bytes[1] & 0x80) != 0))
}
fn is_big_mantissa_divisible_by_10(bytes: &[u8]) -> bool {
if bytes.is_empty() {
return true; }
let is_negative = bytes[0] & 0x80 != 0;
if is_negative {
let abs_mod = twos_complement_abs_mod_10(bytes);
abs_mod == 0
} else {
let mut remainder = 0u32;
for &byte in bytes {
remainder = (remainder * 6 + byte as u32) % 10;
}
remainder == 0
}
}
fn twos_complement_abs_mod_10(bytes: &[u8]) -> u32 {
let mut remainder = 0u32;
for &byte in bytes {
let inverted = !byte;
remainder = (remainder * 6 + inverted as u32) % 10;
}
(remainder + 1) % 10
}
fn decode_text<'a>(
reader: &mut Reader<'a>,
dicts: &WireDictionaries,
) -> Result<Value<'a>, DecodeError> {
let value = reader.read_str(MAX_STRING_LEN, "text")?;
let lang_index = reader.read_varint("text.language")? as usize;
let language = if lang_index == 0 {
None
} else {
let idx = lang_index - 1;
if idx >= dicts.languages.len() {
return Err(DecodeError::IndexOutOfBounds {
dict: "languages",
index: lang_index,
size: dicts.languages.len() + 1, });
}
Some(dicts.languages[idx])
};
Ok(Value::Text {
value: Cow::Borrowed(value),
language,
})
}
fn decode_bytes<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
let len = reader.read_varint("bytes.len")? as usize;
if len > MAX_BYTES_LEN {
return Err(DecodeError::LengthExceedsLimit {
field: "bytes",
len,
max: MAX_BYTES_LEN,
});
}
let bytes = reader.read_bytes(len, "bytes")?;
Ok(Value::Bytes(Cow::Borrowed(bytes)))
}
fn decode_date<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
let bytes = reader.read_bytes(6, "date")?;
let days = i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
let offset_min = i16::from_le_bytes([bytes[4], bytes[5]]);
if offset_min < -1440 || offset_min > 1440 {
return Err(DecodeError::MalformedEncoding {
context: "DATE offset_min outside range [-1440, +1440]",
});
}
let value = format_date_rfc3339(days, offset_min);
Ok(Value::Date(Cow::Owned(value)))
}
fn decode_time<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
let bytes = reader.read_bytes(8, "time")?;
let time_micros_unsigned = u64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], 0, 0,
]);
let time_micros = if time_micros_unsigned & 0x8000_0000_0000 != 0 {
(time_micros_unsigned | 0xFFFF_0000_0000_0000) as i64
} else {
time_micros_unsigned as i64
};
let offset_min = i16::from_le_bytes([bytes[6], bytes[7]]);
if time_micros < 0 || time_micros > 86_399_999_999 {
return Err(DecodeError::MalformedEncoding {
context: "TIME time_micros outside range [0, 86399999999]",
});
}
if offset_min < -1440 || offset_min > 1440 {
return Err(DecodeError::MalformedEncoding {
context: "TIME offset_min outside range [-1440, +1440]",
});
}
let value = format_time_rfc3339(time_micros, offset_min);
Ok(Value::Time(Cow::Owned(value)))
}
fn decode_datetime<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
let bytes = reader.read_bytes(10, "datetime")?;
let epoch_micros = i64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]);
let offset_min = i16::from_le_bytes([bytes[8], bytes[9]]);
if offset_min < -1440 || offset_min > 1440 {
return Err(DecodeError::MalformedEncoding {
context: "DATETIME offset_min outside range [-1440, +1440]",
});
}
let value = format_datetime_rfc3339(epoch_micros, offset_min);
Ok(Value::Datetime(Cow::Owned(value)))
}
fn decode_schedule<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
let value = reader.read_str(MAX_STRING_LEN, "schedule")?;
Ok(Value::Schedule(Cow::Borrowed(value)))
}
fn decode_point<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
let ordinate_count = reader.read_byte("point.ordinate_count")?;
if ordinate_count != 2 && ordinate_count != 3 {
return Err(DecodeError::MalformedEncoding {
context: "POINT ordinate_count must be 2 or 3",
});
}
let lat = reader.read_f64("point.lat")?;
let lon = reader.read_f64("point.lon")?;
let alt = if ordinate_count == 3 {
Some(reader.read_f64("point.alt")?)
} else {
None
};
if !(-90.0..=90.0).contains(&lat) {
return Err(DecodeError::LatitudeOutOfRange { lat });
}
if !(-180.0..=180.0).contains(&lon) {
return Err(DecodeError::LongitudeOutOfRange { lon });
}
if lat.is_nan() || lon.is_nan() {
return Err(DecodeError::FloatIsNan);
}
if let Some(a) = alt {
if a.is_nan() {
return Err(DecodeError::FloatIsNan);
}
}
Ok(Value::Point { lat, lon, alt })
}
fn decode_rect<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
let min_lat = reader.read_f64("rect.min_lat")?;
let min_lon = reader.read_f64("rect.min_lon")?;
let max_lat = reader.read_f64("rect.max_lat")?;
let max_lon = reader.read_f64("rect.max_lon")?;
if !(-90.0..=90.0).contains(&min_lat) || !(-90.0..=90.0).contains(&max_lat) {
return Err(DecodeError::LatitudeOutOfRange {
lat: if !(-90.0..=90.0).contains(&min_lat) {
min_lat
} else {
max_lat
},
});
}
if !(-180.0..=180.0).contains(&min_lon) || !(-180.0..=180.0).contains(&max_lon) {
return Err(DecodeError::LongitudeOutOfRange {
lon: if !(-180.0..=180.0).contains(&min_lon) {
min_lon
} else {
max_lon
},
});
}
if min_lat.is_nan() || min_lon.is_nan() || max_lat.is_nan() || max_lon.is_nan() {
return Err(DecodeError::FloatIsNan);
}
Ok(Value::Rect {
min_lat,
min_lon,
max_lat,
max_lon,
})
}
fn decode_embedding<'a>(reader: &mut Reader<'a>) -> Result<Value<'a>, DecodeError> {
let sub_type_byte = reader.read_byte("embedding.sub_type")?;
let sub_type =
EmbeddingSubType::from_u8(sub_type_byte).ok_or(DecodeError::InvalidEmbeddingSubType {
sub_type: sub_type_byte,
})?;
let dims = reader.read_varint("embedding.dims")? as usize;
if dims > MAX_EMBEDDING_DIMS {
return Err(DecodeError::LengthExceedsLimit {
field: "embedding.dims",
len: dims,
max: MAX_EMBEDDING_DIMS,
});
}
let expected_bytes = sub_type.bytes_for_dims(dims);
if expected_bytes > MAX_EMBEDDING_BYTES {
return Err(DecodeError::LengthExceedsLimit {
field: "embedding.data",
len: expected_bytes,
max: MAX_EMBEDDING_BYTES,
});
}
let data = reader.read_bytes(expected_bytes, "embedding.data")?;
if sub_type == EmbeddingSubType::Float32 {
for chunk in data.chunks_exact(4) {
let f = f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
if f.is_nan() {
return Err(DecodeError::FloatIsNan);
}
}
}
if sub_type == EmbeddingSubType::Binary && dims % 8 != 0 {
let last_byte = data[data.len() - 1];
let unused_bits = 8 - (dims % 8);
let mask = !((1u8 << (8 - unused_bits)) - 1);
if last_byte & mask != 0 {
return Err(DecodeError::MalformedEncoding {
context: "binary embedding has non-zero unused bits",
});
}
}
Ok(Value::Embedding {
sub_type,
dims,
data: Cow::Borrowed(data),
})
}
pub fn decode_property_value<'a>(
reader: &mut Reader<'a>,
dicts: &WireDictionaries,
) -> Result<PropertyValue<'a>, DecodeError> {
let prop_index = reader.read_varint("property")? as usize;
if prop_index >= dicts.properties.len() {
return Err(DecodeError::IndexOutOfBounds {
dict: "properties",
index: prop_index,
size: dicts.properties.len(),
});
}
let (property, data_type) = dicts.properties[prop_index];
let value = decode_value(reader, data_type, dicts)?;
Ok(PropertyValue { property, value })
}
pub fn encode_value(
writer: &mut Writer,
value: &Value<'_>,
dict_builder: &mut DictionaryBuilder,
) -> Result<(), EncodeError> {
match value {
Value::Boolean(v) => {
writer.write_byte(if *v { 0x01 } else { 0x00 });
}
Value::Integer { value, unit } => {
writer.write_signed_varint(*value);
let unit_index = dict_builder.add_unit(*unit);
writer.write_varint(unit_index as u64);
}
Value::Float { value, unit } => {
if value.is_nan() {
return Err(EncodeError::FloatIsNan);
}
writer.write_f64(*value);
let unit_index = dict_builder.add_unit(*unit);
writer.write_varint(unit_index as u64);
}
Value::Decimal {
exponent,
mantissa,
unit,
} => {
encode_decimal(writer, *exponent, mantissa)?;
let unit_index = dict_builder.add_unit(*unit);
writer.write_varint(unit_index as u64);
}
Value::Text { value, language } => {
writer.write_string(value);
let lang_index = dict_builder.add_language(*language);
writer.write_varint(lang_index as u64);
}
Value::Bytes(bytes) => {
writer.write_bytes_prefixed(bytes);
}
Value::Date(s) => {
let (days, offset_min) =
parse_date_rfc3339(s).map_err(|_| EncodeError::InvalidInput {
context: "Invalid RFC 3339 date format",
})?;
writer.write_bytes(&days.to_le_bytes());
writer.write_bytes(&offset_min.to_le_bytes());
}
Value::Time(s) => {
let (time_micros, offset_min) =
parse_time_rfc3339(s).map_err(|_| EncodeError::InvalidInput {
context: "Invalid RFC 3339 time format",
})?;
if time_micros < 0 || time_micros > 86_399_999_999 {
return Err(EncodeError::InvalidInput {
context: "TIME time_micros outside range [0, 86399999999]",
});
}
let time_bytes = time_micros.to_le_bytes();
writer.write_bytes(&time_bytes[0..6]);
writer.write_bytes(&offset_min.to_le_bytes());
}
Value::Datetime(s) => {
let (epoch_micros, offset_min) =
parse_datetime_rfc3339(s).map_err(|_| EncodeError::InvalidInput {
context: "Invalid RFC 3339 datetime format",
})?;
writer.write_bytes(&epoch_micros.to_le_bytes());
writer.write_bytes(&offset_min.to_le_bytes());
}
Value::Schedule(s) => {
writer.write_string(s);
}
Value::Point { lat, lon, alt } => {
if *lat < -90.0 || *lat > 90.0 {
return Err(EncodeError::LatitudeOutOfRange { lat: *lat });
}
if *lon < -180.0 || *lon > 180.0 {
return Err(EncodeError::LongitudeOutOfRange { lon: *lon });
}
if lat.is_nan() || lon.is_nan() {
return Err(EncodeError::FloatIsNan);
}
if let Some(a) = alt {
if a.is_nan() {
return Err(EncodeError::FloatIsNan);
}
}
let ordinate_count = if alt.is_some() { 3u8 } else { 2u8 };
writer.write_byte(ordinate_count);
writer.write_f64(*lat);
writer.write_f64(*lon);
if let Some(a) = alt {
writer.write_f64(*a);
}
}
Value::Rect {
min_lat,
min_lon,
max_lat,
max_lon,
} => {
if *min_lat < -90.0 || *min_lat > 90.0 || *max_lat < -90.0 || *max_lat > 90.0 {
return Err(EncodeError::LatitudeOutOfRange {
lat: if *min_lat < -90.0 || *min_lat > 90.0 {
*min_lat
} else {
*max_lat
},
});
}
if *min_lon < -180.0 || *min_lon > 180.0 || *max_lon < -180.0 || *max_lon > 180.0 {
return Err(EncodeError::LongitudeOutOfRange {
lon: if *min_lon < -180.0 || *min_lon > 180.0 {
*min_lon
} else {
*max_lon
},
});
}
if min_lat.is_nan() || min_lon.is_nan() || max_lat.is_nan() || max_lon.is_nan() {
return Err(EncodeError::FloatIsNan);
}
writer.write_f64(*min_lat);
writer.write_f64(*min_lon);
writer.write_f64(*max_lat);
writer.write_f64(*max_lon);
}
Value::Embedding {
sub_type,
dims,
data,
} => {
let expected = sub_type.bytes_for_dims(*dims);
if data.len() != expected {
return Err(EncodeError::EmbeddingDimensionMismatch {
sub_type: *sub_type as u8,
dims: *dims,
data_len: data.len(),
});
}
if *sub_type == EmbeddingSubType::Float32 {
for chunk in data.chunks_exact(4) {
let f = f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
if f.is_nan() {
return Err(EncodeError::FloatIsNan);
}
}
}
writer.write_byte(*sub_type as u8);
writer.write_varint(*dims as u64);
writer.write_bytes(data);
}
}
Ok(())
}
fn decimal_needs_canonicalization(exponent: i32, mantissa: &DecimalMantissa<'_>) -> bool {
match mantissa {
DecimalMantissa::I64(v) => (*v == 0 && exponent != 0) || (*v != 0 && *v % 10 == 0),
DecimalMantissa::Big(bytes) => {
has_redundant_sign_extension(bytes)
|| is_big_mantissa_zero(bytes)
|| is_big_mantissa_divisible_by_10(bytes)
|| twos_complement_bytes_to_i64(bytes).is_some()
}
}
}
fn normalize_decimal_owned(
exponent: i32,
mantissa: &DecimalMantissa<'_>,
) -> Result<(i32, DecimalMantissa<'static>), DecimalNormalizeError> {
match mantissa {
DecimalMantissa::I64(v) => {
let mut value = *v;
let mut exp = exponent;
if value == 0 {
return Ok((0, DecimalMantissa::I64(0)));
}
while value != 0 && value % 10 == 0 {
value /= 10;
exp = exp
.checked_add(1)
.ok_or(DecimalNormalizeError::ExponentOutOfRange)?;
}
Ok((exp, DecimalMantissa::I64(value)))
}
DecimalMantissa::Big(bytes) => {
if bytes.is_empty() {
return Err(DecimalNormalizeError::EmptyBigMantissa);
}
if is_big_mantissa_zero(bytes) {
return Ok((0, DecimalMantissa::I64(0)));
}
let is_negative = bytes[0] & 0x80 != 0;
let mut magnitude = twos_complement_abs_bytes(bytes);
let mut exp = exponent;
let mut steps = 0usize;
loop {
let (quotient, remainder) = divide_unsigned_be_by_10(&magnitude);
if remainder != 0 {
break;
}
steps += 1;
if steps > MAX_DECIMAL_NORMALIZATION_STEPS {
return Err(DecimalNormalizeError::StepLimitExceeded);
}
magnitude = quotient;
exp = exp
.checked_add(1)
.ok_or(DecimalNormalizeError::ExponentOutOfRange)?;
}
let canonical_bytes = signed_bytes_from_magnitude(is_negative, &magnitude);
if let Some(value) = twos_complement_bytes_to_i64(&canonical_bytes) {
Ok((exp, DecimalMantissa::I64(value)))
} else {
Ok((exp, DecimalMantissa::Big(Cow::Owned(canonical_bytes))))
}
}
}
}
fn normalize_decimal_for_decode<'a>(
exponent: i32,
mantissa: DecimalMantissa<'a>,
) -> Result<(i32, DecimalMantissa<'a>), DecodeError> {
if !decimal_needs_canonicalization(exponent, &mantissa) {
return Ok((exponent, mantissa));
}
let (exp, canonical) = normalize_decimal_owned(exponent, &mantissa)
.map_err(DecimalNormalizeError::into_decode_error)?;
match canonical {
DecimalMantissa::I64(value) => Ok((exp, DecimalMantissa::I64(value))),
DecimalMantissa::Big(bytes) => {
Ok((exp, DecimalMantissa::Big(Cow::Owned(bytes.into_owned()))))
}
}
}
fn twos_complement_abs_bytes(bytes: &[u8]) -> Vec<u8> {
if bytes.is_empty() {
return Vec::new();
}
if bytes[0] & 0x80 == 0 {
let first_non_zero = bytes
.iter()
.position(|&byte| byte != 0)
.unwrap_or(bytes.len());
return bytes[first_non_zero..].to_vec();
}
let mut magnitude = vec![0u8; bytes.len()];
let mut carry = 1u16;
for (index, &byte) in bytes.iter().enumerate().rev() {
let sum = (!byte as u16) + carry;
magnitude[index] = sum as u8;
carry = sum >> 8;
}
let first_non_zero = magnitude
.iter()
.position(|&byte| byte != 0)
.unwrap_or(magnitude.len());
magnitude[first_non_zero..].to_vec()
}
fn divide_unsigned_be_by_10(bytes: &[u8]) -> (Vec<u8>, u8) {
let mut quotient = Vec::with_capacity(bytes.len());
let mut remainder = 0u16;
for &byte in bytes {
let current = (remainder << 8) | byte as u16;
quotient.push((current / 10) as u8);
remainder = current % 10;
}
let first_non_zero = quotient
.iter()
.position(|&byte| byte != 0)
.unwrap_or(quotient.len());
(quotient[first_non_zero..].to_vec(), remainder as u8)
}
fn signed_bytes_from_magnitude(is_negative: bool, magnitude: &[u8]) -> Vec<u8> {
if magnitude.is_empty() {
return vec![0];
}
let first_non_zero = magnitude
.iter()
.position(|&byte| byte != 0)
.unwrap_or(magnitude.len());
let magnitude = &magnitude[first_non_zero..];
if magnitude.is_empty() {
return vec![0];
}
if !is_negative {
let mut bytes = magnitude.to_vec();
if bytes[0] & 0x80 != 0 {
bytes.insert(0, 0x00);
}
return bytes;
}
let fits_in_current_width = magnitude[0] < 0x80
|| (magnitude[0] == 0x80 && magnitude[1..].iter().all(|&byte| byte == 0));
let width = if fits_in_current_width {
magnitude.len()
} else {
magnitude.len() + 1
};
let mut bytes = vec![0u8; width];
bytes[width - magnitude.len()..].copy_from_slice(magnitude);
for byte in &mut bytes {
*byte = !*byte;
}
let mut carry = 1u16;
for byte in bytes.iter_mut().rev() {
let sum = *byte as u16 + carry;
*byte = sum as u8;
carry = sum >> 8;
}
while bytes.len() > 1 && bytes[0] == 0xFF && (bytes[1] & 0x80) != 0 {
bytes.remove(0);
}
bytes
}
fn twos_complement_bytes_to_i64(bytes: &[u8]) -> Option<i64> {
if bytes.is_empty() {
return Some(0);
}
if bytes.len() > 8 {
return None;
}
let fill = if bytes[0] & 0x80 != 0 { 0xFF } else { 0x00 };
let mut expanded = [fill; 8];
expanded[8 - bytes.len()..].copy_from_slice(bytes);
Some(i64::from_be_bytes(expanded))
}
fn encode_decimal(
writer: &mut Writer,
exponent: i32,
mantissa: &DecimalMantissa<'_>,
) -> Result<(), EncodeError> {
if let DecimalMantissa::Big(bytes) = mantissa {
if bytes.len() > MAX_BYTES_LEN {
return Err(EncodeError::LengthExceedsLimit {
field: "decimal.mantissa",
len: bytes.len(),
max: MAX_BYTES_LEN,
});
}
}
if !decimal_needs_canonicalization(exponent, mantissa) {
writer.write_signed_varint(exponent as i64);
match mantissa {
DecimalMantissa::I64(value) => {
writer.write_byte(0x00);
writer.write_signed_varint(*value);
}
DecimalMantissa::Big(bytes) => {
writer.write_byte(0x01);
writer.write_varint(bytes.len() as u64);
writer.write_bytes(bytes);
}
}
return Ok(());
}
let (norm_exp, norm_mantissa) =
normalize_decimal_owned(exponent, mantissa).map_err(DecimalNormalizeError::into_encode_error)?;
writer.write_signed_varint(norm_exp as i64);
match norm_mantissa {
DecimalMantissa::I64(value) => {
writer.write_byte(0x00);
writer.write_signed_varint(value);
}
DecimalMantissa::Big(bytes) => {
writer.write_byte(0x01);
writer.write_varint(bytes.len() as u64);
writer.write_bytes(bytes.as_ref());
}
}
Ok(())
}
pub fn encode_property_value(
writer: &mut Writer,
pv: &PropertyValue<'_>,
dict_builder: &mut DictionaryBuilder,
data_type: DataType,
) -> Result<(), EncodeError> {
let prop_index = dict_builder.add_property(pv.property, data_type);
writer.write_varint(prop_index as u64);
encode_value(writer, &pv.value, dict_builder)?;
Ok(())
}
pub fn validate_position(pos: &str) -> Result<(), EncodeError> {
if pos.len() > MAX_POSITION_LEN {
return Err(EncodeError::PositionTooLong);
}
for c in pos.chars() {
if !c.is_ascii_alphanumeric() {
return Err(EncodeError::InvalidPositionChar);
}
}
Ok(())
}
pub fn decode_position<'a>(reader: &mut Reader<'a>) -> Result<Cow<'a, str>, DecodeError> {
let pos = reader.read_str(MAX_POSITION_LEN, "position")?;
for c in pos.chars() {
if !c.is_ascii_alphanumeric() {
return Err(DecodeError::InvalidPositionChar { char: c });
}
}
Ok(Cow::Borrowed(pos))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_boolean_roundtrip() {
for v in [true, false] {
let value = Value::Boolean(v);
let dicts = WireDictionaries::default();
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Boolean, &dicts).unwrap();
assert_eq!(value, decoded);
}
}
#[test]
fn test_integer_roundtrip() {
for v in [0i64, 1, -1, i64::MAX, i64::MIN, 12345678] {
let value = Value::Integer {
value: v,
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let dicts = dict_builder.build();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Integer, &dicts).unwrap();
assert_eq!(value, decoded);
}
}
#[test]
fn test_float_roundtrip() {
for v in [0.0, 1.0, -1.0, f64::INFINITY, f64::NEG_INFINITY, 3.14159] {
let value = Value::Float {
value: v,
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let dicts = dict_builder.build();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Float, &dicts).unwrap();
assert_eq!(value, decoded);
}
}
#[test]
fn test_text_roundtrip() {
let value = Value::Text {
value: Cow::Owned("hello world".to_string()),
language: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let decode_dicts = dict_builder.build();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Text, &decode_dicts).unwrap();
match (&value, &decoded) {
(
Value::Text {
value: v1,
language: l1,
},
Value::Text {
value: v2,
language: l2,
},
) => {
assert_eq!(v1.as_ref(), v2.as_ref());
assert_eq!(l1, l2);
}
_ => panic!("expected Text values"),
}
}
#[test]
fn test_point_roundtrip() {
let value = Value::Point {
lat: 37.7749,
lon: -122.4194,
alt: None,
};
let dicts = WireDictionaries::default();
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Point, &dicts).unwrap();
assert_eq!(value, decoded);
let value_3d = Value::Point {
lat: 37.7749,
lon: -122.4194,
alt: Some(100.0),
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
encode_value(&mut writer, &value_3d, &mut dict_builder).unwrap();
let mut reader = Reader::new(writer.as_bytes());
let decoded_3d = decode_value(&mut reader, DataType::Point, &dicts).unwrap();
assert_eq!(value_3d, decoded_3d);
}
#[test]
fn test_point_validation() {
let value = Value::Point {
lat: 91.0,
lon: 0.0,
alt: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
let result = encode_value(&mut writer, &value, &mut dict_builder);
assert!(result.is_err());
let value = Value::Point {
lat: 0.0,
lon: 181.0,
alt: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
let result = encode_value(&mut writer, &value, &mut dict_builder);
assert!(result.is_err());
let value = Value::Point {
lat: 0.0,
lon: 0.0,
alt: Some(f64::NAN),
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
let result = encode_value(&mut writer, &value, &mut dict_builder);
assert!(result.is_err());
}
#[test]
fn test_rect_roundtrip() {
let value = Value::Rect {
min_lat: 24.5,
min_lon: -125.0,
max_lat: 49.4,
max_lon: -66.9,
};
let dicts = WireDictionaries::default();
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Rect, &dicts).unwrap();
assert_eq!(value, decoded);
}
#[test]
fn test_rect_validation() {
let value = Value::Rect {
min_lat: -91.0,
min_lon: 0.0,
max_lat: 0.0,
max_lon: 0.0,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
let result = encode_value(&mut writer, &value, &mut dict_builder);
assert!(result.is_err());
let value = Value::Rect {
min_lat: 0.0,
min_lon: 0.0,
max_lat: 91.0,
max_lon: 0.0,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
let result = encode_value(&mut writer, &value, &mut dict_builder);
assert!(result.is_err());
let value = Value::Rect {
min_lat: 0.0,
min_lon: -181.0,
max_lat: 0.0,
max_lon: 0.0,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
let result = encode_value(&mut writer, &value, &mut dict_builder);
assert!(result.is_err());
let value = Value::Rect {
min_lat: 0.0,
min_lon: 0.0,
max_lat: 0.0,
max_lon: 181.0,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
let result = encode_value(&mut writer, &value, &mut dict_builder);
assert!(result.is_err());
let value = Value::Rect {
min_lat: f64::NAN,
min_lon: 0.0,
max_lat: 0.0,
max_lon: 0.0,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
let result = encode_value(&mut writer, &value, &mut dict_builder);
assert!(result.is_err());
}
#[test]
fn test_schedule_roundtrip() {
let dicts = WireDictionaries::default();
let mut dict_builder = DictionaryBuilder::new();
let value = Value::Schedule(Cow::Owned(
"BEGIN:VEVENT\r\nDTSTART:20240315T090000Z\r\nDTEND:20240315T100000Z\r\nEND:VEVENT"
.to_string(),
));
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Schedule, &dicts).unwrap();
match (&value, &decoded) {
(Value::Schedule(s1), Value::Schedule(s2)) => {
assert_eq!(s1.as_ref(), s2.as_ref());
}
_ => panic!("expected Schedule values"),
}
}
#[test]
fn test_embedding_roundtrip() {
let value = Value::Embedding {
sub_type: EmbeddingSubType::Float32,
dims: 4,
data: Cow::Owned(vec![0u8; 16]), };
let dicts = WireDictionaries::default();
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Embedding, &dicts).unwrap();
match (&value, &decoded) {
(
Value::Embedding {
sub_type: s1,
dims: d1,
data: data1,
},
Value::Embedding {
sub_type: s2,
dims: d2,
data: data2,
},
) => {
assert_eq!(s1, s2);
assert_eq!(d1, d2);
assert_eq!(data1.as_ref(), data2.as_ref());
}
_ => panic!("expected Embedding values"),
}
}
#[test]
fn test_decimal_normalized() {
let dicts = WireDictionaries::default();
let valid = Value::Decimal {
exponent: -2,
mantissa: DecimalMantissa::I64(1234),
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &valid, &mut dict_builder).is_ok());
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap();
match decoded {
Value::Decimal {
exponent, mantissa, ..
} => {
assert_eq!(exponent, -2);
assert_eq!(mantissa, DecimalMantissa::I64(1234));
}
_ => panic!("expected Decimal"),
}
let trailing = Value::Decimal {
exponent: -2,
mantissa: DecimalMantissa::I64(1230),
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &trailing, &mut dict_builder).is_ok());
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap();
match decoded {
Value::Decimal {
exponent, mantissa, ..
} => {
assert_eq!(exponent, -1);
assert_eq!(mantissa, DecimalMantissa::I64(123));
}
_ => panic!("expected Decimal"),
}
}
#[test]
fn test_decimal_normalize_zero() {
let dicts = WireDictionaries::default();
let zero = Value::Decimal {
exponent: 5,
mantissa: DecimalMantissa::I64(0),
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &zero, &mut dict_builder).is_ok());
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap();
match decoded {
Value::Decimal {
exponent, mantissa, ..
} => {
assert_eq!(exponent, 0);
assert_eq!(mantissa, DecimalMantissa::I64(0));
}
_ => panic!("expected Decimal"),
}
}
#[test]
fn test_decimal_normalize_100_exp_neg2() {
let dicts = WireDictionaries::default();
let dollar = Value::Decimal {
exponent: -2,
mantissa: DecimalMantissa::I64(100),
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &dollar, &mut dict_builder).is_ok());
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap();
match decoded {
Value::Decimal {
exponent, mantissa, ..
} => {
assert_eq!(exponent, 0);
assert_eq!(mantissa, DecimalMantissa::I64(1));
}
_ => panic!("expected Decimal"),
}
}
#[test]
fn test_decimal_normalize_negative() {
let dicts = WireDictionaries::default();
let neg = Value::Decimal {
exponent: 0,
mantissa: DecimalMantissa::I64(-500),
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &neg, &mut dict_builder).is_ok());
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap();
match decoded {
Value::Decimal {
exponent, mantissa, ..
} => {
assert_eq!(exponent, 2);
assert_eq!(mantissa, DecimalMantissa::I64(-5));
}
_ => panic!("expected Decimal"),
}
}
#[test]
fn test_date_roundtrip() {
let dicts = WireDictionaries::default();
let mut dict_builder = DictionaryBuilder::new();
let test_cases = [
"1970-01-01Z", "2024-03-15Z", "2024-03-15+05:30", "2024-03-15-08:00", ];
for date_str in test_cases {
let value = Value::Date(Cow::Owned(date_str.to_string()));
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Date, &dicts).unwrap();
match (&value, &decoded) {
(Value::Date(v1), Value::Date(v2)) => {
assert_eq!(
v1.as_ref(),
v2.as_ref(),
"Roundtrip failed for {}",
date_str
);
}
_ => panic!("expected Date values"),
}
}
}
#[test]
fn test_time_roundtrip() {
let dicts = WireDictionaries::default();
let mut dict_builder = DictionaryBuilder::new();
let test_cases = [
"00:00:00Z", "14:30:00Z", "14:30:00.5+05:30", "23:59:59.999999Z", "00:00:00-05:00", ];
for time_str in test_cases {
let value = Value::Time(Cow::Owned(time_str.to_string()));
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Time, &dicts).unwrap();
match (&value, &decoded) {
(Value::Time(v1), Value::Time(v2)) => {
assert_eq!(
v1.as_ref(),
v2.as_ref(),
"Roundtrip failed for {}",
time_str
);
}
_ => panic!("expected Time values"),
}
}
}
#[test]
fn test_datetime_roundtrip() {
let dicts = WireDictionaries::default();
let mut dict_builder = DictionaryBuilder::new();
let test_cases = [
"1970-01-01T00:00:00Z", "2024-03-15T14:30:00Z", "2024-03-15T14:30:00+05:30", "2024-03-15T14:30:00.123456Z", ];
for datetime_str in test_cases {
let value = Value::Datetime(Cow::Owned(datetime_str.to_string()));
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Datetime, &dicts).unwrap();
match (&value, &decoded) {
(Value::Datetime(v1), Value::Datetime(v2)) => {
assert_eq!(
v1.as_ref(),
v2.as_ref(),
"Roundtrip failed for {}",
datetime_str
);
}
_ => panic!("expected Datetime values"),
}
}
}
#[test]
fn test_date_validation() {
let mut dict_builder = DictionaryBuilder::new();
let invalid = Value::Date(Cow::Borrowed("not-a-date"));
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &invalid, &mut dict_builder).is_err());
let invalid_month = Value::Date(Cow::Borrowed("2024-13-01"));
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &invalid_month, &mut dict_builder).is_err());
}
#[test]
fn test_time_validation() {
let mut dict_builder = DictionaryBuilder::new();
let invalid = Value::Time(Cow::Borrowed("not:a:time"));
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &invalid, &mut dict_builder).is_err());
let invalid_hours = Value::Time(Cow::Borrowed("24:00:00"));
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &invalid_hours, &mut dict_builder).is_err());
let invalid_minutes = Value::Time(Cow::Borrowed("14:60:00"));
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &invalid_minutes, &mut dict_builder).is_err());
}
#[test]
fn test_datetime_validation() {
let mut dict_builder = DictionaryBuilder::new();
let invalid = Value::Datetime(Cow::Borrowed("not-a-datetime"));
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &invalid, &mut dict_builder).is_err());
let invalid_date = Value::Datetime(Cow::Borrowed("2024-13-01T00:00:00Z"));
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &invalid_date, &mut dict_builder).is_err());
let invalid_time = Value::Datetime(Cow::Borrowed("2024-01-01T25:00:00Z"));
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &invalid_time, &mut dict_builder).is_err());
}
#[test]
fn test_big_decimal_normalization_helpers() {
assert!(!is_big_mantissa_zero(&[]));
assert!(is_big_mantissa_zero(&[0]));
assert!(is_big_mantissa_zero(&[0, 0, 0]));
assert!(!is_big_mantissa_zero(&[1]));
assert!(!is_big_mantissa_zero(&[0, 1]));
assert!(is_big_mantissa_divisible_by_10(&[0x0A])); assert!(is_big_mantissa_divisible_by_10(&[0x14])); assert!(is_big_mantissa_divisible_by_10(&[0x64])); assert!(is_big_mantissa_divisible_by_10(&[0x01, 0xF4]));
assert!(!is_big_mantissa_divisible_by_10(&[0x01])); assert!(!is_big_mantissa_divisible_by_10(&[0x07])); assert!(!is_big_mantissa_divisible_by_10(&[0x0B])); assert!(!is_big_mantissa_divisible_by_10(&[0x15]));
assert!(is_big_mantissa_divisible_by_10(&[0xF6])); assert!(is_big_mantissa_divisible_by_10(&[0xEC])); assert!(!is_big_mantissa_divisible_by_10(&[0xFF])); assert!(!is_big_mantissa_divisible_by_10(&[0xF9])); }
fn multiply_unsigned_be_by_10(bytes: &[u8]) -> Vec<u8> {
let mut result = Vec::with_capacity(bytes.len() + 1);
let mut carry = 0u16;
for &byte in bytes.iter().rev() {
let product = byte as u16 * 10 + carry;
result.push((product & 0xFF) as u8);
carry = product >> 8;
}
while carry != 0 {
result.push((carry & 0xFF) as u8);
carry >>= 8;
}
result.reverse();
result
}
fn decimal_power_of_ten_bytes(power: usize) -> Vec<u8> {
let mut magnitude = vec![1u8];
for _ in 0..power {
magnitude = multiply_unsigned_be_by_10(&magnitude);
}
signed_bytes_from_magnitude(false, &magnitude)
}
#[test]
fn test_big_decimal_normalization_encode() {
let dicts = WireDictionaries::default();
let valid = Value::Decimal {
exponent: 0,
mantissa: DecimalMantissa::Big(Cow::Owned(vec![0x07])), unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &valid, &mut dict_builder).is_ok());
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap();
match decoded {
Value::Decimal {
exponent, mantissa, ..
} => {
assert_eq!(exponent, 0);
assert_eq!(mantissa, DecimalMantissa::I64(7)); }
_ => panic!("expected Decimal"),
}
let was_invalid = Value::Decimal {
exponent: 0,
mantissa: DecimalMantissa::Big(Cow::Owned(vec![0x0A])), unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &was_invalid, &mut dict_builder).is_ok());
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap();
match decoded {
Value::Decimal {
exponent, mantissa, ..
} => {
assert_eq!(exponent, 1);
assert_eq!(mantissa, DecimalMantissa::I64(1));
}
_ => panic!("expected Decimal"),
}
let was_invalid_zero = Value::Decimal {
exponent: 1,
mantissa: DecimalMantissa::Big(Cow::Owned(vec![0x00])),
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &was_invalid_zero, &mut dict_builder).is_ok());
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap();
match decoded {
Value::Decimal {
exponent, mantissa, ..
} => {
assert_eq!(exponent, 0);
assert_eq!(mantissa, DecimalMantissa::I64(0));
}
_ => panic!("expected Decimal"),
}
let valid_zero = Value::Decimal {
exponent: 0,
mantissa: DecimalMantissa::Big(Cow::Owned(vec![0x00])),
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
assert!(encode_value(&mut writer, &valid_zero, &mut dict_builder).is_ok());
}
#[test]
fn test_decode_decimal_normalizes_large_big_mantissa_without_truncation() {
let dicts = WireDictionaries::default();
let large = vec![
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
];
let mut writer = Writer::new();
writer.write_signed_varint(0);
writer.write_byte(0x01);
writer.write_varint(large.len() as u64);
writer.write_bytes(&large);
writer.write_varint(0);
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap();
match decoded {
Value::Decimal {
exponent,
mantissa: DecimalMantissa::Big(bytes),
unit,
} => {
assert_eq!(exponent, 1);
assert_eq!(
bytes.as_ref(),
&[
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00
]
);
assert_eq!(unit, None);
}
other => panic!("expected large normalized Decimal, got {other:?}"),
}
}
#[test]
fn test_decode_decimal_keeps_borrowed_large_big_mantissa_when_already_canonical() {
let dicts = WireDictionaries::default();
let canonical = vec![
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
];
let mut writer = Writer::new();
writer.write_signed_varint(0);
writer.write_byte(0x01);
writer.write_varint(canonical.len() as u64);
writer.write_bytes(&canonical);
writer.write_varint(0);
let encoded = writer.as_bytes().to_vec();
let mut reader = Reader::new(&encoded);
let decoded = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap();
match decoded {
Value::Decimal {
exponent,
mantissa: DecimalMantissa::Big(bytes),
unit,
} => {
assert_eq!(exponent, 0);
assert_eq!(bytes.as_ref(), canonical.as_slice());
assert!(matches!(bytes, Cow::Borrowed(_)));
assert_eq!(unit, None);
}
other => panic!("expected borrowed large Decimal, got {other:?}"),
}
}
#[test]
fn test_encode_decimal_normalizes_large_big_mantissa_without_truncation() {
let dicts = WireDictionaries::default();
let mut dict_builder = DictionaryBuilder::new();
let large = vec![
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
];
let value = Value::Decimal {
exponent: 0,
mantissa: DecimalMantissa::Big(Cow::Owned(large)),
unit: None,
};
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap();
match decoded {
Value::Decimal {
exponent,
mantissa: DecimalMantissa::Big(bytes),
..
} => {
assert_eq!(exponent, 1);
assert_eq!(
bytes.as_ref(),
&[
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00
]
);
}
other => panic!("expected large normalized Decimal, got {other:?}"),
}
}
#[test]
fn test_encode_decimal_trims_non_minimal_big_mantissa_bytes() {
let dicts = WireDictionaries::default();
let mut dict_builder = DictionaryBuilder::new();
let value = Value::Decimal {
exponent: 0,
mantissa: DecimalMantissa::Big(Cow::Owned(vec![
0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01,
])),
unit: None,
};
let mut writer = Writer::new();
encode_value(&mut writer, &value, &mut dict_builder).unwrap();
let mut reader = Reader::new(writer.as_bytes());
let decoded = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap();
match decoded {
Value::Decimal {
exponent,
mantissa: DecimalMantissa::Big(bytes),
..
} => {
assert_eq!(exponent, 0);
assert_eq!(
bytes.as_ref(),
&[0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01]
);
}
other => panic!("expected canonical big Decimal, got {other:?}"),
}
}
#[test]
fn test_decode_decimal_rejects_empty_big_mantissa_bytes() {
let dicts = WireDictionaries::default();
let mut writer = Writer::new();
writer.write_signed_varint(0);
writer.write_byte(0x01);
writer.write_varint(0);
writer.write_varint(0);
let mut reader = Reader::new(writer.as_bytes());
let err = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap_err();
assert_eq!(err, DecodeError::DecimalMantissaNotMinimal);
}
#[test]
fn test_decode_decimal_rejects_exponent_outside_i32_range() {
let dicts = WireDictionaries::default();
let mut writer = Writer::new();
writer.write_signed_varint(i32::MAX as i64 + 1);
writer.write_byte(0x00);
writer.write_signed_varint(1);
writer.write_varint(0);
let mut reader = Reader::new(writer.as_bytes());
let err = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap_err();
assert_eq!(
err,
DecodeError::MalformedEncoding {
context: DECIMAL_EXPONENT_OUT_OF_RANGE,
}
);
}
#[test]
fn test_decode_decimal_rejects_exponent_overflow_during_normalization() {
let dicts = WireDictionaries::default();
let mut writer = Writer::new();
writer.write_signed_varint(i32::MAX as i64);
writer.write_byte(0x00);
writer.write_signed_varint(10);
writer.write_varint(0);
let mut reader = Reader::new(writer.as_bytes());
let err = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap_err();
assert_eq!(
err,
DecodeError::MalformedEncoding {
context: DECIMAL_EXPONENT_OUT_OF_RANGE,
}
);
}
#[test]
fn test_encode_decimal_rejects_exponent_overflow_during_normalization() {
let value = Value::Decimal {
exponent: i32::MAX,
mantissa: DecimalMantissa::I64(10),
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
let err = encode_value(&mut writer, &value, &mut dict_builder).unwrap_err();
assert_eq!(
err,
EncodeError::InvalidInput {
context: DECIMAL_EXPONENT_OUT_OF_RANGE,
}
);
}
#[test]
fn test_encode_decimal_rejects_empty_big_mantissa_bytes() {
let value = Value::Decimal {
exponent: 0,
mantissa: DecimalMantissa::Big(Cow::Owned(Vec::new())),
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
let err = encode_value(&mut writer, &value, &mut dict_builder).unwrap_err();
assert_eq!(
err,
EncodeError::InvalidInput {
context: DECIMAL_EMPTY_BIG_MANTISSA,
}
);
}
#[test]
fn test_encode_decimal_rejects_big_mantissa_length_over_limit() {
let mut oversized = vec![0u8; MAX_BYTES_LEN + 1];
oversized[0] = 1;
let value = Value::Decimal {
exponent: 0,
mantissa: DecimalMantissa::Big(Cow::Owned(oversized)),
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
let err = encode_value(&mut writer, &value, &mut dict_builder).unwrap_err();
assert_eq!(
err,
EncodeError::LengthExceedsLimit {
field: "decimal.mantissa",
len: MAX_BYTES_LEN + 1,
max: MAX_BYTES_LEN,
}
);
}
#[test]
fn test_decode_decimal_rejects_excessive_normalization_steps() {
let dicts = WireDictionaries::default();
let bytes = decimal_power_of_ten_bytes(MAX_DECIMAL_NORMALIZATION_STEPS + 1);
let mut writer = Writer::new();
writer.write_signed_varint(0);
writer.write_byte(0x01);
writer.write_varint(bytes.len() as u64);
writer.write_bytes(&bytes);
writer.write_varint(0);
let mut reader = Reader::new(writer.as_bytes());
let err = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap_err();
assert_eq!(
err,
DecodeError::MalformedEncoding {
context: DECIMAL_NORMALIZATION_STEP_LIMIT_EXCEEDED,
}
);
}
#[test]
fn test_encode_decimal_rejects_excessive_normalization_steps() {
let value = Value::Decimal {
exponent: 0,
mantissa: DecimalMantissa::Big(Cow::Owned(decimal_power_of_ten_bytes(
MAX_DECIMAL_NORMALIZATION_STEPS + 1,
))),
unit: None,
};
let mut dict_builder = DictionaryBuilder::new();
let mut writer = Writer::new();
let err = encode_value(&mut writer, &value, &mut dict_builder).unwrap_err();
assert_eq!(
err,
EncodeError::InvalidInput {
context: DECIMAL_NORMALIZATION_STEP_LIMIT_EXCEEDED,
}
);
}
#[test]
fn test_decode_decimal_rejects_big_mantissa_length_over_limit() {
let dicts = WireDictionaries::default();
let mut writer = Writer::new();
writer.write_signed_varint(0);
writer.write_byte(0x01);
writer.write_varint(MAX_BYTES_LEN as u64 + 1);
let mut reader = Reader::new(writer.as_bytes());
let err = decode_value(&mut reader, DataType::Decimal, &dicts).unwrap_err();
assert_eq!(
err,
DecodeError::LengthExceedsLimit {
field: "decimal.mantissa",
len: MAX_BYTES_LEN + 1,
max: MAX_BYTES_LEN,
}
);
}
}