rtz_core/geo/
shared.rs

1//! Shared functionality for geo operations.
2
3// This module is mostly used for cache preprocessing, which is expensive during coverage, so
4// it is not included in the coverage report.
5#![cfg(not(tarpaulin_include))]
6
7use core::str;
8use std::{
9    borrow::Cow,
10    collections::HashMap,
11    fmt::{Display, Formatter},
12    ops::Deref,
13};
14
15use chashmap::CHashMap;
16use geo::{Coord, Geometry, Intersects, LineString, MultiPolygon, Polygon, Rect, SimplifyVw};
17use geojson::{Feature, FeatureCollection, GeoJson};
18use rayon::prelude::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
19use serde_json::{Map, Value};
20use std::path::Path;
21
22#[cfg(feature = "self-contained")]
23use bincode::{
24    config::Configuration,
25    de::{read::BorrowReader, BorrowDecoder, Decoder},
26    enc::Encoder,
27    error::{DecodeError, EncodeError},
28    BorrowDecode, Decode, Encode,
29};
30
31use crate::base::types::Float;
32
33// Types.
34
35/// An index into the global static cache.
36pub type Id = u32;
37/// A rounded integer.
38pub type RoundDegree = i16;
39/// A rounded longitude and latitude.
40pub type RoundLngLat = (RoundDegree, RoundDegree);
41/// An `(id, Feature)` pair.
42pub type IdFeaturePair = (usize, geojson::Feature);
43
44// Concrete helpers.
45
46/// A concrete collection of concrete values.
47#[derive(Debug)]
48#[cfg_attr(feature = "self-contained", derive(Encode, Decode))]
49pub struct ConcreteVec<T>(Vec<T>)
50where
51    T: 'static;
52
53impl<T> Deref for ConcreteVec<T> {
54    type Target = Vec<T>;
55
56    fn deref(&self) -> &Self::Target {
57        &self.0
58    }
59}
60
61impl<T> From<geojson::FeatureCollection> for ConcreteVec<T>
62where
63    T: From<IdFeaturePair> + Send,
64{
65    fn from(value: geojson::FeatureCollection) -> ConcreteVec<T> {
66        let values = value.features.into_par_iter().enumerate().map(T::from).collect::<Vec<T>>();
67
68        ConcreteVec(values)
69    }
70}
71
72impl<T> IntoIterator for ConcreteVec<T> {
73    type IntoIter = std::vec::IntoIter<T>;
74    type Item = T;
75
76    fn into_iter(self) -> Self::IntoIter {
77        self.0.into_iter()
78    }
79}
80
81impl<'a, T> IntoIterator for &'a ConcreteVec<T> {
82    type IntoIter = std::slice::Iter<'a, T>;
83    type Item = &'a T;
84
85    fn into_iter(self) -> Self::IntoIter {
86        self.0.iter()
87    }
88}
89
90// Cow helpers.
91
92/// A wrapper for `Cow<'static, str>` to make encoding and decoding easier.
93#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
94pub struct EncodableString(pub Cow<'static, str>);
95
96impl AsRef<str> for EncodableString {
97    fn as_ref(&self) -> &str {
98        self.0.as_ref()
99    }
100}
101
102impl Deref for EncodableString {
103    type Target = Cow<'static, str>;
104
105    fn deref(&self) -> &Self::Target {
106        &self.0
107    }
108}
109
110impl Display for EncodableString {
111    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
112        write!(f, "{}", self.0)
113    }
114}
115
116#[cfg(feature = "self-contained")]
117impl Encode for EncodableString {
118    fn encode<E>(&self, encoder: &mut E) -> Result<(), EncodeError>
119    where
120        E: Encoder,
121    {
122        let data = pad_string_alignment(self);
123
124        data.encode(encoder)
125    }
126}
127
128#[cfg(feature = "self-contained")]
129impl Decode for EncodableString {
130    fn decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
131    where
132        D: Decoder,
133    {
134        let data = Vec::decode(decoder)?;
135
136        // Now, we can limit the slice to trim the null padding.
137        unpad_string_alignment(&data).map(ToString::to_string).map(Cow::Owned).map(EncodableString)
138    }
139}
140
141#[cfg(feature = "self-contained")]
142impl<'de> BorrowDecode<'de> for EncodableString {
143    fn borrow_decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
144    where
145        D: BorrowDecoder<'de>,
146    {
147        let length = usize::decode(decoder)?;
148        let slice = decoder.borrow_reader().take_bytes(length * std::mem::size_of::<u8>())?;
149
150        // SAFETY: We know this slice is built into the binary, and it has a static lifetime.
151        let slice = unsafe { std::mem::transmute::<&'_ [u8], &'static [u8]>(slice) };
152
153        // Now, we can limit the slice to trim the null padding.
154        unpad_string_alignment(slice).map(Cow::Borrowed).map(EncodableString)
155    }
156}
157
158/// A wrapper for `Option<Cow<'static, str>>` to make encoding and decoding easier.
159#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
160pub struct EncodableOptionString(pub Option<Cow<'static, str>>);
161
162impl EncodableOptionString {
163    /// Converts from `EncodableOptionString`` to `Option<&Cow<'static, str>>``.
164    pub fn as_ref(&self) -> Option<&Cow<'static, str>> {
165        self.0.as_ref()
166    }
167}
168
169impl Deref for EncodableOptionString {
170    type Target = Option<Cow<'static, str>>;
171
172    fn deref(&self) -> &Self::Target {
173        &self.0
174    }
175}
176
177impl Display for EncodableOptionString {
178    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
179        match self.as_ref() {
180            None => write!(f, "None"),
181            Some(cow) => write!(f, "{}", cow),
182        }
183    }
184}
185
186#[cfg(feature = "self-contained")]
187impl Encode for EncodableOptionString {
188    fn encode<E>(&self, encoder: &mut E) -> Result<(), EncodeError>
189    where
190        E: Encoder,
191    {
192        match self.as_ref() {
193            None => 0usize.encode(encoder),
194            Some(cow) => {
195                // Descriminant.
196                1usize.encode(encoder)?;
197
198                // Padded data.
199                let data = pad_string_alignment(cow);
200
201                data.encode(encoder)
202            }
203        }
204    }
205}
206
207#[cfg(feature = "self-contained")]
208impl Decode for EncodableOptionString {
209    fn decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
210    where
211        D: Decoder,
212    {
213        let variant = usize::decode(decoder)?;
214
215        let result = match variant {
216            0 => EncodableOptionString(None),
217            1 => {
218                let es = EncodableString::decode(decoder)?;
219
220                EncodableOptionString(Some(es.0))
221            }
222            _ => panic!("Unsupported variant."),
223        };
224
225        Ok(result)
226    }
227}
228
229#[cfg(feature = "self-contained")]
230impl<'de> BorrowDecode<'de> for EncodableOptionString {
231    fn borrow_decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
232    where
233        D: BorrowDecoder<'de>,
234    {
235        let variant = usize::decode(decoder)?;
236
237        let result = match variant {
238            0 => EncodableOptionString(None),
239            1 => {
240                let es = EncodableString::borrow_decode(decoder)?;
241
242                EncodableOptionString(Some(es.0))
243            }
244            _ => panic!("Unsupported variant."),
245        };
246
247        Ok(result)
248    }
249}
250
251// Traits.
252
253/// A trait for types that have a [`Geometry`].
254///
255/// Helps abstract away this property so the helper methods can be generalized.
256pub trait HasGeometry {
257    /// Get the `id` of the [`HasGeometry`].
258    fn id(&self) -> usize;
259    /// Get the [`Geometry`] of the [`HasGeometry`].
260    fn geometry(&self) -> &Geometry<Float>;
261}
262
263/// A trait for types that have properties.
264pub trait HasProperties {
265    /// Get the properties of the [`HasProperties`].
266    fn properties(&self) -> Map<String, Value>;
267}
268
269/// A trait that allows types to be converted to GeoJSON.
270pub trait ToGeoJsonFeature {
271    /// Convert the type to GeoJSON.
272    fn to_feature(&self) -> geojson::Feature;
273}
274
275impl<T> ToGeoJsonFeature for T
276where
277    T: HasGeometry + HasProperties,
278{
279    fn to_feature(&self) -> geojson::Feature {
280        let geometry = self.geometry();
281        let properties = self.properties();
282
283        geojson::Feature {
284            properties: Some(properties),
285            geometry: Some(geojson::Geometry::from(geometry)),
286            ..geojson::Feature::default()
287        }
288    }
289}
290
291/// A trait that allows for iterator types to be converted to GeoJSON.
292pub trait ToGeoJsonFeatureCollection {
293    /// Convert the type to GeoJSON.
294    fn to_feature_collection(&self) -> geojson::FeatureCollection;
295}
296
297/// Implementation specifically for [`ConcreteVec`].
298impl<'a, L, D, T> ToGeoJsonFeatureCollection for &'a L
299where
300    L: Deref<Target = D>,
301    D: Deref<Target = [T]>,
302    T: ToGeoJsonFeature + 'static,
303{
304    fn to_feature_collection(&self) -> geojson::FeatureCollection {
305        let features = self.iter().map(|x| x.to_feature()).collect();
306
307        geojson::FeatureCollection {
308            features,
309            bbox: None,
310            foreign_members: None,
311        }
312    }
313}
314
315/// A trait to convert to GeoJSON.
316pub trait ToGeoJson {
317    /// Convert the type to GeoJSON.
318    fn to_geojson(&self) -> GeoJson;
319}
320
321impl<T> ToGeoJson for T
322where
323    T: ToGeoJsonFeatureCollection,
324{
325    fn to_geojson(&self) -> GeoJson {
326        GeoJson::FeatureCollection(self.to_feature_collection())
327    }
328}
329
330// Helper methods.
331
332/// Pads a String before encoding to ensure that the string is aligned to the correct byte boundary.
333#[cfg(feature = "self-contained")]
334pub fn pad_string_alignment(string: impl AsRef<str>) -> Vec<u8> {
335    let alignment = std::mem::align_of::<Float>();
336    let padding = alignment - (string.as_ref().as_bytes().len() % alignment);
337
338    string.as_ref().as_bytes().iter().chain(std::iter::repeat(&0u8).take(padding)).copied().collect::<Vec<u8>>()
339}
340
341/// Unpads a String after decoding to remove any null padding.
342#[cfg(feature = "self-contained")]
343pub fn unpad_string_alignment(data: &[u8]) -> Result<&str, DecodeError> {
344    let terminator = data.iter().position(|&x| x == 0).unwrap_or(data.len());
345    let slice = &data[..terminator];
346
347    let str = str::from_utf8(slice).map_err(|e| DecodeError::Utf8 { inner: e })?;
348
349    Ok(str)
350}
351
352/// Simplifies a [`Geometry`] using the [Visvalingam-Whyatt algorithm](https://bost.ocks.org/mike/simplify/).
353///
354/// For geometries that cannot be simplified, the original geometry is returned.
355pub fn simplify_geometry(geometry: Geometry<Float>, simplification_epsilon: Float) -> Geometry<Float> {
356    #[cfg(not(feature = "unsimplified"))]
357    let geometry = match geometry {
358        Geometry::Polygon(polygon) => {
359            let simplified = polygon.simplify_vw(&simplification_epsilon);
360            Geometry::Polygon(simplified)
361        }
362        Geometry::MultiPolygon(multi_polygon) => {
363            let simplified = multi_polygon.simplify_vw(&simplification_epsilon);
364            Geometry::MultiPolygon(simplified)
365        }
366        Geometry::LineString(line_string) => {
367            let simplified = line_string.simplify_vw(&simplification_epsilon);
368            Geometry::LineString(simplified)
369        }
370        Geometry::MultiLineString(multi_line_string) => {
371            let simplified = multi_line_string.simplify_vw(&simplification_epsilon);
372            Geometry::MultiLineString(simplified)
373        }
374        g => g,
375    };
376
377    geometry
378}
379
380/// Get the cache from the timezones.
381pub fn get_lookup_from_geometries<T>(geometries: &ConcreteVec<T>) -> HashMap<RoundLngLat, EncodableIds>
382where
383    T: HasGeometry + Send + Sync,
384{
385    let map = CHashMap::new();
386
387    (-180..180).into_par_iter().for_each(|x| {
388        for y in -90..90 {
389            let xf = x as Float;
390            let yf = y as Float;
391
392            let rect = Rect::new(Coord { x: xf, y: yf }, Coord { x: xf + 1.0, y: yf + 1.0 });
393
394            let mut intersected = Vec::new();
395
396            for g in geometries {
397                if g.geometry().intersects(&rect) {
398                    intersected.push(g.id() as Id);
399                }
400            }
401
402            map.insert((x as RoundDegree, y as RoundDegree), intersected);
403        }
404    });
405
406    let mut cache = HashMap::new();
407    for (key, value) in map.into_iter() {
408        cache.insert(key, EncodableIds(value));
409    }
410
411    cache
412}
413
414/// Generate the bincode representation of the 100km cache.
415///
416/// "100km" is a bit of a misnomer.  This is really 100km _at the equator_, but this
417/// makes it easier to reason about what the caches are doing.
418#[cfg(feature = "self-contained")]
419fn generate_lookup_bincode<T>(bincode_input: impl AsRef<Path>, bincode_destination: impl AsRef<Path>)
420where
421    T: HasGeometry + Decode + Send + Sync + 'static,
422{
423    let data = std::fs::read(bincode_input).unwrap();
424    let (timezones, _len): (ConcreteVec<T>, usize) = bincode::decode_from_slice(&data, get_global_bincode_config()).unwrap();
425
426    let cache = get_lookup_from_geometries(&timezones);
427
428    bincode::encode_into_std_write(cache, &mut std::fs::File::create(bincode_destination).unwrap(), get_global_bincode_config()).unwrap();
429}
430
431/// Get the concrete timezones from features.
432pub fn get_items_from_features<T>(features: FeatureCollection) -> ConcreteVec<T>
433where
434    T: HasGeometry + From<IdFeaturePair> + Send,
435{
436    ConcreteVec::from(features)
437}
438
439/// Generate bincode representation of the timezones.
440#[cfg(feature = "self-contained")]
441fn generate_item_bincode<T>(geojson_features: FeatureCollection, bincode_destination: impl AsRef<Path>)
442where
443    T: HasGeometry + Encode + From<IdFeaturePair> + Send + 'static,
444{
445    let items: ConcreteVec<T> = get_items_from_features(geojson_features);
446    bincode::encode_into_std_write(items, &mut std::fs::File::create(bincode_destination).unwrap(), get_global_bincode_config()).unwrap();
447}
448
449/// Get the GeoJSON features from the binary assets.
450pub fn get_geojson_features_from_file(geojson_input: impl AsRef<Path>) -> FeatureCollection {
451    let geojson = std::fs::read_to_string(geojson_input).unwrap();
452    FeatureCollection::try_from(geojson.parse::<GeoJson>().unwrap()).unwrap()
453}
454
455/// Get the GeoJSON features from the binary assets.
456pub fn get_geojson_features_from_string(geojson_input: &str) -> FeatureCollection {
457    FeatureCollection::try_from(geojson_input.parse::<GeoJson>().unwrap()).unwrap()
458}
459
460/// Get the GeoJSON feature from the binary assets.
461pub fn get_geojson_feature_from_file(geojson_input: impl AsRef<Path>) -> Feature {
462    let geojson = std::fs::read_to_string(geojson_input).unwrap();
463    Feature::try_from(geojson.parse::<GeoJson>().unwrap()).unwrap()
464}
465
466/// Get the GeoJSON feature from the binary assets.
467pub fn get_geojson_feature_from_string(geojson_input: &str) -> Feature {
468    Feature::try_from(geojson_input.parse::<GeoJson>().unwrap()).unwrap()
469}
470
471/// Generates new bincodes for the timezones and the cache from the GeoJSON.
472#[cfg(feature = "self-contained")]
473pub fn generate_bincodes<T>(geojson_features: FeatureCollection, timezone_bincode_destination: impl AsRef<Path>, lookup_bincode_destination: impl AsRef<Path>)
474where
475    T: HasGeometry + Encode + From<IdFeaturePair> + Decode + Send + Sync + 'static,
476{
477    generate_item_bincode::<T>(geojson_features, timezone_bincode_destination.as_ref());
478    generate_lookup_bincode::<T>(timezone_bincode_destination, lookup_bincode_destination);
479}
480
481// Helpers to get GeoJSON features from a source.
482
483/// Trait that supports getting the GeoJSON features from a source.
484pub trait CanGetGeoJsonFeaturesFromSource {
485    /// Get the GeoJSON features from a source.
486    fn get_geojson_features_from_source() -> geojson::FeatureCollection;
487}
488
489// Bincode helpers.
490
491/// Computes the best bincode to be used for the target architecture.
492#[cfg(all(feature = "self-contained", target_endian = "big"))]
493
494pub fn get_global_bincode_config() -> Configuration<bincode::config::BigEndian, bincode::config::Fixint> {
495    bincode::config::legacy().with_big_endian()
496}
497
498/// Computes the best bincode to be used for the target architecture.
499#[cfg(all(feature = "self-contained", target_endian = "little"))]
500pub fn get_global_bincode_config() -> Configuration<bincode::config::LittleEndian, bincode::config::Fixint> {
501    bincode::config::legacy()
502}
503
504// Special encoding / decoding logic for geometries.
505
506/// A wrapped [`Geometry`] that can be encoded and decoded via bincode.
507#[derive(Debug)]
508pub struct EncodableGeometry(pub Geometry<Float>);
509
510#[cfg(feature = "self-contained")]
511fn encode_poly<E>(polygon: &Polygon<Float>, encoder: &mut E) -> Result<(), EncodeError>
512where
513    E: Encoder,
514{
515    let exterior = &polygon.exterior().0;
516
517    // Encode the exterior length.
518    exterior.len().encode(encoder)?;
519
520    // Encode the exterior points.
521    for point in exterior {
522        point.x.encode(encoder)?;
523        point.y.encode(encoder)?;
524    }
525
526    let interiors = polygon.interiors();
527
528    // Encode the number of interiors.
529    interiors.len().encode(encoder)?;
530
531    // Encode the interiors.
532    for interior in interiors {
533        let interior = &interior.0;
534
535        // Encode the interior length.
536        interior.len().encode(encoder)?;
537
538        // Encode the interior points.
539        for point in interior {
540            point.x.encode(encoder)?;
541            point.y.encode(encoder)?;
542        }
543    }
544
545    Ok(())
546}
547
548#[cfg(feature = "self-contained")]
549impl Encode for EncodableGeometry {
550    fn encode<E>(&self, encoder: &mut E) -> Result<(), EncodeError>
551    where
552        E: Encoder,
553    {
554        match &self.0 {
555            Geometry::Polygon(polygon) => {
556                // Encode the variant.
557                0usize.encode(encoder)?;
558
559                encode_poly(polygon, encoder)?;
560            }
561            Geometry::MultiPolygon(multi_polygon) => {
562                // Encode the variant.
563                1usize.encode(encoder)?;
564
565                let polygons = &multi_polygon.0;
566
567                // Encode the number of polygons.
568                polygons.len().encode(encoder)?;
569
570                // Encode the polygons.
571                for polygon in polygons {
572                    encode_poly(polygon, encoder)?;
573                }
574            }
575            _ => panic!("Unsupported geometry variant."),
576        }
577
578        Ok(())
579    }
580}
581
582#[cfg(feature = "self-contained")]
583fn decode_poly<D>(decoder: &mut D) -> Result<Polygon<Float>, DecodeError>
584where
585    D: Decoder,
586{
587    let exterior_len = usize::decode(decoder)?;
588
589    let mut exterior = Vec::with_capacity(exterior_len);
590
591    for _ in 0..exterior_len {
592        let x = Float::decode(decoder)?;
593        let y = Float::decode(decoder)?;
594
595        exterior.push(Coord { x, y });
596    }
597
598    let interior_len = usize::decode(decoder)?;
599
600    let mut interiors = Vec::with_capacity(interior_len);
601
602    for _ in 0..interior_len {
603        let interior_len = usize::decode(decoder)?;
604
605        let mut interior = Vec::with_capacity(interior_len);
606
607        for _ in 0..interior_len {
608            let x = Float::decode(decoder)?;
609            let y = Float::decode(decoder)?;
610
611            interior.push(Coord { x, y });
612        }
613
614        interiors.push(LineString(interior));
615    }
616
617    Ok(Polygon::new(LineString(exterior), interiors))
618}
619
620#[cfg(feature = "self-contained")]
621fn borrow_decode_poly<'de, D>(decoder: &mut D) -> Result<Polygon<Float>, DecodeError>
622where
623    D: BorrowDecoder<'de>,
624{
625    let exterior_len = usize::decode(decoder)?;
626    let exterior_slice = decoder.borrow_reader().take_bytes(exterior_len * std::mem::size_of::<Float>() * 2)?;
627
628    // SAFETY: Perform unholy rites, and summon the devil, lol.
629    // Basically, this is an extreme optimization to prevent loading huge amounts of data into memory that are already
630    // in memory as part of the binary assets.
631    let exterior = unsafe { Vec::from_raw_parts(exterior_slice.as_ptr() as *mut Coord<Float>, exterior_len, exterior_len) };
632
633    let interior_len = usize::decode(decoder)?;
634
635    let mut interiors = Vec::with_capacity(interior_len);
636
637    for _ in 0..interior_len {
638        let interior_len = usize::decode(decoder)?;
639        let interior_slice = decoder.borrow_reader().take_bytes(interior_len * std::mem::size_of::<Float>() * 2)?;
640
641        // SAFETY: Perform unholy rites again: see above.
642        let interior = unsafe { Vec::from_raw_parts(interior_slice.as_ptr() as *mut Coord<Float>, interior_len, interior_len) };
643
644        interiors.push(LineString(interior));
645    }
646
647    Ok(Polygon::new(LineString(exterior), interiors))
648}
649
650#[cfg(feature = "self-contained")]
651impl Decode for EncodableGeometry {
652    fn decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
653    where
654        D: Decoder,
655    {
656        let variant = usize::decode(decoder)?;
657
658        let geometry = match variant {
659            0 => {
660                let polygon = decode_poly(decoder)?;
661
662                Geometry::Polygon(polygon)
663            }
664            1 => {
665                let polygon_len = usize::decode(decoder)?;
666
667                let mut polygons = Vec::with_capacity(polygon_len);
668
669                for _ in 0..polygon_len {
670                    let polygon = decode_poly(decoder)?;
671
672                    polygons.push(polygon);
673                }
674
675                Geometry::MultiPolygon(MultiPolygon::new(polygons))
676            }
677            _ => panic!("Unsupported geometry variant."),
678        };
679
680        Ok(EncodableGeometry(geometry))
681    }
682}
683
684#[cfg(feature = "self-contained")]
685impl<'de> BorrowDecode<'de> for EncodableGeometry {
686    fn borrow_decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
687    where
688        D: BorrowDecoder<'de>,
689    {
690        let variant = usize::decode(decoder)?;
691
692        let geometry = match variant {
693            0 => {
694                let polygon = borrow_decode_poly(decoder)?;
695
696                Geometry::Polygon(polygon)
697            }
698            1 => {
699                let polygon_len = usize::decode(decoder)?;
700
701                let mut polygons = Vec::with_capacity(polygon_len);
702
703                for _ in 0..polygon_len {
704                    let polygon = borrow_decode_poly(decoder)?;
705
706                    polygons.push(polygon);
707                }
708
709                Geometry::MultiPolygon(MultiPolygon::new(polygons))
710            }
711            _ => panic!("Unsupported geometry variant."),
712        };
713
714        Ok(EncodableGeometry(geometry))
715    }
716}
717
718/// A wrapped ['Vec`] that can be encoded and decoded via bincode.
719#[derive(Debug)]
720pub struct EncodableIds(pub Vec<Id>);
721
722impl Deref for EncodableIds {
723    type Target = Vec<Id>;
724
725    fn deref(&self) -> &Self::Target {
726        &self.0
727    }
728}
729
730impl AsRef<[Id]> for EncodableIds {
731    fn as_ref(&self) -> &[Id] {
732        &self.0
733    }
734}
735
736#[cfg(feature = "self-contained")]
737impl Encode for EncodableIds {
738    fn encode<E>(&self, encoder: &mut E) -> Result<(), EncodeError>
739    where
740        E: Encoder,
741    {
742        // Encode the exterior length.
743        self.0.len().encode(encoder)?;
744
745        // Encode the exterior points.
746        for x in &self.0 {
747            x.encode(encoder)?;
748        }
749
750        Ok(())
751    }
752}
753
754#[cfg(feature = "self-contained")]
755impl Decode for EncodableIds {
756    fn decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
757    where
758        D: Decoder,
759    {
760        let len = usize::decode(decoder)?;
761
762        let mut vec = Vec::with_capacity(len);
763
764        for _ in 0..len {
765            let x = Id::decode(decoder)?;
766
767            vec.push(x);
768        }
769
770        Ok(EncodableIds(vec))
771    }
772}
773
774#[cfg(feature = "self-contained")]
775impl<'de> BorrowDecode<'de> for EncodableIds {
776    fn borrow_decode<D>(decoder: &mut D) -> Result<Self, DecodeError>
777    where
778        D: BorrowDecoder<'de>,
779    {
780        let len = usize::decode(decoder)?;
781        let slice = decoder.borrow_reader().take_bytes(len * std::mem::size_of::<Id>())?;
782
783        // SAFETY: Perform unholy rites again: see above.
784        let vec = unsafe { Vec::from_raw_parts(slice.as_ptr() as *mut Id, len, len) };
785
786        Ok(EncodableIds(vec))
787    }
788}