1use core::ops::{Deref, DerefMut};
7use core::sync::atomic::{AtomicUsize, Ordering};
8
9use super::config::GLOBAL_LOG_BUFFER_SIZE;
10use super::entry::LogEntry;
11
12const LOG_ENTRY_SIZE: usize = core::mem::size_of::<LogEntry>();
14
15pub(crate) const MAX_LOG_ENTRIES: usize = GLOBAL_LOG_BUFFER_SIZE / LOG_ENTRY_SIZE;
17
18fn calculate_formatted_length(entry: &LogEntry) -> usize {
42 let color_start_len = entry.level().color_code().len();
44 let color_reset_len = entry.level().reset_color_code().len();
45
46 let level_len = entry.level().as_str().len();
48
49 let timestamp_len = 14;
51
52 let cpu_id = entry.cpu_id();
54 let cpu_digits = if cpu_id == 0 {
55 1
56 } else {
57 let mut n = cpu_id;
59 let mut digits = 0;
60 while n > 0 {
61 digits += 1;
62 n /= 10;
63 }
64 digits
65 };
66
67 let task_id = entry.task_id();
69 let task_digits = if task_id == 0 {
70 3 } else {
72 let mut n = task_id;
73 let mut digits = 0;
74 while n > 0 {
75 digits += 1;
76 n /= 10;
77 }
78 if digits < 3 {
79 3 } else {
81 digits
82 }
83 };
84
85 let context_len = 5 + cpu_digits + 2 + task_digits + 1;
88
89 let message_len = entry.message().len();
91
92 let separators_len = 3;
94
95 color_start_len
96 + level_len
97 + timestamp_len
98 + context_len
99 + message_len
100 + color_reset_len
101 + separators_len
102}
103
104#[repr(C, align(64))]
110struct CachePadded64<T> {
111 inner: T,
112}
113
114impl<T> Deref for CachePadded64<T> {
115 type Target = T;
116
117 #[inline]
118 fn deref(&self) -> &Self::Target {
119 &self.inner
120 }
121}
122
123impl<T> DerefMut for CachePadded64<T> {
124 #[inline]
125 fn deref_mut(&mut self) -> &mut Self::Target {
126 &mut self.inner
127 }
128}
129
130static GLOBAL_LOG_BUFFER: GlobalLogBuffer = GlobalLogBuffer::new();
153
154#[repr(C)]
160pub(super) struct GlobalLogBuffer {
161 writer_data: CachePadded64<WriterData>,
163 reader_data: CachePadded64<ReaderData>,
165 buffer: [LogEntry; MAX_LOG_ENTRIES],
167 unread_bytes: AtomicUsize,
169}
170
171#[repr(C)]
173struct WriterData {
174 write_seq: AtomicUsize,
176}
177
178#[repr(C)]
180struct ReaderData {
181 read_seq: AtomicUsize,
183 dropped: AtomicUsize,
185}
186
187impl GlobalLogBuffer {
188 pub(super) const fn new() -> Self {
190 const EMPTY: LogEntry = LogEntry::empty();
191 Self {
192 writer_data: CachePadded64 {
193 inner: WriterData {
194 write_seq: AtomicUsize::new(1),
195 },
196 },
197 reader_data: CachePadded64 {
198 inner: ReaderData {
199 read_seq: AtomicUsize::new(1),
200 dropped: AtomicUsize::new(0),
201 },
202 },
203 buffer: [EMPTY; MAX_LOG_ENTRIES],
204 unread_bytes: AtomicUsize::new(0),
205 }
206 }
207
208 pub(super) fn write(&self, entry: &LogEntry) {
218 let seq = self.writer_data.write_seq.fetch_add(1, Ordering::Relaxed);
220
221 let slot = seq % MAX_LOG_ENTRIES;
223 let slot_ptr = unsafe { self.buffer.as_ptr().add(slot) as *mut LogEntry };
224
225 self.handle_overwrite(seq);
227
228 unsafe {
230 entry.copy_data_to(slot_ptr);
231 }
232
233 unsafe {
235 entry.publish(slot_ptr, seq);
236 }
237
238 let formatted_len = calculate_formatted_length(entry);
240 self.unread_bytes
241 .fetch_add(formatted_len, Ordering::Release);
242 }
243
244 fn handle_overwrite(&self, current_seq: usize) {
252 let read_seq = self.reader_data.read_seq.load(Ordering::Acquire);
253 if current_seq < read_seq + MAX_LOG_ENTRIES {
254 return;
255 }
256 let new_read_seq = current_seq - MAX_LOG_ENTRIES + 1;
257 let overwritten = new_read_seq.saturating_sub(read_seq);
258 self.reader_data
259 .dropped
260 .fetch_add(overwritten, Ordering::Relaxed);
261
262 let mut current_read_seq = read_seq;
264 while current_read_seq < new_read_seq {
265 match self.reader_data.read_seq.compare_exchange_weak(
266 current_read_seq,
267 new_read_seq,
268 Ordering::Release,
269 Ordering::Relaxed,
270 ) {
271 Ok(_) => break,
272 Err(seen_seq) => {
273 if seen_seq >= new_read_seq {
274 break;
275 }
276 current_read_seq = seen_seq;
277 }
278 }
279 }
280 }
281
282 pub(super) fn read(&self) -> Option<LogEntry> {
288 let read_seq = self.reader_data.read_seq.load(Ordering::Acquire);
289
290 let slot = read_seq % MAX_LOG_ENTRIES;
291 let slot_ptr = unsafe { self.buffer.as_ptr().add(slot) as *const LogEntry };
292
293 const EMPTY: LogEntry = LogEntry::empty();
294 unsafe {
295 if !EMPTY.is_ready(slot_ptr, read_seq) {
296 return None;
297 }
298 }
299
300 let entry_data = unsafe { (*slot_ptr).clone() };
301
302 let formatted_len = calculate_formatted_length(&entry_data);
304 self.unread_bytes
305 .fetch_sub(formatted_len, Ordering::Release);
306
307 self.reader_data
308 .read_seq
309 .store(read_seq + 1, Ordering::Release);
310
311 Some(entry_data)
312 }
313
314 pub(super) fn len(&self) -> usize {
316 let write = self.writer_data.write_seq.load(Ordering::Relaxed);
317 let read = self.reader_data.read_seq.load(Ordering::Relaxed);
318 write.saturating_sub(read)
319 }
320
321 pub(super) fn unread_bytes(&self) -> usize {
323 self.unread_bytes.load(Ordering::Acquire)
324 }
325
326 pub(super) fn dropped_count(&self) -> usize {
328 self.reader_data.dropped.load(Ordering::Relaxed)
329 }
330
331 pub(super) fn peek(&self, index: usize) -> Option<LogEntry> {
346 let current_write = self.writer_data.write_seq.load(Ordering::Acquire);
347 let current_read = self.reader_data.read_seq.load(Ordering::Acquire);
348
349 if index < current_read || index >= current_write {
352 return None;
353 }
354
355 if current_write >= current_read + MAX_LOG_ENTRIES {
358 let oldest_valid = current_write.saturating_sub(MAX_LOG_ENTRIES);
360 if index < oldest_valid {
361 return None; }
363 }
364
365 let slot = index % MAX_LOG_ENTRIES;
367 let slot_ptr = unsafe { self.buffer.as_ptr().add(slot) as *const LogEntry };
368
369 const EMPTY: LogEntry = LogEntry::empty();
371 unsafe {
372 if !EMPTY.is_ready(slot_ptr, index) {
373 return None;
374 }
375 }
376
377 Some(unsafe { (*slot_ptr).clone() })
379 }
380
381 pub(super) fn reader_index(&self) -> usize {
383 self.reader_data.read_seq.load(Ordering::Acquire)
384 }
385
386 pub(super) fn writer_index(&self) -> usize {
388 self.writer_data.write_seq.load(Ordering::Acquire)
389 }
390}
391
392#[inline]
394pub(super) fn write_log(entry: &LogEntry) {
395 GLOBAL_LOG_BUFFER.write(entry);
396}
397
398#[inline]
402pub fn read_log() -> Option<LogEntry> {
403 GLOBAL_LOG_BUFFER.read()
404}
405
406#[inline]
408pub fn log_dropped_count() -> usize {
409 GLOBAL_LOG_BUFFER.dropped_count()
410}
411
412#[inline]
414pub fn log_len() -> usize {
415 GLOBAL_LOG_BUFFER.len()
416}
417
418#[inline]
420pub fn log_unread_bytes() -> usize {
421 GLOBAL_LOG_BUFFER.unread_bytes()
422}
423
424#[inline]
428pub fn peek_log(index: usize) -> Option<LogEntry> {
429 GLOBAL_LOG_BUFFER.peek(index)
430}
431
432#[inline]
434pub fn log_reader_index() -> usize {
435 GLOBAL_LOG_BUFFER.reader_index()
436}
437
438#[inline]
440pub fn log_writer_index() -> usize {
441 GLOBAL_LOG_BUFFER.writer_index()
442}