1#![cfg_attr(not(test), no_std)]
24#![warn(missing_docs)]
25
26#[cfg(feature = "std")]
27extern crate std;
28
29use core::{cmp, mem, ptr, fmt};
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33#[repr(i32)]
34pub enum LogPriority {
35 UNKNOWN = 0,
37 DEFAULT = 1,
39 VERBOSE = 2,
41 DEBUG = 3,
43 INFO = 4,
45 WARN = 5,
49 ERROR = 6,
53 FATAL = 7,
57 SILENT = 8,
59}
60
61const TAG_MAX_LEN: usize = 23;
62const BUFFER_CAPACITY: usize = 4000;
66const DEFAULT_TAG: &str = "Rust";
67
68#[cfg(not(test))]
69#[link(name = "log")]
70extern "C" {
71 fn __android_log_write(prio: i32, tag: *const i8, text: *const i8) -> i32;
72}
73
74#[cfg(test)]
75fn __android_log_write(_: i32, _: *const i8, _: *const i8) -> i32 {
76 0
77}
78
79pub struct Writer {
84 tag: mem::MaybeUninit<[u8; TAG_MAX_LEN + 1]>,
86 prio: LogPriority,
87 buffer: mem::MaybeUninit<[u8; BUFFER_CAPACITY + 1]>,
89 len: usize,
90}
91
92impl Writer {
93 #[inline(always)]
94 pub const fn new_default(prio: LogPriority) -> Self {
98 let mut tag = [0u8; TAG_MAX_LEN + 1];
99
100 tag[0] = DEFAULT_TAG.as_bytes()[0];
101 tag[1] = DEFAULT_TAG.as_bytes()[1];
102 tag[2] = DEFAULT_TAG.as_bytes()[2];
103 tag[3] = DEFAULT_TAG.as_bytes()[3];
104 unsafe {
105 Self::from_raw_parts(mem::MaybeUninit::new(tag), prio)
106 }
107 }
108
109 #[inline]
110 pub fn new(tag: &str, prio: LogPriority) -> Self {
115 let mut tag_buffer = mem::MaybeUninit::<[u8; TAG_MAX_LEN + 1]>::zeroed();
116 unsafe {
117 ptr::copy_nonoverlapping(tag.as_ptr(), tag_buffer.as_mut_ptr() as *mut u8, cmp::min(tag.len(), TAG_MAX_LEN));
118 Self::from_raw_parts(tag_buffer, prio)
119 }
120 }
121
122 #[inline]
123 pub const unsafe fn from_raw_parts(tag: mem::MaybeUninit<[u8; TAG_MAX_LEN + 1]>, prio: LogPriority) -> Self {
128 Self {
129 tag,
130 prio,
131 buffer: mem::MaybeUninit::uninit(),
132 len: 0,
133 }
134 }
135
136 #[inline(always)]
137 pub fn buffer(&self) -> &[u8] {
139 unsafe {
140 core::slice::from_raw_parts(self.buffer.as_ptr() as *const u8, self.len)
141 }
142 }
143
144 #[inline(always)]
145 fn as_mut_ptr(&mut self) -> *mut u8 {
146 self.buffer.as_mut_ptr() as _
147 }
148
149 #[inline(always)]
150 pub fn flush(&mut self) {
155 if self.len > 0 {
156 self.inner_flush();
157 }
158 }
159
160 fn inner_flush(&mut self) {
161 unsafe {
162 (self.buffer.as_mut_ptr() as *mut u8).add(self.len).write(0);
163 __android_log_write(self.prio as _, self.tag.as_ptr() as _, self.buffer.as_ptr() as *const _);
164 }
165 self.len = 0;
166 }
167
168 #[inline]
169 fn copy_data<'a>(&mut self, text: &'a [u8]) -> &'a [u8] {
170 let mut write_len = cmp::min(BUFFER_CAPACITY.saturating_sub(self.len), text.len());
171
172 #[inline(always)]
173 fn is_char_boundary(text: &[u8], idx: usize) -> bool {
174 if idx == 0 {
175 return true;
176 }
177
178 match text.get(idx) {
179 None => idx == text.len(),
180 Some(&byte) => (byte as i8) >= -0x40
181 }
182 }
183
184 #[inline(never)]
185 #[cold]
186 fn shift_by_char_boundary(text: &[u8], mut size: usize) -> usize {
187 while !is_char_boundary(text, size) {
188 size -= 1;
189 }
190 size
191 }
192
193 if !is_char_boundary(text, write_len) {
194 write_len = shift_by_char_boundary(text, write_len - 1);
196 }
197
198 unsafe {
199 ptr::copy_nonoverlapping(text.as_ptr(), self.as_mut_ptr().add(self.len), write_len);
200 }
201 self.len += write_len;
202 &text[write_len..]
203 }
204
205 pub fn write_data(&mut self, mut data: &[u8]) {
210 loop {
211 data = self.copy_data(data);
212
213 if data.is_empty() {
214 break;
215 } else {
216 self.flush();
217 }
218 }
219 }
220}
221
222impl fmt::Write for Writer {
223 #[inline]
224 fn write_str(&mut self, text: &str) -> fmt::Result {
225 self.write_data(text.as_bytes());
226
227 Ok(())
228 }
229}
230
231#[cfg(feature = "std")]
232impl std::io::Write for Writer {
233 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
234 self.write_data(buf);
235 Ok(buf.len())
236 }
237
238 #[inline(always)]
239 fn flush(&mut self) -> std::io::Result<()> {
240 self.flush();
241 Ok(())
242 }
243}
244
245impl Drop for Writer {
246 #[inline]
247 fn drop(&mut self) {
248 self.flush();
249 }
250}
251
252#[macro_export]
253macro_rules! println {
255 () => {{
256 $crate::println!(" ");
257 }};
258 ($($arg:tt)*) => {{
259 use core::fmt::Write;
260 let mut writer = $crate::Writer::new_default($crate::LogPriority::INFO);
261 let _ = write!(writer, $($arg)*);
262 drop(writer);
263 }}
264}
265
266#[macro_export]
267macro_rules! eprintln {
269 () => {{
270 $crate::println!(" ");
271 }};
272 ($($arg:tt)*) => {{
273 use core::fmt::Write;
274 let mut writer = $crate::Writer::new_default($crate::LogPriority::ERROR);
275 let _ = write!(writer, $($arg)*);
276 drop(writer);
277 }}
278}
279
280#[cfg(test)]
281mod tests {
282 use super::{LogPriority, Writer, TAG_MAX_LEN, DEFAULT_TAG};
283 const TAG: &str = "Test";
284 const TAG_OVERFLOW: &str = "123456789123456789123456789";
285
286 #[test]
287 fn should_truncate_tag() {
288 let writer = Writer::new(TAG_OVERFLOW, LogPriority::WARN);
289 assert!(TAG_OVERFLOW.len() > TAG_MAX_LEN);
290 let tag = unsafe { core::slice::from_raw_parts(writer.tag.as_ptr() as *const u8, TAG_MAX_LEN) };
291 assert_eq!(tag, TAG_OVERFLOW[..TAG_MAX_LEN].as_bytes());
292 }
293
294 #[test]
295 fn should_normal_write() {
296 let mut writer = Writer::new_default(LogPriority::WARN);
297
298 let tag = unsafe { core::slice::from_raw_parts(writer.tag.as_ptr() as *const u8, DEFAULT_TAG.len()) };
299 assert_eq!(tag, DEFAULT_TAG.as_bytes());
300 assert_eq!(writer.prio, LogPriority::WARN);
301
302 let data = TAG_OVERFLOW.as_bytes();
303
304 writer.write_data(data);
305 assert_eq!(writer.len, data.len());
306 assert_eq!(writer.buffer(), data);
307
308 writer.write_data(b" ");
309 writer.write_data(data);
310 let expected = format!("{} {}", TAG_OVERFLOW, TAG_OVERFLOW);
311 assert_eq!(writer.len, expected.len());
312 assert_eq!(writer.buffer(), expected.as_bytes());
313 }
314
315 #[test]
316 fn should_handle_write_overflow() {
317 let mut writer = Writer::new(TAG, LogPriority::WARN);
318 let data = TAG_OVERFLOW.as_bytes();
319 assert_eq!(unsafe { core::slice::from_raw_parts(writer.tag.as_ptr() as *const u8, TAG.len() + 1) }, &b"Test\0"[..]);
320
321 for idx in 1..=148 {
323 writer.write_data(data);
324 assert_eq!(writer.len, data.len() * idx);
325 }
326
327 writer.write_data(data);
328 assert_eq!(writer.len, 23);
329 }
330
331 #[test]
332 fn should_handle_write_overflow_outside_of_char_boundary() {
333 let mut writer = Writer::new(TAG, LogPriority::WARN);
334 let data = b"1234567891";
335
336 for idx in 1..400 {
337 writer.write_data(data);
338 assert_eq!(writer.len, data.len() * idx);
339 }
340
341 assert_eq!(3990, writer.len);
342
343 writer.write_data(b"12345678");
344 assert_eq!(3998, writer.len);
345
346 let unicode = "ロリ";
347 writer.write_data(unicode.as_bytes());
348 assert_eq!(writer.len, unicode.len());
349 assert_eq!(writer.buffer(), unicode.as_bytes());
350 }
351
352}