use crate::cursor::Cursor;
use crate::error::Result;
use crate::reader::ByteBuf;
use std::ops::Deref;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct EntryOwned {
pub(crate) file_id: [u8; 16],
pub(crate) entry_offset: u64,
seqnum: u64,
realtime_usec: u64,
monotonic_usec: u64,
boot_id: [u8; 16],
fields_in_order: Vec<(String, Vec<u8>)>,
}
#[derive(Debug, Clone)]
pub struct LiveEntry(Arc<EntryRef>);
impl LiveEntry {
pub(crate) fn new(entry: EntryRef) -> Self {
Self(Arc::new(entry))
}
pub fn into_owned(self) -> EntryOwned {
self.0.as_ref().to_owned()
}
}
impl Deref for LiveEntry {
type Target = EntryRef;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl AsRef<EntryRef> for LiveEntry {
fn as_ref(&self) -> &EntryRef {
self.0.as_ref()
}
}
impl EntryOwned {
pub(crate) fn new(
file_id: [u8; 16],
entry_offset: u64,
seqnum: u64,
realtime_usec: u64,
monotonic_usec: u64,
boot_id: [u8; 16],
fields_in_order: Vec<(String, Vec<u8>)>,
) -> Self {
Self {
file_id,
entry_offset,
seqnum,
realtime_usec,
monotonic_usec,
boot_id,
fields_in_order,
}
}
pub fn cursor(&self) -> Result<Cursor> {
Ok(Cursor::new_entry_key(
self.file_id,
self.entry_offset,
self.seqnum,
self.realtime_usec,
))
}
pub fn get(&self, field: &str) -> Option<&[u8]> {
self.fields_in_order
.iter()
.find(|(k, _)| k == field)
.map(|(_, v)| v.as_slice())
}
pub fn iter_fields(&self) -> impl Iterator<Item = (&str, &[u8])> {
self.fields_in_order
.iter()
.map(|(k, v)| (k.as_str(), v.as_slice()))
}
pub fn realtime_usec(&self) -> u64 {
self.realtime_usec
}
pub fn monotonic_usec(&self) -> u64 {
self.monotonic_usec
}
pub fn boot_id(&self) -> [u8; 16] {
self.boot_id
}
pub fn seqnum(&self) -> u64 {
self.seqnum
}
}
#[derive(Debug, Clone)]
struct FieldRef {
name: String,
payload: ByteBuf,
eq_pos: usize,
}
impl FieldRef {
fn value(&self) -> &[u8] {
let payload = self.payload.as_slice();
payload.get(self.eq_pos.saturating_add(1)..).unwrap_or(&[])
}
}
#[derive(Debug, Clone)]
pub struct EntryRef {
file_id: [u8; 16],
entry_offset: u64,
seqnum: u64,
realtime_usec: u64,
monotonic_usec: u64,
boot_id: [u8; 16],
fields_in_order: Vec<FieldRef>,
}
impl EntryRef {
pub(crate) fn new_parsed(
file_id: [u8; 16],
entry_offset: u64,
seqnum: u64,
realtime_usec: u64,
monotonic_usec: u64,
boot_id: [u8; 16],
fields_in_order: Vec<(String, ByteBuf, usize)>,
) -> Self {
let fields_in_order = fields_in_order
.into_iter()
.map(|(name, payload, eq_pos)| FieldRef {
name,
payload,
eq_pos,
})
.collect();
Self {
file_id,
entry_offset,
seqnum,
realtime_usec,
monotonic_usec,
boot_id,
fields_in_order,
}
}
pub fn to_owned(&self) -> EntryOwned {
let mut fields = Vec::with_capacity(self.fields_in_order.len());
for f in &self.fields_in_order {
fields.push((f.name.clone(), f.value().to_vec()));
}
EntryOwned::new(
self.file_id,
self.entry_offset,
self.seqnum,
self.realtime_usec,
self.monotonic_usec,
self.boot_id,
fields,
)
}
pub fn cursor(&self) -> Result<Cursor> {
Ok(Cursor::new_entry_key(
self.file_id,
self.entry_offset,
self.seqnum,
self.realtime_usec,
))
}
pub fn get(&self, field: &str) -> Option<&[u8]> {
self.fields_in_order
.iter()
.find(|f| f.name == field)
.map(|f| f.value())
}
pub fn iter_fields(&self) -> impl Iterator<Item = (&str, &[u8])> {
self.fields_in_order
.iter()
.map(|f| (f.name.as_str(), f.value()))
}
pub fn realtime_usec(&self) -> u64 {
self.realtime_usec
}
pub fn monotonic_usec(&self) -> u64 {
self.monotonic_usec
}
pub fn boot_id(&self) -> [u8; 16] {
self.boot_id
}
pub fn seqnum(&self) -> u64 {
self.seqnum
}
pub(crate) fn entry_offset_raw(&self) -> u64 {
self.entry_offset
}
pub(crate) fn file_id_raw(&self) -> [u8; 16] {
self.file_id
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn entry_owned_get_prefers_first_duplicate_and_iter_preserves_order() {
let entry = EntryOwned::new(
[0x11; 16],
7,
9,
11,
13,
[0x22; 16],
vec![
("MESSAGE".to_string(), b"first".to_vec()),
("PRIORITY".to_string(), b"6".to_vec()),
("MESSAGE".to_string(), b"second".to_vec()),
],
);
assert_eq!(entry.get("MESSAGE"), Some(&b"first"[..]));
let fields: Vec<(&str, &[u8])> = entry.iter_fields().collect();
assert_eq!(fields.len(), 3);
assert_eq!(fields[0], ("MESSAGE", &b"first"[..]));
assert_eq!(fields[1], ("PRIORITY", &b"6"[..]));
assert_eq!(fields[2], ("MESSAGE", &b"second"[..]));
assert_eq!(entry.realtime_usec(), 11);
assert_eq!(entry.monotonic_usec(), 13);
assert_eq!(entry.seqnum(), 9);
assert_eq!(entry.boot_id(), [0x22; 16]);
}
#[test]
fn entry_ref_to_owned_preserves_visible_fields_and_cursor() {
let entry = EntryRef::new_parsed(
[0x33; 16],
17,
19,
23,
29,
[0x44; 16],
vec![
(
"MESSAGE".to_string(),
ByteBuf::from_vec(b"MESSAGE=hello".to_vec()),
7,
),
(
"PRIORITY".to_string(),
ByteBuf::from_vec(b"PRIORITY=5".to_vec()),
8,
),
],
);
let owned = entry.to_owned();
assert_eq!(entry.get("MESSAGE"), Some(&b"hello"[..]));
assert_eq!(owned.get("MESSAGE"), Some(&b"hello"[..]));
assert_eq!(owned.get("PRIORITY"), Some(&b"5"[..]));
assert_eq!(
entry.cursor().unwrap().to_string(),
owned.cursor().unwrap().to_string()
);
assert_eq!(owned.realtime_usec(), 23);
assert_eq!(owned.monotonic_usec(), 29);
assert_eq!(owned.seqnum(), 19);
assert_eq!(owned.boot_id(), [0x44; 16]);
}
}