1#![no_std]
2
3use core::{
4 net::{IpAddr, Ipv4Addr, Ipv6Addr},
5 num::{NonZeroUsize, TryFromIntError},
6};
7
8use num_enum::IntoPrimitive;
9
10pub const LOG_BUF_CAPACITY: usize = 8192;
11
12pub const LOG_FIELDS: usize = 6;
13
14pub type LogValueLength = u16;
15
16#[repr(u8)]
17#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, IntoPrimitive)]
18pub enum Level {
19 Error = 1,
23 Warn,
27 Info,
31 Debug,
35 Trace,
39}
40
41macro_rules! impl_formatter_for_types {
42 ($trait:path : { $($type:ty),*}) => {
43 $(
44 impl $trait for $type {}
45 )*
46 };
47}
48
49pub trait DefaultFormatter {}
50impl_formatter_for_types!(
51 DefaultFormatter: {
52 bool,
53 i8, i16, i32, i64, isize,
54 u8, u16, u32, u64, usize,
55 f32, f64,
56 char,
57 str,
58 &str,
59 IpAddr, Ipv4Addr, Ipv6Addr
60 }
61);
62
63pub trait LowerHexFormatter {}
64impl_formatter_for_types!(
65 LowerHexFormatter: {
66 i8, i16, i32, i64, isize,
67 u8, u16, u32, u64, usize,
68 &[u8]
69 }
70);
71
72pub trait UpperHexFormatter {}
73impl_formatter_for_types!(
74 UpperHexFormatter: {
75 i8, i16, i32, i64, isize,
76 u8, u16, u32, u64, usize,
77 &[u8]
78 }
79);
80
81pub trait IpFormatter {}
82impl IpFormatter for IpAddr {}
83impl IpFormatter for Ipv4Addr {}
84impl IpFormatter for Ipv6Addr {}
85impl IpFormatter for u32 {}
86impl IpFormatter for [u8; 4] {}
87impl IpFormatter for [u8; 16] {}
88impl IpFormatter for [u16; 8] {}
89
90pub trait LowerMacFormatter {}
91impl LowerMacFormatter for [u8; 6] {}
92
93pub trait UpperMacFormatter {}
94impl UpperMacFormatter for [u8; 6] {}
95
96#[repr(u8)]
97#[derive(Copy, Clone, Debug, IntoPrimitive)]
98pub enum RecordField {
99 Target = 1,
100 Level,
101 Module,
102 File,
103 Line,
104 NumArgs,
105}
106
107#[repr(u8)]
110#[derive(Copy, Clone, Debug, IntoPrimitive)]
111pub enum Argument {
112 DisplayHint,
113
114 I8,
115 I16,
116 I32,
117 I64,
118 Isize,
119
120 U8,
121 U16,
122 U32,
123 U64,
124 Usize,
125
126 F32,
127 F64,
128
129 Ipv4Addr,
130 Ipv6Addr,
131
132 ArrU8Len4,
134 ArrU8Len6,
136 ArrU8Len16,
138 ArrU16Len8,
140
141 Bytes,
142 Str,
143}
144
145#[repr(u8)]
147#[derive(Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive)]
148pub enum DisplayHint {
149 Default = 1,
151 LowerHex,
153 UpperHex,
155 Ip,
157 LowerMac,
159 UpperMac,
161}
162
163#[inline(always)]
167pub(crate) fn write(tag: u8, value: &[u8], buf: &mut [u8]) -> Option<NonZeroUsize> {
168 let wire_len: LogValueLength = match value.len().try_into() {
169 Ok(wire_len) => Some(wire_len),
170 Err(TryFromIntError { .. }) => None,
171 }?;
172 let mut size = 0;
173 macro_rules! copy_from_slice {
174 ($value:expr) => {{
175 let buf = buf.get_mut(size..)?;
176 let buf = buf.get_mut(..$value.len())?;
177 buf.copy_from_slice($value);
178 size += $value.len();
179 }};
180 }
181 copy_from_slice!(&[tag]);
182 copy_from_slice!(&wire_len.to_ne_bytes());
183 copy_from_slice!(value);
184 NonZeroUsize::new(size)
185}
186
187pub trait WriteToBuf {
188 fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize>;
189}
190
191macro_rules! impl_write_to_buf {
192 ($type:ident, $arg_type:expr) => {
193 impl WriteToBuf for $type {
194 #[inline(never)]
197 fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
198 write($arg_type.into(), &self.to_ne_bytes(), buf)
199 }
200 }
201 };
202}
203
204impl_write_to_buf!(i8, Argument::I8);
205impl_write_to_buf!(i16, Argument::I16);
206impl_write_to_buf!(i32, Argument::I32);
207impl_write_to_buf!(i64, Argument::I64);
208impl_write_to_buf!(isize, Argument::Isize);
209
210impl_write_to_buf!(u8, Argument::U8);
211impl_write_to_buf!(u16, Argument::U16);
212impl_write_to_buf!(u32, Argument::U32);
213impl_write_to_buf!(u64, Argument::U64);
214impl_write_to_buf!(usize, Argument::Usize);
215
216impl_write_to_buf!(f32, Argument::F32);
217impl_write_to_buf!(f64, Argument::F64);
218
219impl WriteToBuf for IpAddr {
220 fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
221 match self {
222 IpAddr::V4(ipv4_addr) => write(Argument::Ipv4Addr.into(), &ipv4_addr.octets(), buf),
223 IpAddr::V6(ipv6_addr) => write(Argument::Ipv6Addr.into(), &ipv6_addr.octets(), buf),
224 }
225 }
226}
227
228impl WriteToBuf for Ipv4Addr {
229 fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
230 write(Argument::Ipv4Addr.into(), &self.octets(), buf)
231 }
232}
233
234impl WriteToBuf for [u8; 4] {
235 #[inline(never)]
238 fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
239 write(Argument::ArrU8Len4.into(), &self, buf)
240 }
241}
242
243impl WriteToBuf for Ipv6Addr {
244 fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
245 write(Argument::Ipv6Addr.into(), &self.octets(), buf)
246 }
247}
248
249impl WriteToBuf for [u8; 16] {
250 #[inline(never)]
253 fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
254 write(Argument::ArrU8Len16.into(), &self, buf)
255 }
256}
257
258impl WriteToBuf for [u16; 8] {
259 #[inline(never)]
262 fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
263 let bytes = unsafe { core::mem::transmute::<[u16; 8], [u8; 16]>(self) };
264 write(Argument::ArrU16Len8.into(), &bytes, buf)
265 }
266}
267
268impl WriteToBuf for [u8; 6] {
269 #[inline(never)]
272 fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
273 write(Argument::ArrU8Len6.into(), &self, buf)
274 }
275}
276
277impl WriteToBuf for &[u8] {
278 #[inline(always)]
282 fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
283 write(Argument::Bytes.into(), self, buf)
284 }
285}
286
287impl WriteToBuf for &str {
288 #[inline(always)]
292 fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
293 write(Argument::Str.into(), self.as_bytes(), buf)
294 }
295}
296
297impl WriteToBuf for DisplayHint {
298 #[inline(never)]
301 fn write(self, buf: &mut [u8]) -> Option<NonZeroUsize> {
302 let v: u8 = self.into();
303 write(Argument::DisplayHint.into(), &v.to_ne_bytes(), buf)
304 }
305}
306
307#[doc(hidden)]
308#[inline(always)] pub fn write_record_header(
310 buf: &mut [u8],
311 target: &str,
312 level: Level,
313 module: &str,
314 file: &str,
315 line: u32,
316 num_args: usize,
317) -> Option<NonZeroUsize> {
318 let level: u8 = level.into();
319 let mut size = 0;
320 macro_rules! write {
321 ($tag:expr, $value:expr) => {{
322 let buf = buf.get_mut(size..)?;
323 let len = write($tag.into(), $value, buf)?;
324 size += len.get();
325 }};
326 }
327 write!(RecordField::Target, target.as_bytes());
328 write!(RecordField::Level, &level.to_ne_bytes());
329 write!(RecordField::Module, module.as_bytes());
330 write!(RecordField::File, file.as_bytes());
331 write!(RecordField::Line, &line.to_ne_bytes());
332 write!(RecordField::NumArgs, &num_args.to_ne_bytes());
333 NonZeroUsize::new(size)
334}
335
336#[cfg(test)]
337mod test {
338 use super::*;
339
340 #[test]
341 fn log_value_length_sufficient() {
342 assert!(
343 LOG_BUF_CAPACITY <= LogValueLength::MAX.into(),
344 "{} > {}",
345 LOG_BUF_CAPACITY,
346 LogValueLength::MAX
347 );
348 }
349}