blob: 8728c9eaffe5f9c7b0b855f9a6b50c55e12d60d3 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//! Utility functions, like configuring various global settings.
use crate::{AvroResult, error::Details, schema::Documentation};
use serde_json::{Map, Value};
use std::{
io::{Read, Write},
sync::OnceLock,
};
/// Maximum number of bytes that can be allocated when decoding Avro-encoded values.
///
/// This is a protection against ill-formed data, whose length field might be interpreted as enormous.
///
/// See [`max_allocation_bytes`] to change this limit.
pub const DEFAULT_MAX_ALLOCATION_BYTES: usize = 512 * 1024 * 1024;
static MAX_ALLOCATION_BYTES: OnceLock<usize> = OnceLock::new();
/// Whether to set serialization & deserialization traits as `human_readable` or not.
///
/// See [`set_serde_human_readable`] to change this value.
pub const DEFAULT_SERDE_HUMAN_READABLE: bool = false;
/// Whether the serializer and deserializer should indicate to types that the format is human-readable.
// crate-visible for testing
pub(crate) static SERDE_HUMAN_READABLE: OnceLock<bool> = OnceLock::new();
pub(crate) trait MapHelper {
fn string(&self, key: &str) -> Option<&str>;
fn name(&self) -> Option<&str> {
self.string("name")
}
fn doc(&self) -> Documentation {
self.string("doc").map(Into::into)
}
fn aliases(&self) -> Option<Vec<String>>;
}
impl MapHelper for Map<String, Value> {
fn string(&self, key: &str) -> Option<&str> {
self.get(key).and_then(|v| v.as_str())
}
fn aliases(&self) -> Option<Vec<String>> {
// FIXME no warning when aliases aren't a json array of json strings
self.get("aliases")
.and_then(|aliases| aliases.as_array())
.and_then(|aliases| {
aliases
.iter()
.map(|alias| alias.as_str())
.map(|alias| alias.map(|a| a.to_string()))
.collect::<Option<_>>()
})
}
}
pub(crate) fn read_long<R: Read>(reader: &mut R) -> AvroResult<i64> {
zag_i64(reader)
}
/// Write the number as a zigzagged varint to the writer.
pub(crate) fn zig_i32<W: Write>(n: i32, buffer: W) -> AvroResult<usize> {
zig_i64(n as i64, buffer)
}
/// Write the number as a zigzagged varint to the writer.
pub(crate) fn zig_i64<W: Write>(n: i64, writer: W) -> AvroResult<usize> {
let zigzagged = ((n << 1) ^ (n >> 63)) as u64;
encode_variable(zigzagged, writer)
}
/// Decode a zigzagged varint from the reader.
pub(crate) fn zag_i32<R: Read>(reader: &mut R) -> AvroResult<i32> {
let i = zag_i64(reader)?;
i32::try_from(i).map_err(|e| Details::ZagI32(e, i).into())
}
/// Decode a zigzagged varint from the reader.
pub(crate) fn zag_i64<R: Read>(reader: &mut R) -> AvroResult<i64> {
let z = decode_variable(reader)?;
Ok(if z & 0x1 == 0 {
(z >> 1) as i64
} else {
!(z >> 1) as i64
})
}
/// Write the number as a varint to the writer.
///
/// Note: this function does not do zigzag encoding, for that see [`zig_i32`] and [`zig_i64`].
fn encode_variable<W: Write>(mut zigzagged: u64, mut writer: W) -> AvroResult<usize> {
// Ensure the number is little endian for the varint encoding (no-op on LE systems)
zigzagged = zigzagged.to_le();
// Encode the number as a varint
let mut buffer = [0u8; 10];
let mut i: usize = 0;
loop {
if zigzagged <= 0x7F {
buffer[i] = (zigzagged & 0x7F) as u8;
i += 1;
break;
} else {
buffer[i] = (0x80 | (zigzagged & 0x7F)) as u8;
i += 1;
zigzagged >>= 7;
}
}
writer
.write_all(&buffer[..i])
.map_err(Details::WriteBytes)?;
Ok(i)
}
/// Read a varint from the reader.
///
/// Note: this function does not do zigzag decoding, for that see [`zag_i32`] and [`zag_i64`].
fn decode_variable<R: Read>(reader: &mut R) -> AvroResult<u64> {
let mut i = 0u64;
let mut buf = [0u8; 1];
let mut j = 0;
loop {
if j > 9 {
// if j * 7 > 64
return Err(Details::IntegerOverflow.into());
}
reader
.read_exact(&mut buf[..])
.map_err(Details::ReadVariableIntegerBytes)?;
i |= (u64::from(buf[0] & 0x7F)) << (j * 7);
if (buf[0] >> 7) == 0 {
break;
} else {
j += 1;
}
}
Ok(u64::from_le(i))
}
/// Set the maximum number of bytes that can be allocated when decoding data.
///
/// This function only changes the setting once. On subsequent calls the value will stay the same
/// as the first time it is called. It is automatically called on first allocation and defaults to
/// [`DEFAULT_MAX_ALLOCATION_BYTES`].
///
/// # Returns
/// The configured maximum, which might be different from what the function was called with if the
/// value was already set before.
pub fn max_allocation_bytes(num_bytes: usize) -> usize {
*MAX_ALLOCATION_BYTES.get_or_init(|| num_bytes)
}
pub(crate) fn safe_len(len: usize) -> AvroResult<usize> {
let max_bytes = max_allocation_bytes(DEFAULT_MAX_ALLOCATION_BYTES);
if len <= max_bytes {
Ok(len)
} else {
Err(Details::MemoryAllocation {
desired: len,
maximum: max_bytes,
}
.into())
}
}
/// Set whether the serializer and deserializer should indicate to types that the format is human-readable.
///
/// This function only changes the setting once. On subsequent calls the value will stay the same
/// as the first time it is called. It is automatically called on first allocation and defaults to
/// [`DEFAULT_SERDE_HUMAN_READABLE`].
///
/// *NOTE*: Changing this setting can change the output of [`from_value`](crate::from_value) and the
/// accepted input of [`to_value`](crate::to_value).
///
/// # Returns
/// The configured human-readable value, which might be different from what the function was called
/// with if the value was already set before.
pub fn set_serde_human_readable(human_readable: bool) -> bool {
*SERDE_HUMAN_READABLE.get_or_init(|| human_readable)
}
pub(crate) fn is_human_readable() -> bool {
*SERDE_HUMAN_READABLE.get_or_init(|| DEFAULT_SERDE_HUMAN_READABLE)
}
#[cfg(test)]
mod tests {
use super::*;
use apache_avro_test_helper::TestResult;
use pretty_assertions::assert_eq;
#[test]
fn test_zigzag() {
let mut a = Vec::new();
let mut b = Vec::new();
zig_i32(42i32, &mut a).unwrap();
zig_i64(42i64, &mut b).unwrap();
assert_eq!(a, b);
}
#[test]
fn test_zig_i64() {
let mut s = Vec::new();
zig_i64(0, &mut s).unwrap();
assert_eq!(s, [0]);
s.clear();
zig_i64(-1, &mut s).unwrap();
assert_eq!(s, [1]);
s.clear();
zig_i64(1, &mut s).unwrap();
assert_eq!(s, [2]);
s.clear();
zig_i64(-64, &mut s).unwrap();
assert_eq!(s, [127]);
s.clear();
zig_i64(64, &mut s).unwrap();
assert_eq!(s, [128, 1]);
s.clear();
zig_i64(i32::MAX as i64, &mut s).unwrap();
assert_eq!(s, [254, 255, 255, 255, 15]);
s.clear();
zig_i64(i32::MAX as i64 + 1, &mut s).unwrap();
assert_eq!(s, [128, 128, 128, 128, 16]);
s.clear();
zig_i64(i32::MIN as i64, &mut s).unwrap();
assert_eq!(s, [255, 255, 255, 255, 15]);
s.clear();
zig_i64(i32::MIN as i64 - 1, &mut s).unwrap();
assert_eq!(s, [129, 128, 128, 128, 16]);
s.clear();
zig_i64(i64::MAX, &mut s).unwrap();
assert_eq!(s, [254, 255, 255, 255, 255, 255, 255, 255, 255, 1]);
s.clear();
zig_i64(i64::MIN, &mut s).unwrap();
assert_eq!(s, [255, 255, 255, 255, 255, 255, 255, 255, 255, 1]);
}
#[test]
fn test_zig_i32() {
let mut s = Vec::new();
zig_i32(i32::MAX / 2, &mut s).unwrap();
assert_eq!(s, [254, 255, 255, 255, 7]);
s.clear();
zig_i32(i32::MIN / 2, &mut s).unwrap();
assert_eq!(s, [255, 255, 255, 255, 7]);
s.clear();
zig_i32(-(i32::MIN / 2), &mut s).unwrap();
assert_eq!(s, [128, 128, 128, 128, 8]);
s.clear();
zig_i32(i32::MIN / 2 - 1, &mut s).unwrap();
assert_eq!(s, [129, 128, 128, 128, 8]);
s.clear();
zig_i32(i32::MAX, &mut s).unwrap();
assert_eq!(s, [254, 255, 255, 255, 15]);
s.clear();
zig_i32(i32::MIN, &mut s).unwrap();
assert_eq!(s, [255, 255, 255, 255, 15]);
}
#[test]
fn test_overflow() {
let causes_left_shift_overflow: &[u8] = &[0xe1; 10];
assert!(matches!(
decode_variable(&mut &*causes_left_shift_overflow)
.unwrap_err()
.details(),
Details::IntegerOverflow
));
}
#[test]
fn test_safe_len() -> TestResult {
assert_eq!(42usize, safe_len(42usize)?);
assert!(safe_len(1024 * 1024 * 1024).is_err());
Ok(())
}
}