use bytes::Bytes;
use crate::error::{new_blob_error, new_wire_error, BlobError, Result};
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum BlobKind {
OsmHeader,
OsmData,
Unknown(String),
}
#[derive(Clone, Debug)]
pub(crate) struct WireBlobHeader {
pub blob_type: BlobKind,
pub datasize: i32,
pub indexdata: Option<[u8; crate::blob_meta::INDEX_SIZE]>,
pub tagdata: Option<Box<[u8]>>,
}
impl WireBlobHeader {
#[hotpath::measure]
pub fn parse(data: &[u8], parse_tagdata: bool, parse_indexdata: bool) -> Result<Self> {
use super::wire::Cursor;
let mut cursor = Cursor::new(data);
let mut blob_type = BlobKind::Unknown(String::new());
let mut datasize: i32 = 0;
let mut indexdata: Option<[u8; crate::blob_meta::INDEX_SIZE]> = None;
let mut tagdata: Option<Box<[u8]>> = None;
while let Some((field, wire_type)) = cursor.read_tag()? {
match field {
1 => {
let bytes = cursor.read_len_delimited()?;
blob_type = match bytes {
b"OSMHeader" => BlobKind::OsmHeader,
b"OSMData" => BlobKind::OsmData,
_ => BlobKind::Unknown(
String::from_utf8(bytes.to_vec())
.map_err(|_| new_wire_error("invalid UTF-8 in BlobHeader type"))?,
),
};
}
2 if parse_indexdata => {
let bytes = cursor.read_len_delimited()?;
let len = bytes.len();
if len == crate::blob_meta::INDEX_SIZE || len == 26 {
let mut buf = [0u8; crate::blob_meta::INDEX_SIZE];
buf[..len].copy_from_slice(bytes);
indexdata = Some(buf);
}
}
3 => {
#[allow(clippy::cast_possible_truncation)]
{ datasize = cursor.read_varint()? as i32; }
}
4 if parse_tagdata => {
let bytes = cursor.read_len_delimited()?;
if !bytes.is_empty() {
tagdata = Some(bytes.into());
}
}
_ => cursor.skip_field(wire_type)?,
}
}
Ok(WireBlobHeader { blob_type, datasize, indexdata, tagdata })
}
}
#[derive(Clone, Debug)]
pub(crate) enum BlobData {
Raw(Bytes),
Zlib(Bytes),
Zstd(Bytes),
}
#[derive(Clone, Debug)]
pub(crate) struct WireBlob {
pub data: Option<BlobData>,
pub raw_size: Option<i32>,
}
impl WireBlob {
pub fn parse(input: &Bytes) -> Result<Self> {
use super::wire::Cursor;
let mut cursor = Cursor::new(input);
let mut data: Option<BlobData> = None;
let mut raw_size: Option<i32> = None;
while let Some((field, wire_type)) = cursor.read_tag()? {
match field {
1 => {
let slice = cursor.read_len_delimited()?;
let offset = slice.as_ptr() as usize - input.as_ptr() as usize;
data = Some(BlobData::Raw(input.slice(offset..offset + slice.len())));
}
2 => {
#[allow(clippy::cast_possible_truncation)]
{ raw_size = Some(cursor.read_varint()? as i32); }
}
3 => {
let slice = cursor.read_len_delimited()?;
let offset = slice.as_ptr() as usize - input.as_ptr() as usize;
data = Some(BlobData::Zlib(input.slice(offset..offset + slice.len())));
}
7 => {
let slice = cursor.read_len_delimited()?;
let offset = slice.as_ptr() as usize - input.as_ptr() as usize;
data = Some(BlobData::Zstd(input.slice(offset..offset + slice.len())));
}
_ => cursor.skip_field(wire_type)?,
}
}
Ok(WireBlob { data, raw_size })
}
pub fn parse_slice(data: &[u8]) -> Result<Self> {
let bytes = Bytes::copy_from_slice(data);
Self::parse(&bytes)
}
#[allow(clippy::cast_sign_loss)]
pub fn estimated_capacity(&self) -> usize {
self.raw_size.unwrap_or(0).max(0) as usize
}
}
pub const MAX_BLOB_HEADER_SIZE: u64 = 64 * 1024;
pub const MAX_BLOB_MESSAGE_SIZE: u64 = 32 * 1024 * 1024;
#[allow(clippy::type_complexity)]
pub(crate) fn parse_blob_header_with_index(
header_bytes: &[u8],
) -> Result<(BlobKind, usize, Option<[u8; crate::blob_meta::INDEX_SIZE]>, Option<Box<[u8]>>)> {
let header = WireBlobHeader::parse(header_bytes, true, true)?;
if header.datasize < 0 {
return Err(new_blob_error(BlobError::InvalidDataSize {
size: header.datasize,
}));
}
#[allow(clippy::cast_sign_loss)]
Ok((header.blob_type, header.datasize as usize, header.indexdata, header.tagdata))
}