use std::fmt;
use std::io::IoSlice;
use std::io::Write;
use log::{kv, Record};
pub(crate) const BUFS_SIZE: usize = 11;
#[inline]
pub(crate) fn record<'b>(
bufs: &'b mut [IoSlice<'b>; BUFS_SIZE],
buf: &'b mut Buffer,
record: &'b Record,
debug: bool,
) -> &'b [IoSlice<'b>] {
#[cfg(feature = "timestamp")]
buf.write_ts();
buf.write_msg(record.args());
buf.write_key_values(record.key_values());
if debug {
buf.write_line(record.line().unwrap_or(0));
}
bufs[0] = IoSlice::new(buf.ts());
bufs[1] = IoSlice::new(record.level().as_str().as_bytes());
bufs[2] = IoSlice::new(buf.msg());
bufs[3] = IoSlice::new(b"\" target=\"");
bufs[4] = IoSlice::new(record.target().as_bytes());
bufs[5] = IoSlice::new(b"\" module=\"");
bufs[6] = IoSlice::new(record.module_path().unwrap_or("").as_bytes());
bufs[7] = IoSlice::new(buf.key_values());
let n = if debug {
bufs[8] = IoSlice::new(b" file=\"");
bufs[9] = IoSlice::new(record.file().unwrap_or("??").as_bytes());
bufs[10] = IoSlice::new(buf.line());
BUFS_SIZE
} else {
bufs[8] = IoSlice::new(b"\n");
BUFS_SIZE - 2
};
&bufs[..n]
}
const N_INDICES: usize = 3;
#[cfg(feature = "timestamp")]
const REUSABLE_PARTS: &[u8] = b"ts=\"0000-00-00T00:00:00.000000Z\" lvl=\"\" msg=\"";
#[cfg(not(feature = "timestamp"))]
const REUSABLE_PARTS: &[u8] = b"lvl=\"\" msg=\"";
#[cfg(feature = "timestamp")]
const TS_END_INDEX: usize = 38;
#[cfg(not(feature = "timestamp"))]
const TS_END_INDEX: usize = 5;
const MSG_START_INDEX: usize = TS_END_INDEX + 7;
pub(crate) struct Buffer {
buf: Vec<u8>,
indices: [usize; N_INDICES],
}
impl Buffer {
pub(crate) fn new() -> Buffer {
let mut buf = Vec::with_capacity(1024);
buf.extend_from_slice(REUSABLE_PARTS);
let indices = [0; N_INDICES];
Buffer { buf, indices }
}
#[inline]
#[cfg(feature = "timestamp")]
fn write_ts(&mut self) {
format_timestamp(&mut self.buf[..TS_END_INDEX]);
}
#[inline]
fn ts(&self) -> &[u8] {
&self.buf[..TS_END_INDEX]
}
#[inline]
fn write_msg(&mut self, args: &fmt::Arguments) {
self.buf.truncate(MSG_START_INDEX);
#[cfg(not(feature = "nightly"))]
write!(self.buf, "{}", args).unwrap_or_else(|_| unreachable!());
#[cfg(feature = "nightly")]
if let Some(msg) = args.as_str() {
self.buf.extend_from_slice(msg.as_bytes());
} else {
write!(self.buf, "{}", args).unwrap_or_else(|_| unreachable!());
}
self.indices[0] = self.buf.len();
}
#[inline]
fn msg(&self) -> &[u8] {
&self.buf[TS_END_INDEX..self.indices[0]]
}
#[inline]
fn write_key_values(&mut self, kvs: &dyn kv::Source) {
self.buf.extend_from_slice(b"\"");
let mut visitor = KeyValueVisitor(&mut self.buf);
kvs.visit(&mut visitor).unwrap_or_else(|_| unreachable!());
self.indices[1] = self.buf.len();
}
#[inline]
fn key_values(&self) -> &[u8] {
&self.buf[self.indices[0]..self.indices[1]]
}
#[inline]
fn write_line(&mut self, line: u32) {
self.buf.push(b':');
let mut itoa = itoa::Buffer::new();
self.buf.extend_from_slice(itoa.format(line).as_bytes());
self.buf.extend_from_slice(b"\"\n");
self.indices[2] = self.buf.len();
}
#[inline]
fn line(&self) -> &[u8] {
&self.buf[self.indices[1]..self.indices[2]]
}
}
#[inline]
#[cfg(feature = "timestamp")]
fn format_timestamp(buf: &mut [u8]) {
use std::mem::MaybeUninit;
use std::time::{Duration, SystemTime};
let now = SystemTime::now();
let diff = now
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_else(|_| Duration::new(0, 0));
let mut tm = MaybeUninit::uninit();
let secs_since_epoch = diff.as_secs() as i64;
let tm = unsafe { libc::gmtime_r(&secs_since_epoch, tm.as_mut_ptr()) };
let (year, month, day, hour, min, sec) = match unsafe { tm.as_ref() } {
Some(tm) => (
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
),
None => (0, 0, 0, 0, 0, 0),
};
let micros = diff.subsec_micros();
let mut itoa = itoa::Buffer::new();
buf[4..8].copy_from_slice(itoa.format(year).as_bytes());
zero_pad2(&mut buf[9..11], itoa.format(month).as_bytes());
zero_pad2(&mut buf[12..14], itoa.format(day).as_bytes());
zero_pad2(&mut buf[15..17], itoa.format(hour).as_bytes());
zero_pad2(&mut buf[18..20], itoa.format(min).as_bytes());
zero_pad2(&mut buf[21..23], itoa.format(sec).as_bytes());
zero_pad6(&mut buf[24..30], itoa.format(micros).as_bytes());
}
#[inline]
#[cfg(feature = "timestamp")]
fn zero_pad2(buf: &mut [u8], v: &[u8]) {
debug_assert_eq!(buf.len(), 2);
if v.len() == 1 {
buf[0] = b'0';
buf[1] = v[0];
} else {
buf[0] = v[0];
buf[1] = v[1];
}
}
#[inline]
#[cfg(feature = "timestamp")]
fn zero_pad6(buf: &mut [u8], v: &[u8]) {
debug_assert_eq!(buf.len(), 6);
let start = 6 - v.len();
for b in buf.iter_mut().take(start) {
*b = b'0';
}
buf[start..6].copy_from_slice(v);
}
struct KeyValueVisitor<'b>(&'b mut Vec<u8>);
impl<'b, 'kvs> kv::Visitor<'kvs> for KeyValueVisitor<'b> {
fn visit_pair(&mut self, key: kv::Key<'kvs>, value: kv::Value<'kvs>) -> Result<(), kv::Error> {
self.0.push(b' ');
self.0.extend_from_slice(key.as_str().as_bytes());
self.0.push(b'=');
if let Some(value) = value.to_borrowed_str() {
self.0.push(b'\"');
self.0.extend_from_slice(value.as_bytes());
self.0.push(b'\"');
} else if let Some(value) = value.to_u64() {
let mut itoa = itoa::Buffer::new();
self.0.extend_from_slice(itoa.format(value).as_bytes());
} else if let Some(value) = value.to_i64() {
let mut itoa = itoa::Buffer::new();
self.0.extend_from_slice(itoa.format(value).as_bytes());
} else if let Some(value) = value.to_f64() {
let mut ryu = ryu::Buffer::new();
self.0.extend_from_slice(ryu.format(value).as_bytes());
} else if let Some(value) = value.to_bool() {
if value {
self.0.extend_from_slice(b"true");
} else {
self.0.extend_from_slice(b"false");
}
} else if let Some(value) = value.to_char() {
self.0.push(b'\"');
let mut buf = [0; 4];
self.0
.extend_from_slice(value.encode_utf8(&mut buf).as_bytes());
self.0.push(b'\"');
} else {
self.0.push(b'\"');
write!(self.0, "{}", value).unwrap_or_else(|_| unreachable!());
self.0.push(b'\"');
}
Ok(())
}
}