use alloc::vec::Vec;
use geometry_cs::Cartesian;
use geometry_model::{
DynGeometry, Linestring, MultiLinestring, MultiPoint, MultiPolygon, Point2D, Polygon, Ring,
};
use crate::header::{ByteOrder, Cursor, WkbError};
type Pt = Point2D<f64, Cartesian>;
const WKB_POINT: u32 = 1;
const WKB_LINESTRING: u32 = 2;
const WKB_POLYGON: u32 = 3;
const WKB_MULTIPOINT: u32 = 4;
const WKB_MULTILINESTRING: u32 = 5;
const WKB_MULTIPOLYGON: u32 = 6;
const WKB_GEOMETRYCOLLECTION: u32 = 7;
const EWKB_Z: u32 = 0x8000_0000;
const EWKB_M: u32 = 0x4000_0000;
const EWKB_SRID: u32 = 0x2000_0000;
fn dyn_code(g: &DynGeometry<f64, Cartesian>) -> u32 {
match g {
DynGeometry::Point(_) => WKB_POINT,
DynGeometry::LineString(_) => WKB_LINESTRING,
DynGeometry::Polygon(_) => WKB_POLYGON,
DynGeometry::MultiPoint(_) => WKB_MULTIPOINT,
DynGeometry::MultiLineString(_) => WKB_MULTILINESTRING,
DynGeometry::MultiPolygon(_) => WKB_MULTIPOLYGON,
DynGeometry::GeometryCollection(_) => WKB_GEOMETRYCOLLECTION,
}
}
fn base_type_code(tag: u32) -> Result<u32, WkbError> {
if tag & (EWKB_Z | EWKB_M | EWKB_SRID) != 0 {
return Err(WkbError::UnsupportedDimension);
}
let base = tag % 1000;
if tag != base {
return Err(WkbError::UnsupportedDimension);
}
Ok(base)
}
const MAX_DEPTH: usize = 128;
const POINT_BYTES: usize = 16;
const MIN_RECORD_BYTES: usize = 5;
fn reserve_bounded<T>(count: u32, remaining: usize, min_elem_bytes: usize) -> Vec<T> {
let cap = (count as usize).min(remaining / min_elem_bytes);
Vec::with_capacity(cap)
}
struct Parser<'a> {
cursor: Cursor<'a>,
}
impl<'a> Parser<'a> {
fn new(bytes: &'a [u8]) -> Self {
Self {
cursor: Cursor::new(bytes),
}
}
fn read_point(&mut self, order: ByteOrder) -> Result<Pt, WkbError> {
let x = self.cursor.read_f64(order)?;
let y = self.cursor.read_f64(order)?;
Ok(Point2D::new(x, y))
}
fn read_point_run(&mut self, order: ByteOrder) -> Result<Vec<Pt>, WkbError> {
let n = self.cursor.read_u32(order)? as usize;
let byte_len = n.checked_mul(POINT_BYTES).ok_or(WkbError::UnexpectedEof)?;
let bytes = self.cursor.read_slice(byte_len)?;
let mut pts = Vec::with_capacity(n);
for point in bytes.chunks_exact(POINT_BYTES) {
let x_bytes: [u8; 8] = point[..8]
.try_into()
.expect("a point chunk contains its x ordinate");
let y_bytes: [u8; 8] = point[8..]
.try_into()
.expect("a point chunk contains its y ordinate");
let (x, y) = match order {
ByteOrder::LittleEndian => {
(f64::from_le_bytes(x_bytes), f64::from_le_bytes(y_bytes))
}
ByteOrder::BigEndian => (f64::from_be_bytes(x_bytes), f64::from_be_bytes(y_bytes)),
};
pts.push(Point2D::new(x, y));
}
Ok(pts)
}
fn read_point_body(
&mut self,
order: ByteOrder,
) -> Result<DynGeometry<f64, Cartesian>, WkbError> {
Ok(DynGeometry::Point(self.read_point(order)?))
}
fn read_linestring_body(
&mut self,
order: ByteOrder,
) -> Result<DynGeometry<f64, Cartesian>, WkbError> {
Ok(DynGeometry::LineString(Linestring(
self.read_point_run(order)?,
)))
}
fn read_polygon_value(&mut self, order: ByteOrder) -> Result<Polygon<Pt>, WkbError> {
let ring_count = self.cursor.read_u32(order)?;
let mut rings = (0..ring_count).map(|_| self.read_point_run(order).map(Ring::from_vec));
let outer = match rings.next() {
Some(r) => r?,
None => Ring::new(),
};
let mut poly = Polygon::new(outer);
for ring in rings {
poly.inners.push(ring?);
}
Ok(poly)
}
fn read_polygon_body(
&mut self,
order: ByteOrder,
) -> Result<DynGeometry<f64, Cartesian>, WkbError> {
Ok(DynGeometry::Polygon(self.read_polygon_value(order)?))
}
fn read_multipoint_body(
&mut self,
order: ByteOrder,
depth: usize,
) -> Result<DynGeometry<f64, Cartesian>, WkbError> {
let n = self.cursor.read_u32(order)?;
let mut pts = reserve_bounded(n, self.cursor.remaining(), MIN_RECORD_BYTES);
for _ in 0..n {
match self.parse_geometry(depth + 1)? {
DynGeometry::Point(p) => pts.push(p),
other => {
return Err(WkbError::MismatchedMemberType {
expected: WKB_POINT,
found: dyn_code(&other),
});
}
}
}
Ok(DynGeometry::MultiPoint(MultiPoint(pts)))
}
fn read_multilinestring_body(
&mut self,
order: ByteOrder,
depth: usize,
) -> Result<DynGeometry<f64, Cartesian>, WkbError> {
let n = self.cursor.read_u32(order)?;
let mut lines = reserve_bounded(n, self.cursor.remaining(), MIN_RECORD_BYTES);
for _ in 0..n {
match self.parse_geometry(depth + 1)? {
DynGeometry::LineString(ls) => lines.push(ls),
other => {
return Err(WkbError::MismatchedMemberType {
expected: WKB_LINESTRING,
found: dyn_code(&other),
});
}
}
}
Ok(DynGeometry::MultiLineString(MultiLinestring(lines)))
}
fn read_multipolygon_body(
&mut self,
order: ByteOrder,
depth: usize,
) -> Result<DynGeometry<f64, Cartesian>, WkbError> {
let n = self.cursor.read_u32(order)?;
let mut polys = reserve_bounded(n, self.cursor.remaining(), MIN_RECORD_BYTES);
for _ in 0..n {
match self.parse_geometry(depth + 1)? {
DynGeometry::Polygon(pg) => polys.push(pg),
other => {
return Err(WkbError::MismatchedMemberType {
expected: WKB_POLYGON,
found: dyn_code(&other),
});
}
}
}
Ok(DynGeometry::MultiPolygon(MultiPolygon(polys)))
}
fn read_collection_body(
&mut self,
order: ByteOrder,
depth: usize,
) -> Result<DynGeometry<f64, Cartesian>, WkbError> {
let n = self.cursor.read_u32(order)?;
let mut items = reserve_bounded(n, self.cursor.remaining(), MIN_RECORD_BYTES);
for _ in 0..n {
items.push(self.parse_geometry(depth + 1)?);
}
Ok(DynGeometry::GeometryCollection(items))
}
fn parse_geometry(&mut self, depth: usize) -> Result<DynGeometry<f64, Cartesian>, WkbError> {
if depth >= MAX_DEPTH {
return Err(WkbError::NestingTooDeep);
}
let order = self.cursor.read_byte_order()?;
let tag = self.cursor.read_u32(order)?;
let code = base_type_code(tag)?;
match code {
WKB_POINT => self.read_point_body(order),
WKB_LINESTRING => self.read_linestring_body(order),
WKB_POLYGON => self.read_polygon_body(order),
WKB_MULTIPOINT => self.read_multipoint_body(order, depth),
WKB_MULTILINESTRING => self.read_multilinestring_body(order, depth),
WKB_MULTIPOLYGON => self.read_multipolygon_body(order, depth),
WKB_GEOMETRYCOLLECTION => self.read_collection_body(order, depth),
other => Err(WkbError::UnknownGeometryType(other)),
}
}
}
pub fn from_wkb(bytes: &[u8]) -> Result<DynGeometry<f64, Cartesian>, WkbError> {
let mut parser = Parser::new(bytes);
let g = parser.parse_geometry(0)?;
if parser.cursor.is_empty() {
Ok(g)
} else {
Err(WkbError::TrailingBytes)
}
}
#[cfg(test)]
mod tests {
#![allow(
clippy::float_cmp,
reason = "coordinate values come from exact integer byte literals"
)]
use super::*;
use alloc::vec;
const F1: [u8; 8] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F];
const F2: [u8; 8] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40];
const F3: [u8; 8] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40];
#[test]
fn le_point() {
let mut b = vec![0x01, 0x01, 0x00, 0x00, 0x00];
b.extend_from_slice(&F1);
b.extend_from_slice(&F2);
let g = from_wkb(&b).unwrap();
assert_eq!(g, DynGeometry::Point(Pt::new(1.0, 2.0)));
}
#[test]
fn le_linestring_two_points() {
let mut b = vec![0x01, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00];
b.extend_from_slice(&F1);
b.extend_from_slice(&F2);
b.extend_from_slice(&F3);
b.extend_from_slice(&F1);
let g = from_wkb(&b).unwrap();
assert_eq!(
g,
DynGeometry::LineString(Linestring::from_vec(vec![
Pt::new(1.0, 2.0),
Pt::new(3.0, 1.0),
]))
);
}
#[test]
fn le_polygon_one_ring() {
let mut b = vec![
0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, ];
b.extend_from_slice(&F1);
b.extend_from_slice(&F2);
b.extend_from_slice(&F3);
b.extend_from_slice(&F1);
b.extend_from_slice(&F1);
b.extend_from_slice(&F2);
let g = from_wkb(&b).unwrap();
assert_eq!(
g,
DynGeometry::Polygon(Polygon::new(Ring::from_vec(vec![
Pt::new(1.0, 2.0),
Pt::new(3.0, 1.0),
Pt::new(1.0, 2.0),
])))
);
}
#[test]
fn trailing_bytes_rejected() {
let mut b = vec![0x01, 0x01, 0x00, 0x00, 0x00];
b.extend_from_slice(&F1);
b.extend_from_slice(&F2);
b.push(0xFF); assert_eq!(from_wkb(&b).unwrap_err(), WkbError::TrailingBytes);
}
#[test]
fn z_dimension_rejected() {
let b = vec![0x01, 0x01, 0x00, 0x00, 0x80];
assert_eq!(from_wkb(&b).unwrap_err(), WkbError::UnsupportedDimension);
}
#[test]
fn iso_z_dimension_rejected() {
let b = vec![0x01, 0xE9, 0x03, 0x00, 0x00];
assert_eq!(from_wkb(&b).unwrap_err(), WkbError::UnsupportedDimension);
}
#[test]
fn truncated_point_is_eof() {
let b = vec![0x01, 0x01, 0x00, 0x00, 0x00];
assert_eq!(from_wkb(&b).unwrap_err(), WkbError::UnexpectedEof);
}
#[test]
fn hostile_count_does_not_over_reserve() {
let ls = vec![0x01, 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF];
assert_eq!(from_wkb(&ls).unwrap_err(), WkbError::UnexpectedEof);
let mpg = vec![0x01, 0x06, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF];
assert_eq!(from_wkb(&mpg).unwrap_err(), WkbError::UnexpectedEof);
let gc = vec![0x01, 0x07, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF];
assert_eq!(from_wkb(&gc).unwrap_err(), WkbError::UnexpectedEof);
}
#[test]
fn deeply_nested_collections_are_rejected_without_overflow() {
let mut b = vec![];
for _ in 0..10_000 {
b.push(0x01);
b.extend_from_slice(&7u32.to_le_bytes());
b.extend_from_slice(&1u32.to_le_bytes());
}
assert_eq!(from_wkb(&b).unwrap_err(), WkbError::NestingTooDeep);
}
#[test]
fn multipoint_member_of_wrong_kind_reports_both_codes() {
let mut b = vec![0x01, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
b.extend_from_slice(&[0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
assert_eq!(
from_wkb(&b).unwrap_err(),
WkbError::MismatchedMemberType {
expected: 1,
found: 2
}
);
}
fn le_point_record() -> Vec<u8> {
let mut b = vec![0x01, 0x01, 0x00, 0x00, 0x00];
b.extend_from_slice(&F1);
b.extend_from_slice(&F2);
b
}
fn le_empty_linestring_record() -> Vec<u8> {
vec![0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
}
fn le_empty_polygon_record() -> Vec<u8> {
vec![0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
}
#[test]
fn le_multipoint_two_members() {
let mut b = vec![0x01, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00];
b.extend_from_slice(&le_point_record());
b.extend_from_slice(&le_point_record());
let g = from_wkb(&b).unwrap();
assert_eq!(
g,
DynGeometry::MultiPoint(MultiPoint::from_vec(vec![
Pt::new(1.0, 2.0),
Pt::new(1.0, 2.0),
]))
);
}
#[test]
fn le_multilinestring_one_member() {
let mut b = vec![0x01, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
b.extend_from_slice(&le_empty_linestring_record());
let g = from_wkb(&b).unwrap();
assert_eq!(
g,
DynGeometry::MultiLineString(MultiLinestring::from_vec(vec![Linestring::from_vec(
Vec::new()
),]))
);
}
#[test]
fn le_multipolygon_one_member() {
let mut b = vec![0x01, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
b.extend_from_slice(&le_empty_polygon_record());
let g = from_wkb(&b).unwrap();
assert_eq!(
g,
DynGeometry::MultiPolygon(MultiPolygon::from_vec(vec![Polygon::new(Ring::new())]))
);
}
#[test]
fn le_geometry_collection_mixed_members() {
let mut b = vec![0x01, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00];
b.extend_from_slice(&le_point_record());
b.extend_from_slice(&le_empty_linestring_record());
let g = from_wkb(&b).unwrap();
assert_eq!(
g,
DynGeometry::GeometryCollection(vec![
DynGeometry::Point(Pt::new(1.0, 2.0)),
DynGeometry::LineString(Linestring::from_vec(Vec::new())),
])
);
}
#[test]
fn multilinestring_wrong_member_reports_both_codes() {
let mut b = vec![0x01, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
b.extend_from_slice(&le_point_record());
assert_eq!(
from_wkb(&b).unwrap_err(),
WkbError::MismatchedMemberType {
expected: WKB_LINESTRING,
found: WKB_POINT,
}
);
}
#[test]
fn multipolygon_wrong_member_reports_both_codes() {
let mut b = vec![0x01, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
b.extend_from_slice(&le_point_record());
assert_eq!(
from_wkb(&b).unwrap_err(),
WkbError::MismatchedMemberType {
expected: WKB_POLYGON,
found: WKB_POINT,
}
);
}
#[test]
fn unknown_base_type_is_rejected() {
let b = vec![0x01, 0x08, 0x00, 0x00, 0x00];
assert_eq!(from_wkb(&b).unwrap_err(), WkbError::UnknownGeometryType(8));
}
}