use super::dense::DenseNodeIter;
use super::elements::{Element, Node, Relation, Way};
use super::wire::{
WireBlock, WireBlockMeta, WireDenseNodes, WireGroup, WireMessageIter, WireNode, WireRelation,
WireWay,
};
use crate::error::{ErrorKind, Result, new_error, new_wire_error};
use bytes::Bytes;
use std;
#[derive(Clone, Debug)]
pub(crate) struct WireHeaderBBox {
pub left: i64,
pub right: i64,
pub top: i64,
pub bottom: i64,
}
impl WireHeaderBBox {
fn parse(data: &[u8]) -> Result<Self> {
use super::wire::Cursor;
let mut cursor = Cursor::new(data);
let mut left: i64 = 0;
let mut right: i64 = 0;
let mut top: i64 = 0;
let mut bottom: i64 = 0;
while let Some((field, wire_type)) = cursor.read_tag()? {
match (field, wire_type) {
(1, 0) => left = cursor.read_sint64()?,
(2, 0) => right = cursor.read_sint64()?,
(3, 0) => top = cursor.read_sint64()?,
(4, 0) => bottom = cursor.read_sint64()?,
_ => cursor.skip_field(wire_type)?,
}
}
Ok(WireHeaderBBox {
left,
right,
top,
bottom,
})
}
}
#[derive(Clone, Debug)]
pub(crate) struct WireHeaderBlock {
pub bbox: Option<WireHeaderBBox>,
pub required_features: Vec<String>,
pub optional_features: Vec<String>,
pub writingprogram: Option<String>,
pub source: Option<String>,
pub osmosis_replication_timestamp: Option<i64>,
pub osmosis_replication_sequence_number: Option<i64>,
pub osmosis_replication_base_url: Option<String>,
}
impl WireHeaderBlock {
pub fn parse(data: &[u8]) -> Result<Self> {
use super::wire::Cursor;
let mut cursor = Cursor::new(data);
let mut bbox: Option<WireHeaderBBox> = None;
let mut required_features: Vec<String> = Vec::new();
let mut optional_features: Vec<String> = Vec::new();
let mut writingprogram: Option<String> = None;
let mut source: Option<String> = None;
let mut osmosis_replication_timestamp: Option<i64> = None;
let mut osmosis_replication_sequence_number: Option<i64> = None;
let mut osmosis_replication_base_url: Option<String> = None;
while let Some((field, wire_type)) = cursor.read_tag()? {
match field {
1 => {
let sub_data = cursor.read_len_delimited()?;
bbox = Some(WireHeaderBBox::parse(sub_data)?);
}
4 => {
let bytes = cursor.read_len_delimited()?;
let s = String::from_utf8(bytes.to_vec())
.map_err(|_| new_wire_error("invalid UTF-8 in required_features"))?;
required_features.push(s);
}
5 => {
let bytes = cursor.read_len_delimited()?;
let s = String::from_utf8(bytes.to_vec())
.map_err(|_| new_wire_error("invalid UTF-8 in optional_features"))?;
optional_features.push(s);
}
16 => {
let bytes = cursor.read_len_delimited()?;
writingprogram = Some(
String::from_utf8(bytes.to_vec())
.map_err(|_| new_wire_error("invalid UTF-8 in writingprogram"))?,
);
}
17 => {
let bytes = cursor.read_len_delimited()?;
source = Some(
String::from_utf8(bytes.to_vec())
.map_err(|_| new_wire_error("invalid UTF-8 in source"))?,
);
}
32 => {
osmosis_replication_timestamp = Some(cursor.read_varint_i64()?);
}
33 => {
osmosis_replication_sequence_number = Some(cursor.read_varint_i64()?);
}
34 => {
let bytes = cursor.read_len_delimited()?;
osmosis_replication_base_url =
Some(String::from_utf8(bytes.to_vec()).map_err(|_| {
new_wire_error("invalid UTF-8 in replication_base_url")
})?);
}
_ => cursor.skip_field(wire_type)?,
}
}
Ok(WireHeaderBlock {
bbox,
required_features,
optional_features,
writingprogram,
source,
osmosis_replication_timestamp,
osmosis_replication_sequence_number,
osmosis_replication_base_url,
})
}
}
#[derive(Clone, Debug)]
pub struct HeaderBlock {
header: WireHeaderBlock,
}
impl HeaderBlock {
pub(crate) fn new(header: WireHeaderBlock) -> HeaderBlock {
HeaderBlock { header }
}
pub(crate) fn parse_from_bytes(data: &[u8]) -> Result<HeaderBlock> {
WireHeaderBlock::parse(data).map(HeaderBlock::new)
}
#[allow(clippy::cast_precision_loss)]
pub fn bbox(&self) -> Option<HeaderBBox> {
self.header.bbox.as_ref().map(|bbox| HeaderBBox {
left: (bbox.left as f64) * 1.0e-9,
right: (bbox.right as f64) * 1.0e-9,
top: (bbox.top as f64) * 1.0e-9,
bottom: (bbox.bottom as f64) * 1.0e-9,
})
}
pub fn required_features(&self) -> &[String] {
self.header.required_features.as_slice()
}
pub fn optional_features(&self) -> &[String] {
self.header.optional_features.as_slice()
}
pub fn writing_program(&self) -> Option<&str> {
self.header.writingprogram.as_deref()
}
pub fn source(&self) -> Option<&str> {
self.header.source.as_deref()
}
pub fn osmosis_replication_timestamp(&self) -> Option<i64> {
self.header.osmosis_replication_timestamp
}
pub fn osmosis_replication_sequence_number(&self) -> Option<i64> {
self.header.osmosis_replication_sequence_number
}
pub fn osmosis_replication_base_url(&self) -> Option<&str> {
self.header.osmosis_replication_base_url.as_deref()
}
pub const SORT_TYPE_THEN_ID: &str = "Sort.Type_then_ID";
pub fn is_sorted(&self) -> bool {
self.header
.optional_features
.iter()
.any(|f| f == Self::SORT_TYPE_THEN_ID)
}
pub const LOCATIONS_ON_WAYS: &str = "LocationsOnWays";
pub const WAY_MEMBERS_V1: &str = "pbfhogg.WayMembers-v1";
pub const SHARED_NODE_PINS_V1: &str = "pbfhogg.SharedNodePins-v1";
pub const HISTORICAL_INFORMATION: &str = "HistoricalInformation";
pub fn has_locations_on_ways(&self) -> bool {
self.header
.optional_features
.iter()
.any(|f| f == Self::LOCATIONS_ON_WAYS)
}
pub fn has_way_members_v1(&self) -> bool {
self.header
.optional_features
.iter()
.any(|f| f == Self::WAY_MEMBERS_V1)
}
pub fn has_shared_node_pins_v1(&self) -> bool {
self.header
.optional_features
.iter()
.any(|f| f == Self::SHARED_NODE_PINS_V1)
}
pub fn has_historical_information(&self) -> bool {
self.header
.required_features
.iter()
.any(|f| f == Self::HISTORICAL_INFORMATION)
}
}
#[derive(Clone, Copy, Debug)]
pub struct HeaderBBox {
pub left: f64,
pub right: f64,
pub top: f64,
pub bottom: f64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum BlockType {
DenseNodes,
Nodes,
Ways,
Relations,
Mixed,
Empty,
}
impl BlockType {
pub fn is_nodes(&self) -> bool {
matches!(self, Self::DenseNodes | Self::Nodes)
}
pub fn is_ways(&self) -> bool {
matches!(self, Self::Ways)
}
pub fn is_relations(&self) -> bool {
matches!(self, Self::Relations)
}
}
fn classify_group(data: &[u8]) -> BlockType {
if data.is_empty() {
return BlockType::Empty;
}
let tag_byte = data[0];
let field = tag_byte >> 3;
let wire_type = tag_byte & 0x07;
if wire_type != 2 {
return BlockType::Mixed; }
match field {
1 => BlockType::Nodes,
2 => BlockType::DenseNodes,
3 => BlockType::Ways,
4 => BlockType::Relations,
_ => BlockType::Mixed, }
}
pub struct PrimitiveBlock {
#[allow(dead_code)]
buffer: Bytes,
block: WireBlock<'static>,
}
impl std::fmt::Debug for PrimitiveBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PrimitiveBlock")
.field("groups", &self.block.group_count())
.field("stringtable_entries", &self.block.stringtable.len())
.field("granularity", &self.block.granularity)
.finish()
}
}
impl PrimitiveBlock {
#[hotpath::measure]
#[allow(clippy::needless_pass_by_value)]
pub fn new(buffer: Bytes) -> Result<PrimitiveBlock> {
Self::from_vec(buffer.to_vec())
}
fn finish(bytes: Bytes, meta: &WireBlockMeta) -> Result<PrimitiveBlock> {
let data: &[u8] = &bytes;
let block = WireBlock::from_inline(data, meta);
for index in 0..block.stringtable.len() {
if let Some(raw) = block.stringtable.get(index) {
std::str::from_utf8(raw)
.map_err(|err| new_error(ErrorKind::StringtableUtf8 { err, index }))?;
}
}
#[allow(clippy::transmute_undefined_repr)]
let block = unsafe { std::mem::transmute::<WireBlock<'_>, WireBlock<'static>>(block) };
Ok(PrimitiveBlock {
buffer: bytes,
block,
})
}
pub(crate) fn from_vec(mut buffer: Vec<u8>) -> Result<PrimitiveBlock> {
let meta = WireBlock::parse_and_inline(&mut buffer)?;
Self::finish(Bytes::from(buffer), &meta)
}
#[hotpath::measure]
pub(crate) fn from_vec_with_scratch(
mut buffer: Vec<u8>,
st_scratch: &mut Vec<(u32, u32)>,
gr_scratch: &mut Vec<(u32, u32)>,
) -> Result<PrimitiveBlock> {
let meta = WireBlock::parse_and_inline_with_scratch(&mut buffer, st_scratch, gr_scratch)?;
Self::finish(Bytes::from(buffer), &meta)
}
#[hotpath::measure]
pub(crate) fn from_vec_pooled_with_scratch(
mut buffer: Vec<u8>,
pool: &std::sync::Arc<crate::blob::DecompressPool>,
st_scratch: &mut Vec<(u32, u32)>,
gr_scratch: &mut Vec<(u32, u32)>,
) -> Result<PrimitiveBlock> {
let meta = WireBlock::parse_and_inline_with_scratch(&mut buffer, st_scratch, gr_scratch)?;
Self::finish(crate::blob::pool_wrap(buffer, Some(pool)), &meta)
}
pub fn decompressed_size(&self) -> usize {
self.block.proto_len as usize
}
pub fn block_type(&self) -> BlockType {
let mut result: Option<BlockType> = None;
for i in 0..self.block.group_count() {
let group_data = self.block.group(i);
let group_type = classify_group(group_data);
match result {
None => result = Some(group_type),
Some(prev) if prev == group_type => {} Some(_) => return BlockType::Mixed,
}
}
result.unwrap_or(BlockType::Empty)
}
pub fn elements(&self) -> BlockElementsIter<'_> {
BlockElementsIter::new(&self.block)
}
pub fn elements_skip_metadata(&self) -> BlockElementsIter<'_> {
BlockElementsIter::new_skip_metadata(&self.block)
}
#[allow(dead_code)]
pub(crate) fn raw_group_bytes(&self, index: usize) -> &[u8] {
self.block.group(index)
}
#[allow(dead_code)]
pub(crate) fn group_count(&self) -> usize {
self.block.group_count()
}
#[allow(dead_code)]
pub(crate) fn raw_stringtable_bytes(&self) -> &[u8] {
self.block.raw_stringtable()
}
#[allow(dead_code)]
pub(crate) fn block_scalars(&self) -> (i32, i64, i64, i32) {
(
self.block.granularity,
self.block.lat_offset,
self.block.lon_offset,
self.block.date_granularity,
)
}
pub fn string_table_len(&self) -> usize {
self.block.stringtable.len()
}
pub fn string_table_entry(&self, index: usize) -> Option<&str> {
self.block.stringtable.get(index).map(|bytes| {
unsafe { std::str::from_utf8_unchecked(bytes) }
})
}
pub(crate) fn decode_dense_columns(
&self,
columns: &mut super::columnar::DenseNodeColumns,
) -> usize {
columns.clear();
for group in self.groups() {
if let Ok(Some(dense_data)) = group.group.dense()
&& let Ok(dense) = super::wire::WireDenseNodes::parse(dense_data)
{
columns.decode_append(
&dense,
self.block.granularity,
self.block.lat_offset,
self.block.lon_offset,
);
}
}
columns.len()
}
pub fn groups(&self) -> GroupIter<'_> {
GroupIter::new(&self.block)
}
pub fn for_each_element<F>(&self, mut f: F)
where
F: for<'a> FnMut(Element<'a>),
{
for group in self.groups() {
for node in group.nodes() {
f(Element::Node(node));
}
for dnode in group.dense_nodes() {
f(Element::DenseNode(dnode));
}
for way in group.ways() {
f(Element::Way(way));
}
for relation in group.relations() {
f(Element::Relation(relation));
}
}
}
}
pub struct PrimitiveGroup<'a> {
block: &'a WireBlock<'static>,
group: WireGroup<'a>,
}
impl<'a> PrimitiveGroup<'a> {
fn new(block: &'a WireBlock<'static>, data: &'a [u8]) -> PrimitiveGroup<'a> {
PrimitiveGroup {
block,
group: WireGroup::new(data),
}
}
pub fn nodes(&self) -> GroupNodeIter<'a> {
GroupNodeIter {
block: self.block,
iter: self.group.nodes(),
}
}
pub fn dense_nodes(&self) -> DenseNodeIter<'a> {
match self.group.dense() {
Ok(Some(data)) => match WireDenseNodes::parse(data) {
Ok(dense) => DenseNodeIter::new(self.block, dense),
Err(_) => DenseNodeIter::empty(self.block),
},
_ => DenseNodeIter::empty(self.block),
}
}
pub fn ways(&self) -> GroupWayIter<'a> {
GroupWayIter {
block: self.block,
iter: self.group.ways(),
}
}
pub fn relations(&self) -> GroupRelationIter<'a> {
GroupRelationIter {
block: self.block,
iter: self.group.relations(),
}
}
}
pub struct BlockElementsIter<'a> {
block: &'a WireBlock<'static>,
state: ElementsIterState,
group_index: usize,
group_count: usize,
dense_nodes: DenseNodeIter<'a>,
nodes: WireMessageIter<'a>,
ways: WireMessageIter<'a>,
relations: WireMessageIter<'a>,
skip_metadata: bool,
}
#[derive(Copy, Clone, Debug)]
enum ElementsIterState {
Group,
DenseNode,
Node,
Way,
Relation,
}
impl<'a> BlockElementsIter<'a> {
fn new(block: &'a WireBlock<'static>) -> BlockElementsIter<'a> {
BlockElementsIter {
block,
state: ElementsIterState::Group,
group_index: 0,
group_count: block.group_count(),
dense_nodes: DenseNodeIter::empty(block),
nodes: WireMessageIter::empty(),
ways: WireMessageIter::empty(),
relations: WireMessageIter::empty(),
skip_metadata: false,
}
}
fn new_skip_metadata(block: &'a WireBlock<'static>) -> BlockElementsIter<'a> {
BlockElementsIter {
block,
state: ElementsIterState::Group,
group_index: 0,
group_count: block.group_count(),
dense_nodes: DenseNodeIter::empty(block),
nodes: WireMessageIter::empty(),
ways: WireMessageIter::empty(),
relations: WireMessageIter::empty(),
skip_metadata: true,
}
}
#[inline]
#[allow(clippy::option_option)]
fn step(&mut self) -> Option<Option<Element<'a>>> {
match self.state {
ElementsIterState::Group => {
if self.group_index >= self.group_count {
return Some(None);
}
let group_data = self.block.group(self.group_index);
self.group_index += 1;
self.state = ElementsIterState::DenseNode;
let group = WireGroup::new(group_data);
self.dense_nodes = match group.dense() {
Ok(Some(data)) => match WireDenseNodes::parse(data) {
Ok(dense) => {
if self.skip_metadata {
DenseNodeIter::new_skip_metadata(self.block, dense)
} else {
DenseNodeIter::new(self.block, dense)
}
}
Err(_) => DenseNodeIter::empty(self.block),
},
_ => DenseNodeIter::empty(self.block),
};
self.nodes = group.nodes();
self.ways = group.ways();
self.relations = group.relations();
None
}
ElementsIterState::DenseNode => match self.dense_nodes.next() {
Some(dense_node) => Some(Some(Element::DenseNode(dense_node))),
None => {
self.state = ElementsIterState::Node;
None
}
},
ElementsIterState::Node => {
for data in self.nodes.by_ref() {
if let Ok(wire_node) = WireNode::parse(data) {
return Some(Some(Element::Node(Node::new(self.block, wire_node))));
}
}
self.state = ElementsIterState::Way;
None
}
ElementsIterState::Way => {
for data in self.ways.by_ref() {
if let Ok(wire_way) = WireWay::parse(data) {
return Some(Some(Element::Way(Way::new(self.block, wire_way))));
}
}
self.state = ElementsIterState::Relation;
None
}
ElementsIterState::Relation => {
for data in self.relations.by_ref() {
if let Ok(wire_rel) = WireRelation::parse(data) {
return Some(Some(Element::Relation(Relation::new(self.block, wire_rel))));
}
}
self.state = ElementsIterState::Group;
None
}
}
}
}
impl<'a> Iterator for BlockElementsIter<'a> {
type Item = Element<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(element) = self.step() {
return element;
}
}
}
}
pub struct GroupIter<'a> {
block: &'a WireBlock<'static>,
index: usize,
count: usize,
}
impl<'a> GroupIter<'a> {
fn new(block: &'a WireBlock<'static>) -> GroupIter<'a> {
GroupIter {
block,
index: 0,
count: block.group_count(),
}
}
}
impl<'a> Iterator for GroupIter<'a> {
type Item = PrimitiveGroup<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.count {
return None;
}
let data = self.block.group(self.index);
self.index += 1;
Some(PrimitiveGroup::new(self.block, data))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.count - self.index;
(remaining, Some(remaining))
}
}
impl ExactSizeIterator for GroupIter<'_> {}
pub struct GroupNodeIter<'a> {
block: &'a WireBlock<'static>,
iter: WireMessageIter<'a>,
}
impl<'a> Iterator for GroupNodeIter<'a> {
type Item = Node<'a>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let data = self.iter.next()?;
if let Ok(wire_node) = WireNode::parse(data) {
return Some(Node::new(self.block, wire_node));
}
}
}
}
pub struct GroupWayIter<'a> {
block: &'a WireBlock<'static>,
iter: WireMessageIter<'a>,
}
impl<'a> Iterator for GroupWayIter<'a> {
type Item = Way<'a>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let data = self.iter.next()?;
if let Ok(wire_way) = WireWay::parse(data) {
return Some(Way::new(self.block, wire_way));
}
}
}
}
pub struct GroupRelationIter<'a> {
block: &'a WireBlock<'static>,
iter: WireMessageIter<'a>,
}
impl<'a> Iterator for GroupRelationIter<'a> {
type Item = Relation<'a>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let data = self.iter.next()?;
if let Ok(wire_rel) = WireRelation::parse(data) {
return Some(Relation::new(self.block, wire_rel));
}
}
}
}
pub(crate) fn str_from_stringtable<'a>(block: &'a WireBlock<'_>, index: usize) -> Result<&'a str> {
if let Some(bytes) = block.stringtable.get(index) {
Ok(unsafe { std::str::from_utf8_unchecked(bytes) })
} else {
Err(new_error(ErrorKind::StringtableIndexOutOfBounds { index }))
}
}
pub(crate) fn get_stringtable_key_value<'a>(
block: &'a WireBlock<'_>,
key_index: Option<usize>,
value_index: Option<usize>,
) -> Option<(&'a str, &'a str)> {
match (key_index, value_index) {
(Some(key_index), Some(val_index)) => {
let k_res = str_from_stringtable(block, key_index);
let v_res = str_from_stringtable(block, val_index);
if let (Ok(k), Ok(v)) = (k_res, v_res) {
Some((k, v))
} else {
None
}
}
_ => None,
}
}