#![no_std]
use core::{
net::{IpAddr, Ipv4Addr, Ipv6Addr},
num::{NonZeroUsize, TryFromIntError},
};
use num_enum::IntoPrimitive;
pub const LOG_BUF_CAPACITY: usize = 8192;
pub const LOG_FIELDS: usize = 6;
pub type LogValueLength = u16;
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, IntoPrimitive)]
pub enum Level {
Error = 1,
Warn,
Info,
Debug,
Trace,
}
macro_rules! impl_formatter_for_types {
($trait:path : { $($type:ty),*}) => {
$(
impl $trait for $type {}
)*
};
}
pub trait DefaultFormatter {}
impl_formatter_for_types!(
DefaultFormatter: {
bool,
i8, i16, i32, i64, isize,
u8, u16, u32, u64, usize,
f32, f64,
char,
str,
&str,
IpAddr, Ipv4Addr, Ipv6Addr
}
);
pub trait LowerHexFormatter {}
impl_formatter_for_types!(
LowerHexFormatter: {
i8, i16, i32, i64, isize,
u8, u16, u32, u64, usize,
&[u8]
}
);
pub trait UpperHexFormatter {}
impl_formatter_for_types!(
UpperHexFormatter: {
i8, i16, i32, i64, isize,
u8, u16, u32, u64, usize,
&[u8]
}
);
pub trait IpFormatter {}
impl IpFormatter for IpAddr {}
impl IpFormatter for Ipv4Addr {}
impl IpFormatter for Ipv6Addr {}
impl IpFormatter for u32 {}
impl IpFormatter for [u8; 4] {}
impl IpFormatter for [u8; 16] {}
impl IpFormatter for [u16; 8] {}
pub trait LowerMacFormatter {}
impl LowerMacFormatter for [u8; 6] {}
pub trait UpperMacFormatter {}
impl UpperMacFormatter for [u8; 6] {}
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
pub enum RecordField {
Target = 1,
Level,
Module,
File,
Line,
NumArgs,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, IntoPrimitive)]
pub enum Argument {
DisplayHint,
I8,
I16,
I32,
I64,
Isize,
U8,
U16,
U32,
U64,
Usize,
F32,
F64,
Ipv4Addr,
Ipv6Addr,
ArrU8Len4,
ArrU8Len6,
ArrU8Len16,
ArrU16Len8,
Bytes,
Str,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive)]
pub enum DisplayHint {
Default = 1,
LowerHex,
UpperHex,
Ip,
LowerMac,
UpperMac,
}
#[inline(always)]
pub(crate) fn write(tag: u8, value: &[u8], buf: &mut [u8]) -> Option<NonZeroUsize> {
let wire_len: LogValueLength = match value.len().try_into() {
Ok(wire_len) => Some(wire_len),
Err(TryFromIntError { .. }) => None,
}?;
let mut size = 0;
macro_rules! copy_from_slice {
($value:expr) => {{
let buf = buf.get_mut(size..)?;
let buf = buf.get_mut(..$value.len())?;
buf.copy_from_slice($value);
size += $value.len();
}};
}
copy_from_slice!(&[tag]);
copy_from_slice!(&wire_len.to_ne_bytes());
copy_from_slice!(value);
NonZeroUsize::new(size)
}
pub trait WriteToBuf {
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize>;
}
macro_rules! impl_write_to_buf {
($type:ident, $arg_type:expr) => {
impl WriteToBuf for $type {
#[inline(never)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write($arg_type.into(), &self.to_ne_bytes(), buf)
}
}
};
}
impl_write_to_buf!(i8, Argument::I8);
impl_write_to_buf!(i16, Argument::I16);
impl_write_to_buf!(i32, Argument::I32);
impl_write_to_buf!(i64, Argument::I64);
impl_write_to_buf!(isize, Argument::Isize);
impl_write_to_buf!(u8, Argument::U8);
impl_write_to_buf!(u16, Argument::U16);
impl_write_to_buf!(u32, Argument::U32);
impl_write_to_buf!(u64, Argument::U64);
impl_write_to_buf!(usize, Argument::Usize);
impl_write_to_buf!(f32, Argument::F32);
impl_write_to_buf!(f64, Argument::F64);
impl WriteToBuf for IpAddr {
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
match self {
IpAddr::V4(ipv4_addr) => write(Argument::Ipv4Addr.into(), &ipv4_addr.octets(), buf),
IpAddr::V6(ipv6_addr) => write(Argument::Ipv6Addr.into(), &ipv6_addr.octets(), buf),
}
}
}
impl WriteToBuf for Ipv4Addr {
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write(Argument::Ipv4Addr.into(), &self.octets(), buf)
}
}
impl WriteToBuf for [u8; 4] {
#[inline(never)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write(Argument::ArrU8Len4.into(), &self, buf)
}
}
impl WriteToBuf for Ipv6Addr {
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write(Argument::Ipv6Addr.into(), &self.octets(), buf)
}
}
impl WriteToBuf for [u8; 16] {
#[inline(never)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write(Argument::ArrU8Len16.into(), &self, buf)
}
}
impl WriteToBuf for [u16; 8] {
#[inline(never)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
let bytes = unsafe { core::mem::transmute::<[u16; 8], [u8; 16]>(self) };
write(Argument::ArrU16Len8.into(), &bytes, buf)
}
}
impl WriteToBuf for [u8; 6] {
#[inline(never)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write(Argument::ArrU8Len6.into(), &self, buf)
}
}
impl WriteToBuf for &[u8] {
#[inline(always)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write(Argument::Bytes.into(), self, buf)
}
}
impl WriteToBuf for &str {
#[inline(always)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
write(Argument::Str.into(), self.as_bytes(), buf)
}
}
impl WriteToBuf for DisplayHint {
#[inline(never)]
fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
let v: u8 = self.into();
write(Argument::DisplayHint.into(), &v.to_ne_bytes(), buf)
}
}
#[doc(hidden)]
#[inline(always)] pub fn write_record_header(
buf: &mut [u8],
target: &str,
level: Level,
module: &str,
file: &str,
line: u32,
num_args: usize,
) -> Option<NonZeroUsize> {
let level: u8 = level.into();
let mut size = 0;
macro_rules! write {
($tag:expr, $value:expr) => {{
let buf = buf.get_mut(size..)?;
let len = write($tag.into(), $value, buf)?;
size += len.get();
}};
}
write!(RecordField::Target, target.as_bytes());
write!(RecordField::Level, &level.to_ne_bytes());
write!(RecordField::Module, module.as_bytes());
write!(RecordField::File, file.as_bytes());
write!(RecordField::Line, &line.to_ne_bytes());
write!(RecordField::NumArgs, &num_args.to_ne_bytes());
NonZeroUsize::new(size)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn log_value_length_sufficient() {
assert!(
LOG_BUF_CAPACITY <= LogValueLength::MAX.into(),
"{} > {}",
LOG_BUF_CAPACITY,
LogValueLength::MAX
);
}
}