blob: 70db8620a6bd3ea3a606765812f7cf5e764f041d [file]
// Copyright 2026 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#![no_std]
pub mod __private {
use core::fmt::Write;
pub struct StackBuffer<const N: usize> {
pub data: [u8; N],
pub len: usize,
}
impl<const N: usize> Write for StackBuffer<N> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
let bytes = s.as_bytes();
let space = N - self.len;
if bytes.len() > space {
return Err(core::fmt::Error);
}
self.data[self.len..self.len + bytes.len()].copy_from_slice(bytes);
self.len += bytes.len();
Ok(())
}
}
extern "C" {
fn pw_Log(
level: i32,
flags: u32,
module: *const u8,
file: *const u8,
line: i32,
func: *const u8,
fmt: *const u8,
...
);
}
/// Logs a message via `pw_Log`.
///
/// # Safety
///
/// The caller must ensure that `module`, `file`, and `msg` point to valid
/// memory for the duration of the call, and that `module` and `file` are
/// null-terminated.
#[no_mangle]
pub unsafe extern "C" fn rust_log(
level: u8,
module: *const u8,
file: *const u8,
line: i32,
msg: *const u8,
len: u32,
) {
// len is at most 128 bytes so will not overflow.
#[allow(clippy::cast_possible_wrap)]
pw_Log(
i32::from(level),
0,
module,
file,
line,
c"".as_ptr().cast::<u8>(),
c"%.*s".as_ptr().cast::<u8>(),
len as core::ffi::c_int,
msg,
);
}
}
// NOTE: This is not the canonical way to implement a backend. It creates an
// extra stack buffer and pulls in `core::fmt`. Ideally, we would have a
// backend similar to the printf backend, which generates the printf format
// string and calls `pw_Log` with a varargs.
#[macro_export]
macro_rules! pw_log_backend {
($log_level:expr, $format_string:literal $(, $args:expr)* $(,)?) => {{
use core::fmt::Write;
let mut buf = $crate::__private::StackBuffer::<128> { data: [0; 128], len: 0 };
if core::write!(&mut buf, $format_string $(, $args)*).is_ok() {
unsafe {
$crate::__private::rust_log(
$log_level as u8,
core::concat!(core::module_path!(), "\0").as_ptr(),
core::concat!(core::file!(), "\0").as_ptr(),
core::line!() as i32,
buf.data.as_ptr(),
buf.len as u32,
);
}
}
}};
}
#[macro_export]
macro_rules! pw_logf_backend {
($log_level:expr, $format_string:literal $(, $args:expr)* $(,)?) => {{
use core::fmt::Write;
let mut buf = $crate::__private::StackBuffer::<128> { data: [0; 128], len: 0 };
if core::write!(&mut buf, $format_string $(, $args)*).is_ok() {
unsafe {
$crate::__private::rust_log(
$log_level as u8,
core::concat!(core::module_path!(), "\0").as_ptr(),
core::concat!(core::file!(), "\0").as_ptr(),
core::line!() as i32,
buf.data.as_ptr(),
buf.len as u32,
);
}
}
}};
}