use rustc_hash::FxHashSet;
use protohoggr::{encode_bytes_field, encode_int64_field, encode_varint, zigzag_encode_64};
use super::block_builder::{member_type_value, MemberData, Metadata};
use super::string_table::StringTable;
#[inline]
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
fn encode_optional_int32(buf: &mut Vec<u8>, field: u32, value: i32) {
debug_assert!(field <= 15, "single-byte tag requires field <= 15, got {field}");
buf.push((field << 3) as u8); encode_varint(buf, value as i64 as u64);
}
#[inline]
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
fn encode_optional_int64(buf: &mut Vec<u8>, field: u32, value: i64) {
debug_assert!(field <= 15, "single-byte tag requires field <= 15, got {field}");
buf.push((field << 3) as u8);
encode_varint(buf, value as u64);
}
#[inline]
#[allow(clippy::cast_possible_truncation)]
fn encode_optional_uint32(buf: &mut Vec<u8>, field: u32, value: u32) {
debug_assert!(field <= 15, "single-byte tag requires field <= 15, got {field}");
buf.push((field << 3) as u8);
encode_varint(buf, u64::from(value));
}
pub(super) fn encode_info_to(
info: &mut Vec<u8>,
string_table: &mut StringTable,
meta: &Metadata<'_>,
) {
info.clear();
encode_optional_int32(info, 1, meta.version);
encode_optional_int64(info, 2, meta.timestamp);
encode_optional_int64(info, 3, meta.changeset);
encode_optional_int32(info, 4, meta.uid);
encode_optional_uint32(info, 5, string_table.add(meta.user));
if !meta.visible {
info.push(6 << 3); info.push(0x00); }
}
pub(super) fn encode_tags<'t>(
string_table: &mut StringTable,
elem: &mut Vec<u8>,
keys_buf: &mut Vec<u8>,
vals_buf: &mut Vec<u8>,
tag_key_indices: &mut FxHashSet<u32>,
tags: impl IntoIterator<Item = (&'t str, &'t str)>,
) {
keys_buf.clear();
vals_buf.clear();
for (key, val) in tags {
let key_idx = string_table.add(key);
tag_key_indices.insert(key_idx);
encode_varint(keys_buf, u64::from(key_idx));
encode_varint(vals_buf, u64::from(string_table.add(val)));
}
if !keys_buf.is_empty() {
encode_bytes_field(elem, 2, keys_buf);
encode_bytes_field(elem, 3, vals_buf);
}
}
#[allow(clippy::too_many_arguments)]
pub(super) fn encode_way<'t>(
string_table: &mut StringTable,
group_buf: &mut Vec<u8>,
elem: &mut Vec<u8>,
packed_keys: &mut Vec<u8>,
packed_vals: &mut Vec<u8>,
info_buf: &mut Vec<u8>,
tag_key_indices: &mut FxHashSet<u32>,
id: i64,
tags: impl IntoIterator<Item = (&'t str, &'t str)>,
refs: &[i64],
metadata: Option<&Metadata<'_>>,
) {
elem.clear();
encode_int64_field(elem, 1, id);
encode_tags(string_table, elem, packed_keys, packed_vals, tag_key_indices, tags);
if let Some(meta) = metadata {
encode_info_to(info_buf, string_table, meta);
encode_bytes_field(elem, 4, info_buf);
}
if !refs.is_empty() {
packed_keys.clear();
let mut last_ref: i64 = 0;
for &r in refs {
encode_varint(packed_keys, zigzag_encode_64(r - last_ref));
last_ref = r;
}
encode_bytes_field(elem, 8, packed_keys);
}
encode_bytes_field(group_buf, 3, elem);
}
#[allow(clippy::too_many_arguments)]
pub(super) fn encode_way_with_locations<'t>(
string_table: &mut StringTable,
group_buf: &mut Vec<u8>,
elem: &mut Vec<u8>,
packed_refs: &mut Vec<u8>,
packed_vals: &mut Vec<u8>,
packed_lats: &mut Vec<u8>,
packed_lons: &mut Vec<u8>,
info_buf: &mut Vec<u8>,
tag_key_indices: &mut FxHashSet<u32>,
id: i64,
tags: impl IntoIterator<Item = (&'t str, &'t str)>,
refs: &[i64],
locations: &[(i32, i32)],
metadata: Option<&Metadata<'_>>,
) {
elem.clear();
encode_int64_field(elem, 1, id);
encode_tags(string_table, elem, packed_refs, packed_vals, tag_key_indices, tags);
if let Some(meta) = metadata {
encode_info_to(info_buf, string_table, meta);
encode_bytes_field(elem, 4, info_buf);
}
if !refs.is_empty() {
let mut last_ref: i64 = 0;
let mut last_lat: i64 = 0;
let mut last_lon: i64 = 0;
packed_refs.clear();
packed_lats.clear();
packed_lons.clear();
for (&r, &(loc_lat, loc_lon)) in refs.iter().zip(locations.iter()) {
encode_varint(packed_refs, zigzag_encode_64(r - last_ref));
last_ref = r;
let lat = i64::from(loc_lat);
encode_varint(packed_lats, zigzag_encode_64(lat - last_lat));
last_lat = lat;
let lon = i64::from(loc_lon);
encode_varint(packed_lons, zigzag_encode_64(lon - last_lon));
last_lon = lon;
}
encode_bytes_field(elem, 8, packed_refs);
encode_bytes_field(elem, 9, packed_lats);
encode_bytes_field(elem, 10, packed_lons);
}
encode_bytes_field(group_buf, 3, elem);
}
pub(super) fn encode_way_raw_bytes(
group_buf: &mut Vec<u8>,
elem: &mut Vec<u8>,
id: i64,
keys_data: &[u8],
vals_data: &[u8],
refs_data: &[u8],
info_data: Option<&[u8]>,
) {
debug_assert!(
keys_data.is_empty() == vals_data.is_empty(),
"keys/vals must be paired: keys={} vals={} bytes",
keys_data.len(),
vals_data.len(),
);
elem.clear();
encode_int64_field(elem, 1, id);
encode_bytes_field(elem, 2, keys_data);
encode_bytes_field(elem, 3, vals_data);
if let Some(info) = info_data {
encode_bytes_field(elem, 4, info);
}
encode_bytes_field(elem, 8, refs_data);
encode_bytes_field(group_buf, 3, elem);
}
#[allow(clippy::too_many_arguments)]
pub(super) fn encode_way_raw_bytes_with_locations(
group_buf: &mut Vec<u8>,
elem: &mut Vec<u8>,
id: i64,
keys_data: &[u8],
vals_data: &[u8],
refs_data: &[u8],
lat_data: &[u8],
lon_data: &[u8],
info_data: Option<&[u8]>,
) {
elem.clear();
encode_int64_field(elem, 1, id);
encode_bytes_field(elem, 2, keys_data);
encode_bytes_field(elem, 3, vals_data);
if let Some(info) = info_data {
encode_bytes_field(elem, 4, info);
}
encode_bytes_field(elem, 8, refs_data);
if !lat_data.is_empty() {
encode_bytes_field(elem, 9, lat_data);
}
if !lon_data.is_empty() {
encode_bytes_field(elem, 10, lon_data);
}
encode_bytes_field(group_buf, 3, elem);
}
#[allow(clippy::too_many_arguments)]
pub(super) fn encode_relation<'t>(
string_table: &mut StringTable,
group_buf: &mut Vec<u8>,
elem: &mut Vec<u8>,
packed: &mut Vec<u8>,
packed_vals: &mut Vec<u8>,
info_buf: &mut Vec<u8>,
tag_key_indices: &mut FxHashSet<u32>,
id: i64,
tags: impl IntoIterator<Item = (&'t str, &'t str)>,
members: &[MemberData<'_>],
metadata: Option<&Metadata<'_>>,
) {
elem.clear();
encode_int64_field(elem, 1, id);
encode_tags(string_table, elem, packed, packed_vals, tag_key_indices, tags);
if let Some(meta) = metadata {
encode_info_to(info_buf, string_table, meta);
encode_bytes_field(elem, 4, info_buf);
}
if !members.is_empty() {
packed.clear();
#[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
for m in members {
let role_sid = string_table.add(m.role) as i32;
encode_varint(packed, role_sid as i64 as u64);
}
encode_bytes_field(elem, 8, packed);
packed.clear();
let mut last_memid: i64 = 0;
for m in members {
encode_varint(packed, zigzag_encode_64(m.id.id() - last_memid));
last_memid = m.id.id();
}
encode_bytes_field(elem, 9, packed);
packed.clear();
for m in members {
let mt = member_type_value(m.id.member_type());
#[allow(clippy::cast_sign_loss)]
encode_varint(packed, mt as u64);
}
encode_bytes_field(elem, 10, packed);
}
encode_bytes_field(group_buf, 4, elem);
}
#[allow(clippy::too_many_arguments)]
pub(super) fn encode_relation_raw_bytes(
group_buf: &mut Vec<u8>,
elem: &mut Vec<u8>,
id: i64,
keys_data: &[u8],
vals_data: &[u8],
roles_sid_data: &[u8],
memids_data: &[u8],
types_data: &[u8],
info_data: Option<&[u8]>,
) {
elem.clear();
encode_int64_field(elem, 1, id);
encode_bytes_field(elem, 2, keys_data);
encode_bytes_field(elem, 3, vals_data);
if let Some(info) = info_data {
encode_bytes_field(elem, 4, info);
}
encode_bytes_field(elem, 8, roles_sid_data);
encode_bytes_field(elem, 9, memids_data);
encode_bytes_field(elem, 10, types_data);
encode_bytes_field(group_buf, 4, elem);
}
#[allow(clippy::cast_possible_truncation)]
pub(super) fn collect_packed_varint_keys(data: &[u8], indices: &mut FxHashSet<u32>) {
let mut cur = protohoggr::Cursor::new(data);
while !cur.is_empty() {
if let Ok(val) = cur.read_varint() {
indices.insert(val as u32);
} else {
break;
}
}
}