tor-geoip-db 0.1.0-pre2

Raw geoip data for use by tor-geoip
Documentation
#![doc = include_str!("../README.md")]

#[doc = include_str!("../doc/export_info_v4.md")]
pub mod ipv4_source{}

#[doc = include_str!("../doc/export_info_v6.md")]
pub mod ipv6_source{}

use std::num::NonZeroU16;

/// Declare alignment types.
macro_rules! align {
    {
        $($t:ident ($n:expr)),* $(,)?
    } => {
        $(
            /// Wrapper to enforce the alignment of a structure to
            #[doc=concat!(stringify!($n), " bytes.")]
            #[repr(align($n))]
            struct $t<T>(T);
        )*
    }
}

align!{
    Align2(2),
    Align4(4),
    Align16(16),
}

/// Declare static variables to hold aligned data.
macro_rules! data {
    {
        $({$w:ident $v:ident from $path:expr})*
    } => {
        $(
            static $v : $w<[u8; include_bytes!($path).len()]>
                = $w(*include_bytes!($path));
        )*
    }
}

data! {
    { Align4  GEOIP_DATA_V4S from "../data/geoip_data_v4s" }
    { Align2  GEOIP_DATA_V4C from "../data/geoip_data_v4c" }
    { Align16 GEOIP_DATA_V6S from "../data/geoip_data_v6s" }
    { Align2  GEOIP_DATA_V6C from "../data/geoip_data_v6c" }
}

/// Transmute byte slice into a slice of T.
///
/// # Panics
///
/// Panics if the byte slice is not correctly aligned for `T`,
/// or if it contains excess bytes.
///
/// # Safety
///
/// The safety requirements of [`slice::align_to`] and [`std::mem::transmute`]
/// apply.
///
/// In particular, is safe to use this function if every possible
/// sequence of bits is a valid representation for a `T`.
/// This requirement holds in eery case where we use this function.
///
/// [`slice::align_to`]: https://doc.rust-lang.org/std/primitive.slice.html#method.align_to
unsafe fn export<T>(a: &'static [u8]) -> &'static [T] {
    // SAFETY: Since we require that every bit sequence is a valid T,
    // it is safe to use this function to enforce alignment
    // and transmute.
    let (pre, data, post) = unsafe { a.align_to::<T>() };
    assert!(pre.is_empty(), "Data was not aligned!");
    assert!(post.is_empty(), "Data had an invalid length!");
    data
}

pub fn ipv4s() -> &'static [u32] {
    // SAFETY: Every sequence of bits is a valid u32.
    unsafe { export(&GEOIP_DATA_V4S.0) }
}
pub fn ipv4c() -> &'static [Option<NonZeroU16>] {
    // SAFETY: Every sequence of bits is a valid Option<NonzeroU16>
    unsafe { export(&GEOIP_DATA_V4C.0) }
}
pub fn ipv6s() -> &'static [u128] {
    // SAFETY: Every sequence of bits is a valid u128
    unsafe { export(&GEOIP_DATA_V6S.0) }
}
pub fn ipv6c() -> &'static [Option<NonZeroU16>] {
    // SAFETY: Every sequence of bits is a valid Option<NonzeroU16>
    unsafe { export(&GEOIP_DATA_V6C.0) }
}

#[cfg(test)]
mod test {
    use super::*;
    
    #[test]
    fn v4_looks_ok() {
        let (v4s, v4c) = (ipv4s(), ipv4c());
        assert!(v4s.is_sorted());
        assert_eq!(v4s.len(), v4c.len());
    }

    #[test]
    fn v6_looks_ok() {
        let (v6s, v6c) = (ipv6s(), ipv6c());
        assert!(v6s.is_sorted());
        assert_eq!(v6s.len(), v6c.len());
    }
}