#![cfg_attr(not(feature = "std"), no_std)]
#![allow(unused_doc_comments)]
extern crate alloc;
extern crate core;
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "std")]
use std::io::Error;
use crate::config::{
DataEndpoint, DataType, STATIC_HEX_LENGTH, STATIC_STRING_LENGTH, get_endpoint_meta,
get_message_meta, max_data_type_id, max_endpoint_id,
};
use crate::macros::{ReprI32Enum, ReprU32Enum};
use alloc::string::ToString;
use alloc::sync::Arc;
use core::fmt::Formatter;
use core::mem::size_of;
use core::ops::Mul;
#[cfg(all(test, feature = "std"))]
mod tests;
#[cfg(feature = "python")]
#[cfg(feature = "std")]
mod python_api;
#[cfg(all(not(feature = "std"), target_os = "none"))]
unsafe extern "C" {
fn seds_error_msg(msg: *const u8, len: usize);
}
#[cfg(all(not(feature = "std"), target_os = "none"))]
mod embedded_alloc {
use core::alloc::{GlobalAlloc, Layout};
use core::mem::size_of;
unsafe extern "C" {
fn telemetryMalloc(size: usize) -> *mut core::ffi::c_void;
fn telemetryFree(ptr: *mut core::ffi::c_void);
fn telemetry_panic_hook(msg: *const u8, len: usize);
}
struct TelemetryAlloc;
#[inline]
fn align_up(addr: usize, align: usize) -> usize {
(addr + (align - 1)) & !(align - 1)
}
unsafe impl GlobalAlloc for TelemetryAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let align = layout.align().max(size_of::<usize>());
let header = size_of::<usize>();
let total = match layout
.size()
.checked_add(align)
.and_then(|v| v.checked_add(header))
{
Some(v) => v,
None => return core::ptr::null_mut(),
};
let raw = unsafe { telemetryMalloc(total) as *mut u8 };
if raw.is_null() {
return core::ptr::null_mut();
}
let base = raw as usize + header;
let aligned = align_up(base, align) as *mut u8;
unsafe {
let slot = (aligned as *mut usize).offset(-1);
*slot = raw as usize;
}
aligned
}
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
if ptr.is_null() {
return;
}
let raw = unsafe {
let slot = (ptr as *mut usize).offset(-1);
*slot as *mut core::ffi::c_void
};
unsafe { telemetryFree(raw) };
}
}
#[global_allocator]
static A: TelemetryAlloc = TelemetryAlloc;
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
let msg = b"rust panic";
unsafe {
telemetry_panic_hook(msg.as_ptr(), msg.len());
}
loop {}
}
}
mod c_api;
pub mod config;
#[cfg(feature = "cryptography")]
pub mod crypto;
pub mod diagnostics;
#[cfg(feature = "discovery")]
pub mod discovery;
mod lock;
mod macros;
pub mod packet;
mod queue;
pub mod relay;
pub mod router;
mod small_payload;
#[cfg(feature = "timesync")]
pub mod timesync;
pub mod wire_format;
pub const MAX_VALUE_DATA_ENDPOINT: u32 = 255;
pub const MAX_VALUE_DATA_TYPE: u32 = 4095;
pub const MAX_VALUE_ROUTE_SELECTION_MODE: i32 = 2;
impl crate::macros::ReprU32Enum for DataType {
const MAX: u32 = MAX_VALUE_DATA_TYPE;
#[inline]
fn from_u32(x: u32) -> Option<Self> {
DataType::try_from_u32(x)
}
}
impl crate::macros::ReprU32Enum for DataEndpoint {
const MAX: u32 = MAX_VALUE_DATA_ENDPOINT;
#[inline]
fn from_u32(x: u32) -> Option<Self> {
DataEndpoint::try_from_u32(x)
}
}
#[inline]
pub fn current_max_endpoint_id() -> u32 {
max_endpoint_id()
}
#[inline]
pub fn current_max_data_type_id() -> u32 {
max_data_type_id()
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum RouteSelectionMode {
Fanout = 0,
Weighted = 1,
Failover = 2,
}
impl_repr_i32_enum!(
RouteSelectionMode,
RouteSelectionMode::Fanout as i32,
RouteSelectionMode::Failover as i32
);
#[inline]
const fn parse_usize(s: &str) -> usize {
let bytes = s.as_bytes();
let mut i = 0;
let mut val = 0;
while i < bytes.len() {
let c = bytes[i];
if c < b'0' || c > b'9' {
panic!("Invalid digit");
}
val = val * 10 + (c - b'0') as usize;
i += 1;
}
val
}
#[inline]
pub const fn parse_f64(s: &str) -> f64 {
let bytes = s.as_bytes();
let mut i = 0;
if bytes.is_empty() {
panic!("empty string");
}
let mut sign = 1.0;
if bytes[i] == b'-' {
sign = -1.0;
i += 1;
} else if bytes[i] == b'+' {
i += 1;
}
let mut int_part: f64 = 0.0;
let mut has_digits = false;
while i < bytes.len() && bytes[i] >= b'0' && bytes[i] <= b'9' {
int_part = int_part * 10.0 + (bytes[i] - b'0') as f64;
i += 1;
has_digits = true;
}
let mut frac_part: f64 = 0.0;
let mut scale: f64 = 1.0;
if i < bytes.len() && bytes[i] == b'.' {
i += 1;
while i < bytes.len() && bytes[i] >= b'0' && bytes[i] <= b'9' {
scale *= 10.0;
frac_part += (bytes[i] - b'0') as f64 / scale;
i += 1;
has_digits = true;
}
}
if !has_digits || i != bytes.len() {
panic!("invalid f64 literal");
}
sign * (int_part + frac_part)
}
#[inline(always)]
const fn parse_strings(s: &str) -> &str {
s
}
#[inline]
pub const fn parse_u8(s: &str) -> u8 {
let bytes = s.as_bytes();
let mut i = 0;
let mut val: u16 = 0;
if bytes.is_empty() {
panic!("empty string");
}
while i < bytes.len() {
let c = bytes[i];
if c < b'0' || c > b'9' {
panic!("invalid digit in u8");
}
val = val * 10 + (c - b'0') as u16;
if val > 255 {
panic!("u8 overflow");
}
i += 1;
}
val as u8
}
#[inline]
pub const fn parse_u128(s: &str) -> u128 {
let bytes = s.as_bytes();
let mut i = 0;
let mut val: u128 = 0;
if bytes.is_empty() {
panic!("empty string");
}
while i < bytes.len() {
let c = bytes[i];
if c < b'0' || c > b'9' {
panic!("invalid digit in u128");
}
let digit = (c - b'0') as u128;
if val > (u128::MAX - digit) / 10 {
panic!("u128 overflow");
}
val = val * 10 + digit;
i += 1;
}
val
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct EndpointMeta {
name: &'static str,
description: &'static str,
link_local_only: bool,
}
impl EndpointMeta {
pub fn as_str(&self) -> &'static str {
self.name
}
#[inline]
pub fn description(&self) -> &'static str {
self.description
}
#[inline]
pub fn is_link_local_only(&self) -> bool {
self.link_local_only
}
}
impl DataEndpoint {
pub fn as_str(&self) -> &'static str {
get_endpoint_meta(*self).name
}
pub fn description(&self) -> &'static str {
get_endpoint_meta(*self).description
}
#[inline]
pub fn is_link_local_only(&self) -> bool {
get_endpoint_meta(*self).link_local_only
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum MessageElement {
Static(usize, MessageDataType, MessageClass),
Dynamic(MessageDataType, MessageClass),
}
impl MessageElement {
#[inline]
pub const fn data_type(&self) -> MessageDataType {
match self {
MessageElement::Static(_, dt, _) => *dt,
MessageElement::Dynamic(dt, _) => *dt,
}
}
#[inline]
pub const fn message_type(&self) -> MessageClass {
match self {
MessageElement::Static(_, _, mt) => *mt,
MessageElement::Dynamic(_, mt) => *mt,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum ReliableMode {
None,
Ordered,
Unordered,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum E2eEncryptionPolicy {
PreferOff,
PreferOn,
RequireOn,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct MessageMeta {
name: &'static str,
description: &'static str,
element: MessageElement,
endpoints: &'static [DataEndpoint],
reliable: ReliableMode,
priority: u8,
e2e_encryption: E2eEncryptionPolicy,
}
impl DataType {
pub fn as_str(&self) -> &'static str {
get_message_meta(*self).name
}
pub fn description(&self) -> &'static str {
get_message_meta(*self).description
}
}
#[inline]
pub fn message_meta(ty: DataType) -> MessageMeta {
get_message_meta(ty)
}
#[inline]
pub fn is_reliable_type(ty: DataType) -> bool {
!matches!(get_message_meta(ty).reliable, ReliableMode::None)
}
#[inline]
pub fn reliable_mode(ty: DataType) -> ReliableMode {
get_message_meta(ty).reliable
}
#[inline]
pub fn message_priority(ty: DataType) -> u8 {
get_message_meta(ty).priority
}
#[inline]
pub fn message_e2e_encryption_policy(ty: DataType) -> E2eEncryptionPolicy {
get_message_meta(ty).e2e_encryption
}
impl Mul<MessageElement> for usize {
type Output = usize;
#[inline]
fn mul(self, rhs: MessageElement) -> usize {
self * rhs.into()
}
}
impl Mul<usize> for MessageElement {
type Output = usize;
#[inline]
fn mul(self, rhs: usize) -> usize {
self.into() * rhs
}
}
impl MessageElement {
#[inline]
fn into(self) -> usize {
match self {
MessageElement::Static(a, _, _) => a,
_ => 0,
}
}
}
#[inline]
pub fn get_needed_message_size(ty: DataType) -> usize {
data_type_size(get_data_type(ty)) * get_message_meta(ty).element
}
#[inline]
pub fn get_info_type(ty: DataType) -> MessageClass {
get_message_meta(ty).element.message_type()
}
#[inline]
pub fn get_data_type(ty: DataType) -> MessageDataType {
get_message_meta(ty).element.data_type()
}
#[inline]
pub fn get_message_name(ty: DataType) -> &'static str {
get_message_meta(ty).name
}
#[inline]
pub fn endpoints_from_datatype(ty: DataType) -> &'static [DataEndpoint] {
get_message_meta(ty).endpoints
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum MessageDataType {
Float64,
Float32,
UInt8,
UInt16,
UInt32,
UInt64,
UInt128,
Int8,
Int16,
Int32,
Int64,
Int128,
Bool,
String,
Binary,
NoData,
}
#[inline]
pub const fn data_type_size(dt: MessageDataType) -> usize {
match dt {
MessageDataType::Float64 => size_of::<f64>(),
MessageDataType::Float32 => size_of::<f32>(),
MessageDataType::UInt8 => size_of::<u8>(),
MessageDataType::UInt16 => size_of::<u16>(),
MessageDataType::UInt32 => size_of::<u32>(),
MessageDataType::UInt64 => size_of::<u64>(),
MessageDataType::UInt128 => size_of::<u128>(),
MessageDataType::Int8 => size_of::<i8>(),
MessageDataType::Int16 => size_of::<i16>(),
MessageDataType::Int32 => size_of::<i32>(),
MessageDataType::Int64 => size_of::<i64>(),
MessageDataType::Int128 => size_of::<i128>(),
MessageDataType::Bool => size_of::<bool>(),
MessageDataType::String => STATIC_STRING_LENGTH,
MessageDataType::Binary => STATIC_HEX_LENGTH,
MessageDataType::NoData => 0,
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum MessageClass {
Data,
Error,
Warning,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TelemetryError {
GenericError(Option<Arc<str>>),
InvalidType,
SizeMismatch { expected: usize, got: usize },
SizeMismatchError,
EmptyEndpoints,
TimestampInvalid,
MissingPayload,
HandlerError(&'static str),
BadArg,
PermissionDenied,
Pack(&'static str),
Unpack(&'static str),
Io(&'static str),
InvalidUtf8,
TypeMismatch { expected: usize, got: usize },
InvalidLinkId(&'static str),
PacketTooLarge(&'static str),
}
impl TelemetryError {
pub const fn to_error_code(&self) -> TelemetryErrorCode {
match self {
TelemetryError::GenericError(_) => TelemetryErrorCode::GenericError,
TelemetryError::InvalidType => TelemetryErrorCode::InvalidType,
TelemetryError::SizeMismatch { .. } => TelemetryErrorCode::SizeMismatch,
TelemetryError::SizeMismatchError => TelemetryErrorCode::SizeMismatchError,
TelemetryError::EmptyEndpoints => TelemetryErrorCode::EmptyEndpoints,
TelemetryError::TimestampInvalid => TelemetryErrorCode::TimestampInvalid,
TelemetryError::MissingPayload => TelemetryErrorCode::MissingPayload,
TelemetryError::HandlerError(_) => TelemetryErrorCode::HandlerError,
TelemetryError::BadArg => TelemetryErrorCode::BadArg,
TelemetryError::PermissionDenied => TelemetryErrorCode::PermissionDenied,
TelemetryError::Pack(_) => TelemetryErrorCode::Pack,
TelemetryError::Unpack(_) => TelemetryErrorCode::Unpack,
TelemetryError::Io(_) => TelemetryErrorCode::Io,
TelemetryError::InvalidUtf8 => TelemetryErrorCode::InvalidUtf8,
TelemetryError::TypeMismatch { .. } => TelemetryErrorCode::TypeMismatch,
TelemetryError::InvalidLinkId(_) => TelemetryErrorCode::InvalidLinkId,
TelemetryError::PacketTooLarge(_) => TelemetryErrorCode::PacketTooLarge,
}
}
}
impl core::fmt::Display for TelemetryError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.write_str(&TelemetryError::to_string(self))
}
}
#[cfg(feature = "std")]
impl std::error::Error for TelemetryError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
#[cfg(feature = "std")]
impl From<Error> for TelemetryError {
fn from(error: Error) -> Self {
let str = error.to_string();
let astr: Arc<str> = Arc::from(str.as_str());
TelemetryError::GenericError(Some(astr))
}
}
#[cfg(feature = "std")]
impl From<Box<dyn std::error::Error>> for TelemetryError {
fn from(err: Box<dyn std::error::Error>) -> Self {
let str = err.to_string();
let astr: Arc<str> = Arc::from(str.as_str());
TelemetryError::GenericError(Some(astr))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum TelemetryErrorCode {
GenericError = -2,
InvalidType = -3,
SizeMismatch = -4,
SizeMismatchError = -5,
EmptyEndpoints = -6,
TimestampInvalid = -7,
MissingPayload = -8,
HandlerError = -9,
BadArg = -10,
PermissionDenied = -11,
Pack = -12,
Unpack = -13,
Io = -14,
InvalidUtf8 = -15,
TypeMismatch = -16,
InvalidLinkId = -17,
PacketTooLarge = -18,
}
impl_repr_i32_enum!(
TelemetryErrorCode,
TelemetryErrorCode::MAX,
TelemetryErrorCode::MIN
);
impl TelemetryErrorCode {
pub const MAX: i32 = TelemetryErrorCode::InvalidType as i32;
pub const MIN: i32 = TelemetryErrorCode::PacketTooLarge as i32;
#[inline]
pub fn as_str(&self) -> &'static str {
match self {
TelemetryErrorCode::GenericError => "GenericError",
TelemetryErrorCode::InvalidType => "{Invalid Type}",
TelemetryErrorCode::SizeMismatch => "{Size Mismatch}",
TelemetryErrorCode::SizeMismatchError => "{Size Mismatch Error}",
TelemetryErrorCode::EmptyEndpoints => "{Empty Endpoints}",
TelemetryErrorCode::TimestampInvalid => "{Timestamp Invalid}",
TelemetryErrorCode::MissingPayload => "{Missing Payload}",
TelemetryErrorCode::HandlerError => "{Handler Error}",
TelemetryErrorCode::BadArg => "{Bad Arg}",
TelemetryErrorCode::PermissionDenied => "{Permission Denied}",
TelemetryErrorCode::Pack => "{Pack Error}",
TelemetryErrorCode::Unpack => "{Unpack Error}",
TelemetryErrorCode::Io => "{IO Error}",
TelemetryErrorCode::InvalidUtf8 => "{Invalid UTF-8}",
TelemetryErrorCode::TypeMismatch => "{Type Mismatch}",
TelemetryErrorCode::InvalidLinkId => "{Invalid Link ID}",
TelemetryErrorCode::PacketTooLarge => "{Packet Too Large}",
}
}
#[inline]
pub fn try_from_i32(x: i32) -> Option<Self> {
try_enum_from_i32(x)
}
}
pub type TelemetryResult<T> = Result<T, TelemetryError>;
#[inline]
pub fn try_enum_from_u32<E: ReprU32Enum>(x: u32) -> Option<E> {
if x > E::MAX {
return None;
}
E::from_u32(x)
}
#[inline]
pub fn try_enum_from_i32<E: ReprI32Enum>(x: i32) -> Option<E> {
if x < E::MIN || x > E::MAX {
return None;
}
let e = unsafe { (&x as *const i32 as *const E).read() };
Some(e)
}