osmflat/
tags.rs

1//! All functions in this module operate on raw bytes for performance reasons.
2//! It is easy to combine these with `std::str::from_utf8` family of functions,
3//! to lift them to operate on `str`.
4
5use crate::Osm;
6use std::ops::Range;
7
8/// Returns an iterator over tags specified by `range`.
9///
10/// When searching for a tag by key consider to use `find_tag` which
11/// performs better.
12#[inline]
13pub fn iter_tags(archive: &Osm, range: Range<u64>) -> impl Iterator<Item = (&[u8], &[u8])> + Clone {
14    let tags = archive.tags();
15    let tags_index = archive.tags_index();
16    let strings = archive.stringtable();
17
18    range.map(move |idx| {
19        let tag = &tags[tags_index[idx as usize].value() as usize];
20        let key = strings.substring_raw(tag.key_idx() as usize);
21        let val = strings.substring_raw(tag.value_idx() as usize);
22        (key, val)
23    })
24}
25
26/// Finds the first tag in the given `range` which satisfies the predicate
27/// applied to the key and value and returns the corresponding value.
28///
29/// Note that the predicate function is called on the whole key block and value
30/// block. These are zero (`\0`) divided blocks of bytes that start at the key
31/// resp. value, and contain the rest string data. In particular, the len of
32/// the block is *not* the len of the key resp. value. The user is responsible
33/// to check or find the zero terminator.
34#[inline]
35pub fn find_tag_by(
36    archive: &Osm,
37    mut range: Range<u64>,
38    mut predicate: impl FnMut(&[u8], &[u8]) -> bool,
39) -> Option<&[u8]> {
40    let tags = archive.tags();
41    let tags_index = archive.tags_index();
42    let strings = archive.stringtable();
43
44    range.find_map(move |idx| {
45        let tag = &tags[tags_index[idx as usize].value() as usize];
46        let key_block = &strings.as_bytes()[tag.key_idx() as usize..];
47        let value_block = &strings.as_bytes()[tag.value_idx() as usize..];
48        if predicate(key_block, value_block) {
49            Some(strings.substring_raw(tag.value_idx() as usize))
50        } else {
51            None
52        }
53    })
54}
55
56/// Finds a tag by its key in the given `range` and returns the corresponding
57/// value.
58#[inline]
59pub fn find_tag<'a>(archive: &'a Osm, range: Range<u64>, key: &[u8]) -> Option<&'a [u8]> {
60    find_tag_by(archive, range, |key_block, _| {
61        key_block.starts_with(key) && *key_block.get(key.len()).unwrap_or(&0) == 0
62    })
63}
64
65/// Checks if there is a tag in `range` with a given `key` and `value`.
66#[inline]
67pub fn has_tag(archive: &Osm, range: Range<u64>, key: &[u8], value: &[u8]) -> bool {
68    let tags = archive.tags();
69    let tags_index = archive.tags_index();
70    let strings = archive.stringtable();
71
72    let matches = |idx, value| {
73        let block = &strings.as_bytes()[idx as usize..];
74        block.starts_with(value) && *block.get(value.len()).unwrap_or(&0) == 0
75    };
76
77    for idx in range {
78        let tag = &tags[tags_index[idx as usize].value() as usize];
79        if matches(tag.key_idx(), key) {
80            return matches(tag.value_idx(), value);
81        }
82    }
83    false
84}