@@ -152,74 +152,114 @@ impl WkbHeader {
152152 }
153153}
154154
155- /// Read the `(x, y)` of a WKB Point without parsing the geometry generally .
155+ /// The coordinate layout of a WKB Point, discovered by reading only its header .
156156///
157- /// This is a fast path for the common case of sampling/indexing by a single
158- /// point: a Point WKB has a fixed layout (`byte order`, `type code`, optional
159- /// SRID, then the coordinate), so the coordinate is at a known offset and can
160- /// be read with a couple of fixed-offset loads. It deliberately handles only
161- /// Points — any other geometry is an error.
157+ /// This is the fast-path primitive for sampling/indexing by Point. A Point WKB
158+ /// has a fixed layout (`byte order`, `type code`, optional SRID, then the
159+ /// coordinate), so [`try_from_wkb`](Self::try_from_wkb) can cheaply decide
160+ /// whether a buffer is a Point — reading only the first few header bytes, never
161+ /// a coordinate — and a later [`read_xy`](Self::read_xy) reads the coordinate
162+ /// from the offset it already found.
162163///
163- /// Returns `Ok(None)` for `POINT EMPTY` (encoded as `NaN`/`NaN`), `Ok(Some((x, y)))`
164- /// for a finite point, and an error if the bytes are not a Point or are truncated.
165- /// Accepts both ISO (with Z/M dimension flags) and EWKB (with Z/M/SRID flags)
166- /// encodings; only the first two ordinates are read.
167- pub fn read_point_xy ( buf : & [ u8 ] ) -> Result < Option < ( f64 , f64 ) > , SedonaGeometryError > {
168- let little_endian = match buf. first ( ) {
169- Some ( 1 ) => true ,
170- Some ( 0 ) => false ,
171- _ => {
172- return Err ( SedonaGeometryError :: Invalid (
173- "WKB: missing or invalid byte order" . to_string ( ) ,
174- ) )
175- }
176- } ;
164+ /// Splitting the header inspection from the coordinate read lets a binary
165+ /// function (e.g. distance) test *both* operands first and read coordinates only
166+ /// when both are Points, without re-parsing the header or wasting a coordinate
167+ /// read on rows that fall back to the general parser.
168+ #[ derive( Debug , Clone , Copy ) ]
169+ pub struct WkbPointLayout {
170+ little_endian : bool ,
171+ coord_offset : usize ,
172+ }
177173
178- let read_u32 = |offset : usize | -> Result < u32 , SedonaGeometryError > {
179- let bytes: [ u8 ; 4 ] = buf
180- . get ( offset..offset + 4 )
174+ impl WkbPointLayout {
175+ /// Inspects a WKB header, returning the Point layout or `Ok(None)` if `buf`
176+ /// is not a Point. Reads only the byte order and type code (recognizing the
177+ /// EWKB SRID prefix) — never a coordinate. Errors only on a missing/invalid
178+ /// byte order or a truncated type code. Accepts both ISO (with Z/M dimension
179+ /// flags) and EWKB (with Z/M/SRID flags) encodings.
180+ #[ inline]
181+ pub fn try_from_wkb ( buf : & [ u8 ] ) -> Result < Option < Self > , SedonaGeometryError > {
182+ let little_endian = match buf. first ( ) {
183+ Some ( 1 ) => true ,
184+ Some ( 0 ) => false ,
185+ _ => {
186+ return Err ( SedonaGeometryError :: Invalid (
187+ "WKB: missing or invalid byte order" . to_string ( ) ,
188+ ) )
189+ }
190+ } ;
191+
192+ let type_bytes: [ u8 ; 4 ] = buf
193+ . get ( 1 ..5 )
181194 . ok_or_else ( || SedonaGeometryError :: Invalid ( "WKB: truncated header" . to_string ( ) ) ) ?
182195 . try_into ( )
183196 . unwrap ( ) ;
184- Ok ( if little_endian {
185- u32:: from_le_bytes ( bytes )
197+ let type_code = if little_endian {
198+ u32:: from_le_bytes ( type_bytes )
186199 } else {
187- u32:: from_be_bytes ( bytes)
188- } )
189- } ;
200+ u32:: from_be_bytes ( type_bytes)
201+ } ;
202+
203+ // The low three bits of the base type identify a Point (1) regardless of
204+ // the ISO dimension digits or EWKB high-bit flags.
205+ if type_code & 0x7 != 1 {
206+ return Ok ( None ) ;
207+ }
208+
209+ // EWKB carries a 4-byte SRID between the type code and the coordinate.
210+ let has_srid = type_code & SRID_FLAG_BIT != 0 ;
211+ let coord_offset = if has_srid { 9 } else { 5 } ;
190212
191- let read_f64 = |offset : usize | -> Result < f64 , SedonaGeometryError > {
213+ Ok ( Some ( Self {
214+ little_endian,
215+ coord_offset,
216+ } ) )
217+ }
218+
219+ /// Reads the `(x, y)` from `buf`, which must be the same buffer passed to
220+ /// [`try_from_wkb`](Self::try_from_wkb). Returns `Ok(None)` for `POINT EMPTY`
221+ /// (encoded as `NaN`/`NaN`); errors if the coordinate is truncated.
222+ #[ inline]
223+ pub fn read_xy ( & self , buf : & [ u8 ] ) -> Result < Option < ( f64 , f64 ) > , SedonaGeometryError > {
224+ let x = self . read_f64 ( buf, self . coord_offset ) ?;
225+ let y = self . read_f64 ( buf, self . coord_offset + 8 ) ?;
226+ if x. is_nan ( ) && y. is_nan ( ) {
227+ Ok ( None )
228+ } else {
229+ Ok ( Some ( ( x, y) ) )
230+ }
231+ }
232+
233+ #[ inline]
234+ fn read_f64 ( & self , buf : & [ u8 ] , offset : usize ) -> Result < f64 , SedonaGeometryError > {
192235 let bytes: [ u8 ; 8 ] = buf
193236 . get ( offset..offset + 8 )
194237 . ok_or_else ( || SedonaGeometryError :: Invalid ( "WKB: truncated coordinate" . to_string ( ) ) ) ?
195238 . try_into ( )
196239 . unwrap ( ) ;
197- Ok ( if little_endian {
240+ Ok ( if self . little_endian {
198241 f64:: from_le_bytes ( bytes)
199242 } else {
200243 f64:: from_be_bytes ( bytes)
201244 } )
202- } ;
203-
204- let type_code = read_u32 ( 1 ) ?;
205- // The low three bits of the base type identify a Point (1) regardless of the
206- // ISO dimension digits or EWKB high-bit flags.
207- if type_code & 0x7 != 1 {
208- return Err ( SedonaGeometryError :: Invalid ( format ! (
209- "WKB: expected a Point (type code {type_code})"
210- ) ) ) ;
211245 }
246+ }
212247
213- // EWKB carries a 4-byte SRID between the type code and the coordinate.
214- let has_srid = type_code & SRID_FLAG_BIT != 0 ;
215- let coord_offset = if has_srid { 9 } else { 5 } ;
216-
217- let x = read_f64 ( coord_offset) ?;
218- let y = read_f64 ( coord_offset + 8 ) ?;
219- if x. is_nan ( ) && y. is_nan ( ) {
220- Ok ( None )
221- } else {
222- Ok ( Some ( ( x, y) ) )
248+ /// Read the `(x, y)` of a WKB Point without parsing the geometry generally.
249+ ///
250+ /// Convenience wrapper over [`WkbPointLayout`] for the single-Point case: it
251+ /// deliberately handles only Points — any other geometry is an error.
252+ ///
253+ /// Returns `Ok(None)` for `POINT EMPTY` (encoded as `NaN`/`NaN`), `Ok(Some((x, y)))`
254+ /// for a finite point, and an error if the bytes are not a Point or are truncated.
255+ /// Accepts both ISO (with Z/M dimension flags) and EWKB (with Z/M/SRID flags)
256+ /// encodings; only the first two ordinates are read.
257+ pub fn read_point_xy ( buf : & [ u8 ] ) -> Result < Option < ( f64 , f64 ) > , SedonaGeometryError > {
258+ match WkbPointLayout :: try_from_wkb ( buf) ? {
259+ Some ( layout) => layout. read_xy ( buf) ,
260+ None => Err ( SedonaGeometryError :: Invalid (
261+ "WKB: expected a Point" . to_string ( ) ,
262+ ) ) ,
223263 }
224264}
225265
0 commit comments