use super::{Coord, Geometry, ParseError, Polygon};
const TYPE_POINT: u32 = 1;
const TYPE_LINESTRING: u32 = 2;
const TYPE_POLYGON: u32 = 3;
const TYPE_MULTIPOINT: u32 = 4;
const TYPE_MULTILINESTRING: u32 = 5;
const TYPE_MULTIPOLYGON: u32 = 6;
const TYPE_GEOMETRYCOLLECTION: u32 = 7;
const EWKB_FLAG_Z: u32 = 0x80000000;
const EWKB_FLAG_M: u32 = 0x40000000;
const EWKB_FLAG_SRID: u32 = 0x20000000;
pub(super) fn parse(bytes: &[u8]) -> Result<Geometry, ParseError> {
let mut p = Parser { buf: bytes, pos: 0 };
let g = p.geometry()?;
if p.pos != p.buf.len() {
return Err(ParseError::Syntax(format!(
"trailing {} byte(s) after geometry",
p.buf.len() - p.pos
)));
}
Ok(g)
}
struct Parser<'a> {
buf: &'a [u8],
pos: usize,
}
struct TypeTag {
base: u32,
has_z: bool,
has_m: bool,
}
impl<'a> Parser<'a> {
fn take(&mut self, n: usize) -> Result<&'a [u8], ParseError> {
if self.pos + n > self.buf.len() {
return Err(ParseError::UnexpectedEnd);
}
let s = &self.buf[self.pos..self.pos + n];
self.pos += n;
Ok(s)
}
fn byte(&mut self) -> Result<u8, ParseError> {
Ok(self.take(1)?[0])
}
fn u32(&mut self, le: bool) -> Result<u32, ParseError> {
let s = self.take(4)?;
let bytes = [s[0], s[1], s[2], s[3]];
Ok(if le {
u32::from_le_bytes(bytes)
} else {
u32::from_be_bytes(bytes)
})
}
fn f64(&mut self, le: bool) -> Result<f64, ParseError> {
let s = self.take(8)?;
let bytes = [s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]];
Ok(if le {
f64::from_le_bytes(bytes)
} else {
f64::from_be_bytes(bytes)
})
}
fn geometry(&mut self) -> Result<Geometry, ParseError> {
let bo = self.byte()?;
let le = match bo {
0 => false,
1 => true,
other => {
return Err(ParseError::Syntax(format!("invalid byte order {other}")));
}
};
let raw_type = self.u32(le)?;
let tag = resolve_type(raw_type)?;
if raw_type & EWKB_FLAG_SRID != 0 {
let _srid = self.u32(le)?;
}
self.body(le, &tag)
}
fn body(&mut self, le: bool, tag: &TypeTag) -> Result<Geometry, ParseError> {
match tag.base {
TYPE_POINT => {
let c = self.coord(le, tag)?;
if c.0.is_nan() && c.1.is_nan() {
Ok(Geometry::Empty)
} else {
Ok(Geometry::Point(c))
}
}
TYPE_LINESTRING => {
let n = self.u32(le)? as usize;
let mut cs = Vec::with_capacity(n);
for _ in 0..n {
cs.push(self.coord(le, tag)?);
}
Ok(Geometry::LineString(cs))
}
TYPE_POLYGON => Ok(Geometry::Polygon(self.polygon_body(le, tag)?)),
TYPE_MULTIPOINT => {
let n = self.u32(le)? as usize;
let mut pts = Vec::with_capacity(n);
for _ in 0..n {
match self.geometry()? {
Geometry::Point(c) => pts.push(c),
Geometry::Empty => {}
other => {
return Err(ParseError::Syntax(format!(
"MultiPoint child was {other:?}, expected Point",
)));
}
}
}
Ok(Geometry::MultiPoint(pts))
}
TYPE_MULTILINESTRING => {
let n = self.u32(le)? as usize;
let mut lines = Vec::with_capacity(n);
for _ in 0..n {
match self.geometry()? {
Geometry::LineString(cs) => lines.push(cs),
other => {
return Err(ParseError::Syntax(format!(
"MultiLineString child was {other:?}, expected LineString",
)));
}
}
}
Ok(Geometry::MultiLineString(lines))
}
TYPE_MULTIPOLYGON => {
let n = self.u32(le)? as usize;
let mut polys = Vec::with_capacity(n);
for _ in 0..n {
match self.geometry()? {
Geometry::Polygon(p) => polys.push(p),
other => {
return Err(ParseError::Syntax(format!(
"MultiPolygon child was {other:?}, expected Polygon",
)));
}
}
}
Ok(Geometry::MultiPolygon(polys))
}
TYPE_GEOMETRYCOLLECTION => {
let n = self.u32(le)? as usize;
let mut children = Vec::with_capacity(n);
for _ in 0..n {
children.push(self.geometry()?);
}
Ok(Geometry::GeometryCollection(children))
}
other => Err(ParseError::UnknownType(format!("type code {other}"))),
}
}
fn polygon_body(&mut self, le: bool, tag: &TypeTag) -> Result<Polygon, ParseError> {
let nrings = self.u32(le)? as usize;
if nrings == 0 {
return Ok(Polygon {
exterior: Vec::new(),
interiors: Vec::new(),
});
}
let mut rings: Vec<Vec<Coord>> = Vec::with_capacity(nrings);
for _ in 0..nrings {
let npts = self.u32(le)? as usize;
let mut ring = Vec::with_capacity(npts);
for _ in 0..npts {
ring.push(self.coord(le, tag)?);
}
rings.push(ring);
}
let mut it = rings.into_iter();
let exterior = it.next().unwrap();
let interiors = it.collect();
Ok(Polygon {
exterior,
interiors,
})
}
fn coord(&mut self, le: bool, tag: &TypeTag) -> Result<Coord, ParseError> {
let x = self.f64(le)?;
let y = self.f64(le)?;
if tag.has_z {
let _ = self.f64(le)?;
}
if tag.has_m {
let _ = self.f64(le)?;
}
Ok((x, y))
}
}
fn resolve_type(raw: u32) -> Result<TypeTag, ParseError> {
let mut t = raw & !EWKB_FLAG_SRID;
let has_z_ewkb = t & EWKB_FLAG_Z != 0;
let has_m_ewkb = t & EWKB_FLAG_M != 0;
t &= !(EWKB_FLAG_Z | EWKB_FLAG_M);
let (base, has_z_iso, has_m_iso) = match t {
1..=7 => (t, false, false),
1001..=1007 => (t - 1000, true, false),
2001..=2007 => (t - 2000, false, true),
3001..=3007 => (t - 3000, true, true),
other => return Err(ParseError::UnknownType(format!("type code {other}"))),
};
Ok(TypeTag {
base,
has_z: has_z_ewkb || has_z_iso,
has_m: has_m_ewkb || has_m_iso,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn point_le(x: f64, y: f64) -> Vec<u8> {
let mut v = Vec::new();
v.push(0x01); v.extend_from_slice(&TYPE_POINT.to_le_bytes());
v.extend_from_slice(&x.to_le_bytes());
v.extend_from_slice(&y.to_le_bytes());
v
}
#[test]
fn point_round_trip_le() {
assert_eq!(
parse(&point_le(1.5, 2.5)).unwrap(),
Geometry::Point((1.5, 2.5))
);
}
#[test]
fn point_round_trip_be() {
let mut v = Vec::new();
v.push(0x00); v.extend_from_slice(&TYPE_POINT.to_be_bytes());
v.extend_from_slice(&1.5f64.to_be_bytes());
v.extend_from_slice(&2.5f64.to_be_bytes());
assert_eq!(parse(&v).unwrap(), Geometry::Point((1.5, 2.5)));
}
#[test]
fn point_z_drops_third_coord() {
let mut v = Vec::new();
v.push(0x01);
v.extend_from_slice(&(TYPE_POINT | EWKB_FLAG_Z).to_le_bytes());
v.extend_from_slice(&1.0f64.to_le_bytes());
v.extend_from_slice(&2.0f64.to_le_bytes());
v.extend_from_slice(&99.0f64.to_le_bytes());
assert_eq!(parse(&v).unwrap(), Geometry::Point((1.0, 2.0)));
}
#[test]
fn point_iso_z_drops_third_coord() {
let mut v = Vec::new();
v.push(0x01);
v.extend_from_slice(&1001u32.to_le_bytes());
v.extend_from_slice(&1.0f64.to_le_bytes());
v.extend_from_slice(&2.0f64.to_le_bytes());
v.extend_from_slice(&99.0f64.to_le_bytes());
assert_eq!(parse(&v).unwrap(), Geometry::Point((1.0, 2.0)));
}
#[test]
fn linestring_round_trip() {
let mut v = Vec::new();
v.push(0x01);
v.extend_from_slice(&TYPE_LINESTRING.to_le_bytes());
v.extend_from_slice(&2u32.to_le_bytes());
for n in [1.0f64, 2.0, 3.0, 4.0] {
v.extend_from_slice(&n.to_le_bytes());
}
assert_eq!(
parse(&v).unwrap(),
Geometry::LineString(vec![(1.0, 2.0), (3.0, 4.0)])
);
}
#[test]
fn polygon_with_hole() {
let mut v = Vec::new();
v.push(0x01);
v.extend_from_slice(&TYPE_POLYGON.to_le_bytes());
v.extend_from_slice(&2u32.to_le_bytes()); v.extend_from_slice(&4u32.to_le_bytes());
for n in [0.0f64, 0.0, 4.0, 0.0, 4.0, 4.0, 0.0, 0.0] {
v.extend_from_slice(&n.to_le_bytes());
}
v.extend_from_slice(&4u32.to_le_bytes());
for n in [1.0f64, 1.0, 2.0, 1.0, 2.0, 2.0, 1.0, 1.0] {
v.extend_from_slice(&n.to_le_bytes());
}
match parse(&v).unwrap() {
Geometry::Polygon(p) => {
assert_eq!(p.exterior.len(), 4);
assert_eq!(p.interiors.len(), 1);
assert_eq!(p.interiors[0].len(), 4);
}
_ => panic!("expected polygon"),
}
}
#[test]
fn multipoint_with_two_points() {
let mut v = Vec::new();
v.push(0x01);
v.extend_from_slice(&TYPE_MULTIPOINT.to_le_bytes());
v.extend_from_slice(&2u32.to_le_bytes());
v.extend_from_slice(&point_le(1.0, 2.0));
v.extend_from_slice(&point_le(3.0, 4.0));
assert_eq!(
parse(&v).unwrap(),
Geometry::MultiPoint(vec![(1.0, 2.0), (3.0, 4.0)])
);
}
#[test]
fn srid_consumed_silently() {
let mut v = Vec::new();
v.push(0x01);
v.extend_from_slice(&(TYPE_POINT | EWKB_FLAG_SRID).to_le_bytes());
v.extend_from_slice(&4326u32.to_le_bytes());
v.extend_from_slice(&1.0f64.to_le_bytes());
v.extend_from_slice(&2.0f64.to_le_bytes());
assert_eq!(parse(&v).unwrap(), Geometry::Point((1.0, 2.0)));
}
#[test]
fn nan_point_becomes_empty() {
let mut v = Vec::new();
v.push(0x01);
v.extend_from_slice(&TYPE_POINT.to_le_bytes());
v.extend_from_slice(&f64::NAN.to_le_bytes());
v.extend_from_slice(&f64::NAN.to_le_bytes());
assert_eq!(parse(&v).unwrap(), Geometry::Empty);
}
#[test]
fn truncated_input_errors() {
let bytes = &point_le(1.0, 2.0)[..10];
assert!(matches!(parse(bytes), Err(ParseError::UnexpectedEnd)));
}
}