use crate::read::elements::MemberType;
use super::interner::StringInterner;
#[inline]
fn read_i64_le(data: &[u8], offset: usize) -> i64 {
let bytes: [u8; 8] = data[offset..offset + 8].try_into().expect("slice length");
i64::from_le_bytes(bytes)
}
#[inline]
fn read_i32_le(data: &[u8], offset: usize) -> i32 {
let bytes: [u8; 4] = data[offset..offset + 4].try_into().expect("slice length");
i32::from_le_bytes(bytes)
}
#[inline]
fn read_u32_le(data: &[u8], offset: usize) -> u32 {
let bytes: [u8; 4] = data[offset..offset + 4].try_into().expect("slice length");
u32::from_le_bytes(bytes)
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
pub(super) fn member_type_to_byte(mt: MemberType) -> u8 {
match mt {
MemberType::Node => 0,
MemberType::Way => 1,
MemberType::Relation => 2,
MemberType::Unknown(v) => v as u8,
}
}
fn byte_to_member_type(b: u8) -> MemberType {
match b {
0 => MemberType::Node,
1 => MemberType::Way,
2 => MemberType::Relation,
other => MemberType::Unknown(i32::from(other)),
}
}
#[derive(Clone, Copy, Debug, Default)]
pub(super) struct ArenaMeta {
pub(super) flags: u8,
pub(super) version: i32,
pub(super) timestamp: i64,
pub(super) changeset: i64,
pub(super) uid: i32,
pub(super) user_id: u32,
}
pub(super) const META_LEN: usize = 29;
pub(super) const META_FLAG_PRESENT: u8 = 1;
pub(super) const META_FLAG_HIDDEN: u8 = 2;
pub(super) const META_FLAG_HAS_USER: u8 = 4;
fn arena_append_meta(arena: &mut Vec<u8>, meta: &ArenaMeta) {
arena.push(meta.flags);
arena.extend_from_slice(&meta.version.to_le_bytes());
arena.extend_from_slice(&meta.timestamp.to_le_bytes());
arena.extend_from_slice(&meta.changeset.to_le_bytes());
arena.extend_from_slice(&meta.uid.to_le_bytes());
arena.extend_from_slice(&meta.user_id.to_le_bytes());
}
fn read_meta<'a>(
data: &'a [u8],
offset: usize,
interner: &'a StringInterner,
) -> Option<crate::write::block_builder::Metadata<'a>> {
let flags = data[offset];
if flags & META_FLAG_PRESENT == 0 {
return None;
}
Some(crate::write::block_builder::Metadata {
version: read_i32_le(data, offset + 1),
timestamp: read_i64_le(data, offset + 5),
changeset: read_i64_le(data, offset + 13),
uid: read_i32_le(data, offset + 21),
user: if flags & META_FLAG_HAS_USER != 0 {
interner.resolve(read_u32_le(data, offset + 25))
} else {
""
},
visible: flags & META_FLAG_HIDDEN == 0,
})
}
#[allow(clippy::cast_possible_truncation)]
pub(super) fn arena_append_node(
arena: &mut Vec<u8>,
id: i64,
lat: i32,
lon: i32,
tags: &[(u32, &str)],
meta: &ArenaMeta,
) -> u32 {
let offset = arena.len() as u32;
arena.extend_from_slice(&id.to_le_bytes());
arena.extend_from_slice(&lat.to_le_bytes());
arena.extend_from_slice(&lon.to_le_bytes());
let tag_count = tags.len() as u32;
arena.extend_from_slice(&tag_count.to_le_bytes());
arena_append_meta(arena, meta);
for &(key_id, value) in tags {
arena.extend_from_slice(&key_id.to_le_bytes());
let value_len = value.len() as u32;
arena.extend_from_slice(&value_len.to_le_bytes());
arena.extend_from_slice(value.as_bytes());
}
offset
}
#[allow(clippy::cast_possible_truncation)]
pub(super) fn arena_append_way(
arena: &mut Vec<u8>,
id: i64,
refs: &[i64],
tags: &[(u32, &str)],
meta: &ArenaMeta,
) -> u32 {
let offset = arena.len() as u32;
arena.extend_from_slice(&id.to_le_bytes());
let ref_count = refs.len() as u32;
arena.extend_from_slice(&ref_count.to_le_bytes());
let tag_count = tags.len() as u32;
arena.extend_from_slice(&tag_count.to_le_bytes());
arena_append_meta(arena, meta);
for &r in refs {
arena.extend_from_slice(&r.to_le_bytes());
}
for &(key_id, value) in tags {
arena.extend_from_slice(&key_id.to_le_bytes());
let value_len = value.len() as u32;
arena.extend_from_slice(&value_len.to_le_bytes());
arena.extend_from_slice(value.as_bytes());
}
offset
}
#[allow(clippy::cast_possible_truncation)]
pub(super) fn arena_append_relation(
arena: &mut Vec<u8>,
id: i64,
members: &[(i64, u8, u32)],
tags: &[(u32, &str)],
meta: &ArenaMeta,
) -> u32 {
let offset = arena.len() as u32;
arena.extend_from_slice(&id.to_le_bytes());
let member_count = members.len() as u32;
arena.extend_from_slice(&member_count.to_le_bytes());
let tag_count = tags.len() as u32;
arena.extend_from_slice(&tag_count.to_le_bytes());
arena_append_meta(arena, meta);
for &(ref_id, type_byte, role_id) in members {
arena.extend_from_slice(&ref_id.to_le_bytes());
arena.push(type_byte);
arena.extend_from_slice(&role_id.to_le_bytes());
}
for &(key_id, value) in tags {
arena.extend_from_slice(&key_id.to_le_bytes());
let value_len = value.len() as u32;
arena.extend_from_slice(&value_len.to_le_bytes());
arena.extend_from_slice(value.as_bytes());
}
offset
}
pub struct CompactTagIter<'a> {
data: &'a [u8],
offset: usize,
remaining: usize,
interner: &'a StringInterner,
}
impl<'a> Iterator for CompactTagIter<'a> {
type Item = (&'a str, &'a str);
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
self.remaining -= 1;
let key_id = read_u32_le(self.data, self.offset);
self.offset += 4;
let value_len = read_u32_le(self.data, self.offset) as usize;
self.offset += 4;
let value_bytes = &self.data[self.offset..self.offset + value_len];
self.offset += value_len;
let key = self.interner.resolve(key_id);
let value = std::str::from_utf8(value_bytes).unwrap_or("");
Some((key, value))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl ExactSizeIterator for CompactTagIter<'_> {}
pub struct CompactRefIter<'a> {
data: &'a [u8],
offset: usize,
remaining: usize,
}
impl Iterator for CompactRefIter<'_> {
type Item = i64;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
self.remaining -= 1;
let val = read_i64_le(self.data, self.offset);
self.offset += 8;
Some(val)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl ExactSizeIterator for CompactRefIter<'_> {}
pub struct CompactMemberIter<'a> {
data: &'a [u8],
offset: usize,
remaining: usize,
interner: &'a StringInterner,
}
impl<'a> Iterator for CompactMemberIter<'a> {
type Item = (MemberType, i64, &'a str);
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
self.remaining -= 1;
let ref_id = read_i64_le(self.data, self.offset);
self.offset += 8;
let type_byte = self.data[self.offset];
self.offset += 1;
let role_id = read_u32_le(self.data, self.offset);
self.offset += 4;
let member_type = byte_to_member_type(type_byte);
let role = self.interner.resolve(role_id);
Some((member_type, ref_id, role))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl ExactSizeIterator for CompactMemberIter<'_> {}
pub struct CompactNodeRef<'a> {
pub(super) data: &'a [u8],
pub(super) interner: &'a StringInterner,
}
impl<'a> CompactNodeRef<'a> {
const HEADER_LEN: usize = 20 + META_LEN;
const META_OFF: usize = 20;
pub fn id(&self) -> i64 {
read_i64_le(self.data, 0)
}
pub fn decimicro_lat(&self) -> i32 {
read_i32_le(self.data, 8)
}
pub fn decimicro_lon(&self) -> i32 {
read_i32_le(self.data, 12)
}
pub fn tag_count(&self) -> usize {
read_u32_le(self.data, 16) as usize
}
pub fn metadata(&self) -> Option<crate::write::block_builder::Metadata<'a>> {
read_meta(self.data, Self::META_OFF, self.interner)
}
pub fn tags(&self) -> CompactTagIter<'a> {
CompactTagIter {
data: self.data,
offset: Self::HEADER_LEN,
remaining: self.tag_count(),
interner: self.interner,
}
}
}
pub struct CompactWayRef<'a> {
pub(super) data: &'a [u8],
pub(super) interner: &'a StringInterner,
}
impl<'a> CompactWayRef<'a> {
const HEADER_LEN: usize = 16 + META_LEN;
const META_OFF: usize = 16;
pub fn id(&self) -> i64 {
read_i64_le(self.data, 0)
}
pub fn ref_count(&self) -> usize {
read_u32_le(self.data, 8) as usize
}
pub fn tag_count(&self) -> usize {
read_u32_le(self.data, 12) as usize
}
pub fn metadata(&self) -> Option<crate::write::block_builder::Metadata<'a>> {
read_meta(self.data, Self::META_OFF, self.interner)
}
pub fn refs(&self) -> CompactRefIter<'a> {
CompactRefIter {
data: self.data,
offset: Self::HEADER_LEN,
remaining: self.ref_count(),
}
}
pub fn tags(&self) -> CompactTagIter<'a> {
let tag_offset = Self::HEADER_LEN + self.ref_count() * 8;
CompactTagIter {
data: self.data,
offset: tag_offset,
remaining: self.tag_count(),
interner: self.interner,
}
}
}
pub struct CompactRelationRef<'a> {
pub(super) data: &'a [u8],
pub(super) interner: &'a StringInterner,
}
impl<'a> CompactRelationRef<'a> {
const HEADER_LEN: usize = 16 + META_LEN;
const META_OFF: usize = 16;
const MEMBER_SIZE: usize = 13;
pub fn id(&self) -> i64 {
read_i64_le(self.data, 0)
}
pub fn member_count(&self) -> usize {
read_u32_le(self.data, 8) as usize
}
pub fn tag_count(&self) -> usize {
read_u32_le(self.data, 12) as usize
}
pub fn metadata(&self) -> Option<crate::write::block_builder::Metadata<'a>> {
read_meta(self.data, Self::META_OFF, self.interner)
}
pub fn members(&self) -> CompactMemberIter<'a> {
CompactMemberIter {
data: self.data,
offset: Self::HEADER_LEN,
remaining: self.member_count(),
interner: self.interner,
}
}
pub fn tags(&self) -> CompactTagIter<'a> {
let tag_offset = Self::HEADER_LEN + self.member_count() * Self::MEMBER_SIZE;
CompactTagIter {
data: self.data,
offset: tag_offset,
remaining: self.tag_count(),
interner: self.interner,
}
}
}