use std::{
fmt,
hash::{Hash, Hasher},
};
use bitflags::bitflags;
use cxx::ExternType;
#[cfg(feature = "proto")]
use prost::Message;
#[cfg(feature = "proto")]
mod actor;
pub mod config;
#[cfg(feature = "proto")]
pub mod proto;
#[cfg(feature = "proto")]
pub use actor::{Actor, Response};
pub use config::Config;
pub use config::ConfigBuilder;
pub use ffi::AdminInfo;
pub use ffi::EdgeInfo;
pub use ffi::EdgeUse;
pub use ffi::GraphLevel;
pub use ffi::RoadClass;
pub use ffi::TimeZoneInfo;
pub use ffi::TrafficTile;
pub use ffi::decode_weekly_speeds;
pub use ffi::encode_weekly_speeds;
#[cxx::bridge]
mod ffi {
#[derive(Clone, Copy, Debug)]
enum GraphLevel {
Highway = 0,
Arterial = 1,
Local = 2,
}
#[namespace = "valhalla::baldr"]
#[cxx_name = "Use"]
#[repr(u8)]
#[derive(Debug)]
enum EdgeUse {
kRoad = 0,
kRamp = 1, kTurnChannel = 2, kTrack = 3, kDriveway = 4, kAlley = 5, kParkingAisle = 6, kEmergencyAccess = 7, kDriveThru = 8, kCuldesac = 9, kLivingStreet = 10, kServiceRoad = 11,
kCycleway = 20, kMountainBike = 21,
kSidewalk = 24,
kFootway = 25,
kSteps = 26, kPath = 27,
kPedestrian = 28,
kBridleway = 29,
kPedestrianCrossing = 32, kElevator = 33,
kEscalator = 34,
kPlatform = 35,
kRestArea = 30,
kServiceArea = 31,
kOther = 40,
kFerry = 41,
kRailFerry = 42,
kConstruction = 43,
kRail = 50, kBus = 51, kEgressConnection = 52, kPlatformConnection = 53, kTransitConnection = 54, }
#[namespace = "valhalla::baldr"]
#[repr(u8)]
#[derive(Debug, PartialOrd, Ord)]
enum RoadClass {
kMotorway = 0,
kTrunk = 1,
kPrimary = 2,
kSecondary = 3,
kTertiary = 4,
kUnclassified = 5,
kResidential = 6,
kServiceOther = 7,
kInvalid = 8,
}
struct EdgeInfo {
way_id: u64,
speed_limit: u8,
shape: String,
}
struct LatLon {
lat: f64,
lon: f64,
}
#[derive(Clone)]
struct AdminInfo {
country_text: String,
state_text: String,
country_iso: String,
state_iso: String,
}
#[derive(Clone)]
struct TimeZoneInfo {
name: String,
offset_seconds: i32,
}
struct TrafficTile {
header: *mut u64,
speeds: *mut u64,
edge_count: u32,
traffic_tar: SharedPtr<tar>,
}
impl Vec<GraphId> {}
unsafe extern "C++" {
include!("valhalla/src/libvalhalla.hpp");
type GraphLevel;
#[namespace = "valhalla::baldr"]
type GraphId = crate::GraphId;
fn from_parts(level: u32, tileid: u32, id: u32) -> Result<GraphId>;
#[namespace = "boost::property_tree"]
type ptree = crate::config::ffi::ptree;
type TileSet;
fn new_tileset(config: &ptree) -> Result<SharedPtr<TileSet>>;
fn tiles(self: &TileSet) -> Vec<GraphId>;
fn tiles_in_bbox(
self: &TileSet,
min_lat: f32,
min_lon: f32,
max_lat: f32,
max_lon: f32,
level: GraphLevel,
) -> Vec<GraphId>;
fn get_graph_tile(self: &TileSet, id: GraphId) -> *const GraphTile;
fn get_traffic_tile(self: &TileSet, id: GraphId) -> Result<TrafficTile>;
fn dataset_id(self: &TileSet) -> u64;
#[namespace = "valhalla::baldr"]
type GraphTile;
unsafe fn clone(tile: *const GraphTile) -> *const GraphTile;
unsafe fn drop(tile: *const GraphTile);
fn id(self: &GraphTile) -> GraphId;
fn directededges(tile: &GraphTile) -> &[DirectedEdge];
fn directededge(self: &GraphTile, index: usize) -> Result<*const DirectedEdge>;
fn edgeinfo(tile: &GraphTile, de: &DirectedEdge) -> EdgeInfo;
fn nodes(tile: &GraphTile) -> &[NodeInfo];
fn node(self: &GraphTile, index: usize) -> Result<*const NodeInfo>;
fn transitions(tile: &GraphTile) -> &[NodeTransition];
fn transition(self: &GraphTile, index: u32) -> Result<*const NodeTransition>;
fn node_edges<'a>(tile: &'a GraphTile, node: &NodeInfo) -> &'a [DirectedEdge];
fn node_transitions<'a>(tile: &'a GraphTile, node: &NodeInfo) -> &'a [NodeTransition];
fn node_latlon(tile: &GraphTile, node: &NodeInfo) -> LatLon;
fn admininfo(tile: &GraphTile, index: u32) -> Result<AdminInfo>;
unsafe fn IsClosed(self: &GraphTile, de: *const DirectedEdge) -> bool;
unsafe fn GetSpeed(
self: &GraphTile,
de: *const DirectedEdge,
flow_mask: u8,
seconds: u64,
is_truck: bool,
flow_sources: *mut u8,
seconds_from_now: u64,
) -> u32;
fn live_speed(tile: &GraphTile, de: &DirectedEdge) -> u8;
#[namespace = "valhalla::midgard"]
type tar;
fn id(tile: &TrafficTile) -> GraphId;
fn last_update(tile: &TrafficTile) -> u64;
fn write_last_update(tile: &TrafficTile, unix_timestamp: u64);
fn spare(tile: &TrafficTile) -> u64;
fn write_spare(tile: &TrafficTile, spare: u64);
#[namespace = "valhalla::baldr"]
#[cxx_name = "Use"]
type EdgeUse;
#[namespace = "valhalla::baldr"]
type RoadClass;
#[namespace = "valhalla::baldr"]
type DirectedEdge = crate::DirectedEdge;
fn endnode(self: &DirectedEdge) -> GraphId;
fn opp_index(self: &DirectedEdge) -> u32;
#[cxx_name = "use"]
fn use_type(self: &DirectedEdge) -> EdgeUse;
#[cxx_name = "classification"]
fn road_class(self: &DirectedEdge) -> RoadClass;
fn length(self: &DirectedEdge) -> u32;
fn toll(self: &DirectedEdge) -> bool;
fn destonly(self: &DirectedEdge) -> bool;
fn tunnel(self: &DirectedEdge) -> bool;
fn bridge(self: &DirectedEdge) -> bool;
fn roundabout(self: &DirectedEdge) -> bool;
#[cxx_name = "ctry_crossing"]
fn crosses_country_border(self: &DirectedEdge) -> bool;
#[cxx_name = "forwardaccess"]
fn forwardaccess_u32(self: &DirectedEdge) -> u32;
#[cxx_name = "reverseaccess"]
fn reverseaccess_u32(self: &DirectedEdge) -> u32;
fn speed(self: &DirectedEdge) -> u32;
fn truck_speed(self: &DirectedEdge) -> u32;
fn free_flow_speed(self: &DirectedEdge) -> u32;
fn constrained_flow_speed(self: &DirectedEdge) -> u32;
fn is_shortcut(self: &DirectedEdge) -> bool;
fn leaves_tile(self: &DirectedEdge) -> bool;
#[namespace = "valhalla::baldr"]
type NodeInfo = crate::NodeInfo;
fn edge_index(self: &NodeInfo) -> u32;
fn edge_count(self: &NodeInfo) -> u32;
fn elevation(self: &NodeInfo) -> f32;
#[cxx_name = "access"]
fn access_u16(self: &NodeInfo) -> u16;
fn admin_index(self: &NodeInfo) -> u32;
fn timezone(self: &NodeInfo) -> u32;
fn density(self: &NodeInfo) -> u32;
fn transition_index(self: &NodeInfo) -> u32;
fn transition_count(self: &NodeInfo) -> u32;
fn from_id(id: u32, unix_timestamp: u64) -> Result<TimeZoneInfo>;
#[namespace = "valhalla::baldr"]
type NodeTransition = crate::NodeTransition;
fn endnode(self: &NodeTransition) -> GraphId;
#[cxx_name = "up"]
fn upward(self: &NodeTransition) -> bool;
fn encode_weekly_speeds(speeds: &[f32]) -> Result<String>;
fn decode_weekly_speeds(encoded: &str) -> Result<Vec<f32>>;
}
#[cfg(feature = "proto")]
unsafe extern "C++" {
include!("valhalla/src/costing.hpp");
#[namespace = "valhalla::sif"]
type DynamicCost;
#[cxx_name = "Allowed"]
unsafe fn NodeAllowed(self: &DynamicCost, node: *const NodeInfo) -> bool;
unsafe fn IsAccessible(self: &DynamicCost, edge: *const DirectedEdge) -> bool;
fn new_cost(costing: &[u8]) -> Result<SharedPtr<DynamicCost>>;
}
}
unsafe impl Send for ffi::TileSet {}
unsafe impl Sync for ffi::TileSet {}
#[cfg(feature = "proto")]
unsafe impl Send for ffi::DynamicCost {}
#[cfg(feature = "proto")]
unsafe impl Sync for ffi::DynamicCost {}
#[derive(Clone, Copy, Eq)]
#[repr(C)]
pub struct GraphId {
pub value: u64,
}
unsafe impl ExternType for GraphId {
type Id = cxx::type_id!("valhalla::baldr::GraphId");
type Kind = cxx::kind::Trivial;
}
impl Default for GraphId {
fn default() -> Self {
Self {
value: 0x3fffffffffff,
}
}
}
impl fmt::Debug for GraphId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GraphId")
.field("level", &self.level())
.field("tileid", &self.tileid())
.field("id", &self.id())
.finish()
}
}
impl fmt::Display for GraphId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}/{}/{}", self.level(), self.tileid(), self.id())
}
}
impl PartialEq for GraphId {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl Hash for GraphId {
fn hash<H: Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}
impl GraphId {
#[inline(always)]
pub fn new(value: u64) -> Self {
Self { value }
}
#[inline(always)]
pub fn from_parts(level: u32, tileid: u32, id: u32) -> Option<Self> {
ffi::from_parts(level, tileid, id).ok()
}
#[inline(always)]
pub fn level(&self) -> u32 {
self.value as u32 & 0x7
}
#[inline(always)]
pub fn tileid(&self) -> u32 {
((self.value & 0x1fffff8) >> 3) as u32
}
#[inline(always)]
pub fn tile(&self) -> GraphId {
Self::new(self.value & 0x1ffffff)
}
#[inline(always)]
pub fn id(&self) -> u32 {
((self.value & 0x3ffffe000000) >> 25) as u32
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Error(Box<str>);
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for Error {}
impl From<cxx::Exception> for Error {
fn from(err: cxx::Exception) -> Self {
Error(err.what().into())
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Access: u16 {
const AUTO = 1;
const PEDESTRIAN = 2;
const BICYCLE = 4;
const TRUCK = 8;
const EMERGENCY = 16;
const TAXI = 32;
const BUS = 64;
const HOV = 128;
const WHEELCHAIR = 256;
const MOPED = 512;
const MOTORCYCLE = 1024;
const ALL = 4095;
const VEHICULAR = Self::AUTO.bits() | Self::TRUCK.bits() | Self::MOPED.bits() | Self::MOTORCYCLE.bits()
| Self::TAXI.bits() | Self::BUS.bits() | Self::HOV.bits();
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SpeedSources: u8 {
const NO_FLOW = 0;
const FREE_FLOW = 1;
const CONSTRAINED_FLOW = 2;
const PREDICTED_FLOW = 4;
const CURRENT_FLOW = 8;
const ALL = Self::FREE_FLOW.bits() | Self::CONSTRAINED_FLOW.bits()
| Self::PREDICTED_FLOW.bits() | Self::CURRENT_FLOW.bits();
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct LatLon(pub f64, pub f64);
#[cfg(feature = "proto")]
impl From<LatLon> for proto::LatLng {
fn from(loc: LatLon) -> Self {
proto::LatLng {
has_lat: Some(proto::lat_lng::HasLat::Lat(loc.0)),
has_lng: Some(proto::lat_lng::HasLng::Lng(loc.1)),
}
}
}
#[cfg(feature = "proto")]
impl From<LatLon> for Option<proto::LatLng> {
fn from(loc: LatLon) -> Self {
Some(loc.into())
}
}
#[derive(Clone)]
pub struct GraphReader(cxx::SharedPtr<ffi::TileSet>);
impl GraphReader {
pub fn new(config: &Config) -> Result<Self, Error> {
Ok(Self(ffi::new_tileset(config.inner())?))
}
pub fn dataset_id(&self) -> u64 {
self.0.dataset_id()
}
pub fn tiles(&self) -> Vec<GraphId> {
self.0.tiles()
}
pub fn tiles_in_bbox(&self, min: LatLon, max: LatLon, level: GraphLevel) -> Vec<GraphId> {
self.0.tiles_in_bbox(
min.0 as f32,
min.1 as f32,
max.0 as f32,
max.1 as f32,
level,
)
}
#[deprecated(since = "0.6.9", note = "use `GraphReader::graph_tile()` instead")]
pub fn get_tile(&self, id: GraphId) -> Option<GraphTile> {
self.graph_tile(id)
}
#[deprecated(since = "0.6.11", note = "use `GraphReader::graph_tile()` instead")]
pub fn tile(&self, id: GraphId) -> Option<GraphTile> {
self.graph_tile(id)
}
pub fn graph_tile(&self, id: GraphId) -> Option<GraphTile> {
GraphTile::new(self.0.get_graph_tile(id))
}
pub fn traffic_tile(&self, id: GraphId) -> Option<ffi::TrafficTile> {
self.0.get_traffic_tile(id).ok()
}
}
pub struct GraphTile(*const ffi::GraphTile);
impl Clone for GraphTile {
fn clone(&self) -> Self {
Self(unsafe { ffi::clone(self.0) })
}
}
impl Drop for GraphTile {
fn drop(&mut self) {
unsafe { ffi::drop(self.0) };
}
}
impl GraphTile {
fn new(tile: *const ffi::GraphTile) -> Option<Self> {
if !tile.is_null() {
Some(Self(tile))
} else {
None
}
}
fn deref(&self) -> &ffi::GraphTile {
unsafe { &*self.0 }
}
#[inline(always)]
pub fn id(&self) -> GraphId {
self.deref().id()
}
#[inline(always)]
pub fn directededges(&self) -> &[ffi::DirectedEdge] {
ffi::directededges(self.deref())
}
pub fn directededge(&self, index: u32) -> Option<&ffi::DirectedEdge> {
match self.deref().directededge(index as usize) {
Ok(ptr) if !ptr.is_null() => Some(unsafe { &*ptr }),
_ => None,
}
}
#[inline(always)]
pub fn nodes(&self) -> &[ffi::NodeInfo] {
ffi::nodes(self.deref())
}
pub fn node(&self, index: u32) -> Option<&ffi::NodeInfo> {
match self.deref().node(index as usize) {
Ok(ptr) if !ptr.is_null() => Some(unsafe { &*ptr }),
_ => None,
}
}
#[deprecated(
since = "0.6.15",
note = "please use `GraphTile::node_transitions()` instead"
)]
pub fn transitions(&self) -> &[ffi::NodeTransition] {
ffi::transitions(self.deref())
}
#[deprecated(
since = "0.6.15",
note = "please use `GraphTile::node_transitions()` instead"
)]
pub fn transition(&self, index: u32) -> Option<&ffi::NodeTransition> {
match self.deref().transition(index) {
Ok(ptr) if !ptr.is_null() => Some(unsafe { &*ptr }),
_ => None,
}
}
#[inline(always)]
pub fn node_latlon(&self, node: &ffi::NodeInfo) -> LatLon {
debug_assert!(ref_within_slice(self.nodes(), node), "Wrong tile");
let latlon = ffi::node_latlon(self.deref(), node);
LatLon(latlon.lat, latlon.lon)
}
#[inline(always)]
pub fn node_edges<'a>(&'a self, node: &ffi::NodeInfo) -> &'a [ffi::DirectedEdge] {
debug_assert!(ref_within_slice(self.nodes(), node), "Wrong tile");
ffi::node_edges(self.deref(), node)
}
#[inline(always)]
pub fn node_transitions<'a>(&'a self, node: &ffi::NodeInfo) -> &'a [ffi::NodeTransition] {
debug_assert!(ref_within_slice(self.nodes(), node), "Wrong tile");
ffi::node_transitions(self.deref(), node)
}
pub fn admin_info(&self, index: u32) -> Option<ffi::AdminInfo> {
ffi::admininfo(self.deref(), index).ok()
}
#[inline(always)]
pub fn edgeinfo(&self, de: &ffi::DirectedEdge) -> ffi::EdgeInfo {
debug_assert!(ref_within_slice(self.directededges(), de), "Wrong tile");
ffi::edgeinfo(self.deref(), de)
}
#[inline(always)]
pub fn live_speed(&self, de: &ffi::DirectedEdge) -> Option<u32> {
debug_assert!(ref_within_slice(self.directededges(), de), "Wrong tile");
match ffi::live_speed(self.deref(), de) {
0 => Some(0), 255 => None, speed => Some(speed as u32),
}
}
#[inline(always)]
pub fn edge_closed(&self, de: &ffi::DirectedEdge) -> bool {
debug_assert!(ref_within_slice(self.directededges(), de), "Wrong tile");
unsafe { self.deref().IsClosed(de as *const ffi::DirectedEdge) }
}
pub fn edge_speed(
&self,
de: &ffi::DirectedEdge,
speed_sources: SpeedSources,
is_truck: bool,
second_of_week: u64,
seconds_from_now: u64,
) -> (u32, SpeedSources) {
debug_assert!(ref_within_slice(self.directededges(), de), "Wrong tile");
let mut flow_sources: u8 = 0;
let speed = unsafe {
self.deref().GetSpeed(
de as *const ffi::DirectedEdge,
speed_sources.bits(),
second_of_week,
is_truck,
&mut flow_sources,
seconds_from_now,
)
};
(speed, SpeedSources::from_bits_retain(flow_sources))
}
}
#[repr(C)]
pub struct DirectedEdge {
data: [u64; 6],
}
unsafe impl ExternType for DirectedEdge {
type Id = cxx::type_id!("valhalla::baldr::DirectedEdge");
type Kind = cxx::kind::Trivial;
}
impl DirectedEdge {
#[inline(always)]
pub fn forwardaccess(&self) -> Access {
Access::from_bits_retain(self.forwardaccess_u32() as u16)
}
#[inline(always)]
pub fn reverseaccess(&self) -> Access {
Access::from_bits_retain(self.reverseaccess_u32() as u16)
}
}
#[repr(C)]
pub struct NodeInfo {
data: [u64; 4],
}
unsafe impl ExternType for NodeInfo {
type Id = cxx::type_id!("valhalla::baldr::NodeInfo");
type Kind = cxx::kind::Trivial;
}
impl NodeInfo {
#[deprecated(
since = "0.6.10",
note = "please use `GraphTile::node_edges()` instead"
)]
pub fn edges(&self) -> std::ops::Range<usize> {
let start = self.edge_index() as usize;
let count = self.edge_count() as usize;
start..start + count
}
#[deprecated(
since = "0.6.10",
note = "please use `GraphTile::node_transitions()` instead"
)]
pub fn transitions(&self) -> std::ops::Range<usize> {
let start = self.transition_index() as usize;
let count = self.transition_count() as usize;
start..start + count
}
#[inline(always)]
pub fn access(&self) -> Access {
Access::from_bits_retain(self.access_u16())
}
}
#[repr(C)]
pub struct NodeTransition {
data: [u64; 1],
}
unsafe impl ExternType for NodeTransition {
type Id = cxx::type_id!("valhalla::baldr::NodeTransition");
type Kind = cxx::kind::Trivial;
}
impl TimeZoneInfo {
#[inline(always)]
pub fn from_id(id: u32, unix_timestamp: u64) -> Option<Self> {
ffi::from_id(id, unix_timestamp).ok()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LiveTraffic(u64);
impl LiveTraffic {
pub const UNKNOWN: Self = Self(0);
pub const CLOSED: Self = Self(255u64 << 28);
#[inline(always)]
pub const fn from_bits(value: u64) -> Self {
Self(value)
}
#[inline(always)]
pub const fn to_bits(&self) -> u64 {
self.0
}
#[inline(always)]
pub const fn from_uniform_speed(speed: u8) -> Self {
Self::from_segmented_speeds(speed, [speed, 0, 0], [255, 0])
}
#[inline(always)]
pub const fn from_segmented_speeds(
overall_speed: u8,
subsegment_speeds: [u8; 3],
breakpoints: [u8; 2],
) -> Self {
let overall_encoded = (overall_speed >> 1) as u64;
let speed1_encoded = (subsegment_speeds[0] >> 1) as u64;
let speed2_encoded = (subsegment_speeds[1] >> 1) as u64;
let speed3_encoded = (subsegment_speeds[2] >> 1) as u64;
let bp1 = breakpoints[0] as u64;
let bp2 = breakpoints[1] as u64;
Self(
overall_encoded | (speed1_encoded << 7) | (speed2_encoded << 14) | (speed3_encoded << 21) | (bp1 << 28) | (bp2 << 36), )
}
#[inline(always)]
pub const fn with_spare(self, spare: bool) -> Self {
let cleared = self.0 & !(1u64 << 63); let spare_bit = (spare as u64) << 63;
Self(cleared | spare_bit)
}
#[inline(always)]
pub const fn spare(&self) -> bool {
(self.0 >> 63) & 1 != 0
}
}
impl TrafficTile {
#[inline(always)]
pub fn id(&self) -> GraphId {
ffi::id(self)
}
#[inline(always)]
pub fn last_update(&self) -> u64 {
ffi::last_update(self)
}
#[inline(always)]
pub fn write_last_update(&self, unix_timestamp: u64) {
ffi::write_last_update(self, unix_timestamp)
}
#[inline(always)]
pub fn spare(&self) -> u64 {
ffi::spare(self)
}
#[inline(always)]
pub fn write_spare(&self, spare: u64) {
ffi::write_spare(self, spare)
}
#[inline(always)]
pub fn edge_count(&self) -> u32 {
self.edge_count
}
#[inline(always)]
pub fn edge_traffic(&self, edge_index: u32) -> Option<LiveTraffic> {
if edge_index < self.edge_count {
let data = unsafe { std::ptr::read_volatile(self.speeds.add(edge_index as usize)) };
Some(LiveTraffic(data))
} else {
None
}
}
#[inline(always)]
pub fn write_edge_traffic(&self, edge_index: u32, traffic: LiveTraffic) {
if edge_index < self.edge_count {
unsafe { std::ptr::write_volatile(self.speeds.add(edge_index as usize), traffic.0) };
}
}
pub fn clear_traffic(&self) {
for i in 0..self.edge_count as usize {
unsafe { std::ptr::write_volatile(self.speeds.add(i), 0u64) };
}
self.write_last_update(0);
}
}
#[cfg(feature = "proto")]
#[derive(Clone)]
pub struct CostingModel(cxx::SharedPtr<ffi::DynamicCost>);
#[cfg(feature = "proto")]
impl CostingModel {
pub fn new(costing_type: proto::costing::Type) -> Result<Self, Error> {
let costing = proto::Costing {
r#type: costing_type as i32,
..Default::default()
};
let buf = costing.encode_to_vec();
Ok(Self(ffi::new_cost(&buf)?))
}
pub fn with_options(costing: &proto::Costing) -> Result<Self, Error> {
let buf = costing.encode_to_vec();
Ok(Self(ffi::new_cost(&buf)?))
}
pub fn node_accessible(&self, node: &ffi::NodeInfo) -> bool {
unsafe { self.0.NodeAllowed(node as *const ffi::NodeInfo) }
}
pub fn edge_accessible(&self, edge: &ffi::DirectedEdge) -> bool {
unsafe { self.0.IsAccessible(edge as *const ffi::DirectedEdge) }
}
}
fn ref_within_slice<T>(slice: &[T], item: &T) -> bool {
let start = slice.as_ptr() as usize;
let item_pos = item as *const T as usize;
let byte_offset = item_pos.wrapping_sub(start);
byte_offset < std::mem::size_of_val(slice)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn graph_id() {
let id = GraphId::new(5411833275938);
assert_eq!(id.level(), 2);
assert_eq!(id.tileid(), 838852);
assert_eq!(id.id(), 161285);
assert_eq!(
GraphId::from_parts(id.level(), id.tileid(), id.id()),
Some(id)
);
assert_eq!(format!("{id}"), "2/838852/161285");
assert_eq!(
format!("{id:?}"),
"GraphId { level: 2, tileid: 838852, id: 161285 }"
);
let base = id.tile();
assert_eq!(base.level(), 2);
assert_eq!(base.tileid(), 838852);
assert_eq!(base.id(), 0);
assert_eq!(GraphId::from_parts(id.level(), id.tileid(), 0), Some(base));
let default_id = GraphId::default();
assert_eq!(default_id.level(), 7);
assert_eq!(default_id.tileid(), 4194303);
assert_eq!(default_id.id(), 2097151);
assert_eq!(GraphId::from_parts(8, id.tileid(), 0), None);
}
#[test]
fn test_ref_within_slice() {
let data = [10, 20, 30, 40, 50];
assert!(ref_within_slice(&data, &data[0]));
assert!(ref_within_slice(&data, &data[2]));
assert!(ref_within_slice(&data, &data[4]));
let outside = 30;
assert!(!ref_within_slice(&data, &outside));
let subslice = &data[1..4];
assert!(!ref_within_slice(subslice, &data[0]));
assert!(ref_within_slice(subslice, &data[1]));
assert!(ref_within_slice(subslice, &data[2]));
assert!(ref_within_slice(subslice, &data[3]));
assert!(!ref_within_slice(subslice, &data[4]));
}
#[test]
fn test_mock_live_traffic_tile() {
let mut header: [u64; 16] = [0; 16]; let mut speeds: [u64; 16] = [0; 16];
let tile = TrafficTile {
header: header.as_mut_ptr(),
speeds: speeds.as_mut_ptr(),
edge_count: 16,
traffic_tar: cxx::SharedPtr::null(),
};
assert_eq!(tile.last_update(), 0);
assert_eq!(tile.spare(), 0);
assert_eq!(tile.edge_count(), 16);
for i in 0..tile.edge_count() {
assert_eq!(tile.edge_traffic(i), Some(LiveTraffic::UNKNOWN));
}
tile.write_last_update(1234567890);
tile.write_spare(42);
for i in 0..tile.edge_count() {
tile.write_edge_traffic(i, LiveTraffic::from_uniform_speed(i as u8));
}
assert_eq!(tile.last_update(), 1234567890);
assert_eq!(tile.spare(), 42);
for i in 0..tile.edge_count() {
assert_eq!(
tile.edge_traffic(i),
Some(LiveTraffic::from_uniform_speed(i as u8))
);
}
for i in 0..tile.edge_count() {
assert_eq!(
speeds[i as usize],
LiveTraffic::from_uniform_speed(i as u8).to_bits()
);
}
}
}