diagnostics_log_encoding/
encode.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Encoding diagnostic records using the Fuchsia Tracing format.
6
7use crate::{constants, ArgType, Argument, Header, Metatag, RawSeverity, Record, Value};
8use fidl_fuchsia_diagnostics::Severity;
9use std::array::TryFromSliceError;
10use std::borrow::{Borrow, Cow};
11use std::fmt::Debug;
12use std::io::Cursor;
13use std::ops::Deref;
14use thiserror::Error;
15
16/// An `Encoder` wraps any value implementing `MutableBuffer` and writes diagnostic stream records
17/// into it.
18pub struct Encoder<B> {
19    pub(crate) buf: B,
20    /// Encoder options
21    options: EncoderOpts,
22}
23
24/// Options for the encoder
25#[derive(Default)]
26pub struct EncoderOpts {
27    /// Whether or not to always log the line/file information
28    /// Defaults to false. If false, the line/file information
29    /// will only be logged for ERROR and above.
30    pub always_log_file_line: bool,
31}
32
33/// Parameters for `Encoder/write_event`.
34pub struct WriteEventParams<'a, E, T, MS> {
35    /// The event to write as a record.
36    pub event: E,
37    /// Tags associated with the log event.
38    pub tags: &'a [T],
39    /// Metatags associated with the log event.
40    pub metatags: MS,
41    /// The process that emitted the log.
42    pub pid: zx::Koid,
43    /// The thread that emitted the log.
44    pub tid: zx::Koid,
45    /// Number of events that were dropped before this one.
46    pub dropped: u64,
47}
48
49impl<B> Encoder<B>
50where
51    B: MutableBuffer,
52{
53    /// Create a new `Encoder` from the provided buffer.
54    pub fn new(buf: B, options: EncoderOpts) -> Self {
55        Self { buf, options }
56    }
57
58    /// Returns a reference to the underlying buffer being used for encoding.
59    pub fn inner(&self) -> &B {
60        &self.buf
61    }
62
63    /// Returns a reference to the underlying buffer being used for encoding.
64    pub fn take(self) -> B {
65        self.buf
66    }
67
68    /// Writes an event to to the buffer as a record.
69    ///
70    /// Fails if there is insufficient space in the buffer for encoding.
71    pub fn write_event<'a, E, MS, T>(
72        &mut self,
73        params: WriteEventParams<'a, E, T, MS>,
74    ) -> Result<(), EncodingError>
75    where
76        E: RecordEvent,
77        MS: Iterator<Item = &'a Metatag>,
78        T: AsRef<str>,
79    {
80        let WriteEventParams { event, tags, metatags, pid, tid, dropped } = params;
81        let severity = event.raw_severity();
82        self.write_inner(event.timestamp(), severity, |this| {
83            this.write_raw_argument(constants::PID, pid.raw_koid())?;
84            this.write_raw_argument(constants::TID, tid.raw_koid())?;
85            if dropped > 0 {
86                this.write_raw_argument(constants::NUM_DROPPED, dropped)?;
87            }
88            if this.options.always_log_file_line || severity >= Severity::Error.into_primitive() {
89                // If the severity is ERROR or higher, we add the file and line information.
90                if let Some(mut file) = event.file() {
91                    let split = file.split("../");
92                    file = split.last().unwrap();
93                    this.write_raw_argument(constants::FILE, Value::Text(Cow::Borrowed(file)))?;
94                }
95
96                if let Some(line) = event.line() {
97                    this.write_raw_argument(constants::LINE, line as u64)?;
98                }
99            }
100
101            // Write the metatags as tags (if any were given)
102            for metatag in metatags {
103                match metatag {
104                    Metatag::Target => this.write_raw_argument(constants::TAG, event.target())?,
105                }
106            }
107
108            event.write_arguments(this)?;
109
110            for tag in tags {
111                this.write_raw_argument(constants::TAG, tag.as_ref())?;
112            }
113            Ok(())
114        })?;
115        Ok(())
116    }
117
118    /// Writes a Record to the buffer.
119    pub fn write_record<R>(&mut self, record: R) -> Result<(), EncodingError>
120    where
121        R: RecordFields,
122    {
123        self.write_inner(record.timestamp(), record.raw_severity(), |this| {
124            record.write_arguments(this)
125        })
126    }
127
128    fn write_inner<F>(
129        &mut self,
130        timestamp: zx::BootInstant,
131        severity: RawSeverity,
132        write_args: F,
133    ) -> Result<(), EncodingError>
134    where
135        F: FnOnce(&mut Self) -> Result<(), EncodingError>,
136    {
137        // TODO(https://fxbug.dev/42138121): on failure, zero out the region we were using
138        let starting_idx = self.buf.cursor();
139        // Prepare the header, we'll finish writing once we know the full size of the record.
140        let header_slot = self.buf.put_slot(std::mem::size_of::<u64>())?;
141        self.write_i64(timestamp.into_nanos())?;
142
143        write_args(self)?;
144
145        let mut header = Header(0);
146        header.set_type(crate::TRACING_FORMAT_LOG_RECORD_TYPE);
147        header.set_severity(severity);
148
149        let length = self.buf.cursor() - starting_idx;
150        header.set_len(length);
151
152        assert_eq!(length % 8, 0, "all records must be written 8-byte aligned");
153        self.buf.fill_slot(header_slot, &header.0.to_le_bytes());
154        Ok(())
155    }
156
157    /// Writes an argument with this encoder with the given name and value.
158    pub fn write_raw_argument(
159        &mut self,
160        name: &str,
161        value: impl WriteArgumentValue<B>,
162    ) -> Result<(), EncodingError> {
163        self.inner_write_argument(move |header, encoder| {
164            encoder.write_argument_name(header, name)?;
165            value.write_value(header, encoder)?;
166            Ok(())
167        })
168    }
169
170    /// Writes an argument with this encoder.
171    pub fn write_argument<'a>(
172        &mut self,
173        argument: impl Borrow<Argument<'a>>,
174    ) -> Result<(), EncodingError> {
175        let argument = argument.borrow();
176        self.inner_write_argument(move |header, encoder| {
177            encoder.write_argument_name(header, argument.name())?;
178            argument.write_value(header, encoder)?;
179            Ok(())
180        })
181    }
182
183    fn write_argument_name(
184        &mut self,
185        header: &mut Header,
186        name: &str,
187    ) -> Result<(), EncodingError> {
188        self.write_string(name)?;
189        header.set_name_ref(string_mask(name));
190        Ok(())
191    }
192
193    fn inner_write_argument(
194        &mut self,
195        cb: impl FnOnce(&mut Header, &mut Self) -> Result<(), EncodingError>,
196    ) -> Result<(), EncodingError> {
197        let starting_idx = self.buf.cursor();
198        let header_slot = self.buf.put_slot(std::mem::size_of::<Header>())?;
199
200        let mut header = Header(0);
201        cb(&mut header, self)?;
202
203        let record_len = self.buf.cursor() - starting_idx;
204        assert_eq!(record_len % 8, 0, "arguments must be 8-byte aligned");
205
206        header.set_size_words((record_len / 8) as u16);
207        self.buf.fill_slot(header_slot, &header.0.to_le_bytes());
208
209        Ok(())
210    }
211
212    /// Write an unsigned integer.
213    fn write_u64(&mut self, n: u64) -> Result<(), EncodingError> {
214        self.buf.put_u64_le(n).map_err(|_| EncodingError::BufferTooSmall)
215    }
216
217    /// Write a signed integer.
218    fn write_i64(&mut self, n: i64) -> Result<(), EncodingError> {
219        self.buf.put_i64_le(n).map_err(|_| EncodingError::BufferTooSmall)
220    }
221
222    /// Write a floating-point number.
223    fn write_f64(&mut self, n: f64) -> Result<(), EncodingError> {
224        self.buf.put_f64(n).map_err(|_| EncodingError::BufferTooSmall)
225    }
226
227    /// Write a string padded to 8-byte alignment.
228    fn write_string(&mut self, src: &str) -> Result<(), EncodingError> {
229        self.write_bytes(src.as_bytes())
230    }
231
232    /// Write bytes padded to 8-byte alignment.
233    #[doc(hidden)]
234    pub fn write_bytes(&mut self, src: &[u8]) -> Result<(), EncodingError> {
235        self.buf.put_slice(src).map_err(|_| EncodingError::BufferTooSmall)?;
236        unsafe {
237            let align = std::mem::size_of::<u64>();
238            let num_padding_bytes = (align - src.len() % align) % align;
239            // TODO(https://fxbug.dev/42138122) need to enforce that the buffer is zeroed
240            self.buf.advance_cursor(num_padding_bytes);
241        }
242        Ok(())
243    }
244}
245
246mod private {
247    use super::*;
248
249    pub trait Sealed {}
250    impl Sealed for Value<'_> {}
251    impl Sealed for Argument<'_> {}
252    impl Sealed for u64 {}
253    impl Sealed for f64 {}
254    impl Sealed for i64 {}
255    impl Sealed for bool {}
256    impl Sealed for String {}
257    impl Sealed for &str {}
258    impl Sealed for Cow<'_, str> {}
259}
260
261/// Trait implemented by types which can be written to the encoder.
262pub trait WriteArgumentValue<B>: private::Sealed {
263    /// Writes the value of the argument.
264    fn write_value(
265        &self,
266        header: &mut Header,
267        encoder: &mut Encoder<B>,
268    ) -> Result<(), EncodingError>;
269}
270
271impl<B: MutableBuffer> WriteArgumentValue<B> for Argument<'_> {
272    fn write_value(
273        &self,
274        header: &mut Header,
275        encoder: &mut Encoder<B>,
276    ) -> Result<(), EncodingError> {
277        match self {
278            Self::Pid(value) | Self::Tid(value) => value.raw_koid().write_value(header, encoder),
279            Self::Line(value) | Self::Dropped(value) => value.write_value(header, encoder),
280            Self::Tag(value) | Self::File(value) | Self::Message(value) => {
281                value.write_value(header, encoder)
282            }
283            Self::Other { value, .. } => value.write_value(header, encoder),
284        }
285    }
286}
287
288impl<B: MutableBuffer> WriteArgumentValue<B> for i64 {
289    fn write_value(
290        &self,
291        header: &mut Header,
292        encoder: &mut Encoder<B>,
293    ) -> Result<(), EncodingError> {
294        header.set_type(ArgType::I64 as u8);
295        encoder.write_i64(*self)
296    }
297}
298
299impl<B: MutableBuffer> WriteArgumentValue<B> for u64 {
300    fn write_value(
301        &self,
302        header: &mut Header,
303        encoder: &mut Encoder<B>,
304    ) -> Result<(), EncodingError> {
305        header.set_type(ArgType::U64 as u8);
306        encoder.write_u64(*self)
307    }
308}
309
310impl<B: MutableBuffer> WriteArgumentValue<B> for f64 {
311    fn write_value(
312        &self,
313        header: &mut Header,
314        encoder: &mut Encoder<B>,
315    ) -> Result<(), EncodingError> {
316        header.set_type(ArgType::F64 as u8);
317        encoder.write_f64(*self)
318    }
319}
320
321impl<B: MutableBuffer> WriteArgumentValue<B> for bool {
322    fn write_value(
323        &self,
324        header: &mut Header,
325        _encoder: &mut Encoder<B>,
326    ) -> Result<(), EncodingError> {
327        header.set_type(ArgType::Bool as u8);
328        header.set_bool_val(*self);
329        Ok(())
330    }
331}
332
333impl<B: MutableBuffer> WriteArgumentValue<B> for &str {
334    fn write_value(
335        &self,
336        header: &mut Header,
337        encoder: &mut Encoder<B>,
338    ) -> Result<(), EncodingError> {
339        header.set_type(ArgType::String as u8);
340        header.set_value_ref(string_mask(self));
341        encoder.write_string(self)
342    }
343}
344
345impl<B: MutableBuffer> WriteArgumentValue<B> for String {
346    fn write_value(
347        &self,
348        header: &mut Header,
349        encoder: &mut Encoder<B>,
350    ) -> Result<(), EncodingError> {
351        self.as_str().write_value(header, encoder)
352    }
353}
354
355impl<B: MutableBuffer> WriteArgumentValue<B> for Cow<'_, str> {
356    fn write_value(
357        &self,
358        header: &mut Header,
359        encoder: &mut Encoder<B>,
360    ) -> Result<(), EncodingError> {
361        self.as_ref().write_value(header, encoder)
362    }
363}
364
365impl<B: MutableBuffer> WriteArgumentValue<B> for Value<'_> {
366    fn write_value(
367        &self,
368        header: &mut Header,
369        encoder: &mut Encoder<B>,
370    ) -> Result<(), EncodingError> {
371        match self {
372            Value::SignedInt(s) => s.write_value(header, encoder),
373            Value::UnsignedInt(u) => u.write_value(header, encoder),
374            Value::Floating(f) => f.write_value(header, encoder),
375            Value::Text(t) => t.write_value(header, encoder),
376            Value::Boolean(b) => b.write_value(header, encoder),
377        }
378    }
379}
380
381fn string_mask(s: &str) -> u16 {
382    let len = s.len();
383    if len == 0 {
384        return 0;
385    }
386    (len as u16) | (1 << 15)
387}
388
389/// Trait implemented by types which can be written by the Encoder.
390pub trait RecordEvent {
391    /// Returns the record severity.
392    fn raw_severity(&self) -> RawSeverity;
393    /// Returns the name of the file where the record was emitted.
394    fn file(&self) -> Option<&str>;
395    /// Returns the number of the line in the file where the record was emitted.
396    fn line(&self) -> Option<u32>;
397    /// Returns the target of the record.
398    fn target(&self) -> &str;
399    /// Consumes this type and writes all the arguments.
400    fn write_arguments<B: MutableBuffer>(
401        self,
402        writer: &mut Encoder<B>,
403    ) -> Result<(), EncodingError>;
404    /// Returns the timestamp associated to this record.
405    fn timestamp(&self) -> zx::BootInstant;
406}
407
408/// Trait implemented by complete Records.
409pub trait RecordFields {
410    /// Returns the record severity.
411    fn raw_severity(&self) -> RawSeverity;
412
413    /// Returns the timestamp associated to this record.
414    fn timestamp(&self) -> zx::BootInstant;
415
416    /// Consumes this type and writes all the arguments.
417    fn write_arguments<B: MutableBuffer>(
418        self,
419        writer: &mut Encoder<B>,
420    ) -> Result<(), EncodingError>;
421}
422
423/// Arguments to create a record for testing purposes.
424pub struct TestRecord<'a> {
425    /// Severity of the log
426    pub severity: RawSeverity,
427    /// Timestamp of the test record.
428    pub timestamp: zx::BootInstant,
429    /// File that emitted the log.
430    pub file: Option<&'a str>,
431    /// Line in the file that emitted the log.
432    pub line: Option<u32>,
433    /// Additional record arguments.
434    pub record_arguments: Vec<Argument<'a>>,
435}
436
437impl TestRecord<'_> {
438    /// Creates a test record from a record.
439    pub fn from<'a>(file: &'a str, line: u32, record: &'a Record<'a>) -> TestRecord<'a> {
440        TestRecord {
441            severity: record.severity,
442            timestamp: record.timestamp,
443            file: Some(file),
444            line: Some(line),
445            record_arguments: record.arguments.clone(),
446        }
447    }
448}
449
450impl RecordEvent for TestRecord<'_> {
451    fn raw_severity(&self) -> RawSeverity {
452        self.severity
453    }
454
455    fn file(&self) -> Option<&str> {
456        self.file
457    }
458
459    fn line(&self) -> Option<u32> {
460        self.line
461    }
462
463    fn target(&self) -> &str {
464        unimplemented!("Unused at the moment");
465    }
466
467    fn timestamp(&self) -> zx::BootInstant {
468        self.timestamp
469    }
470
471    fn write_arguments<B: MutableBuffer>(
472        self,
473        writer: &mut Encoder<B>,
474    ) -> Result<(), EncodingError> {
475        for argument in self.record_arguments {
476            writer.write_argument(argument)?;
477        }
478        Ok(())
479    }
480}
481
482impl RecordFields for Record<'_> {
483    fn raw_severity(&self) -> RawSeverity {
484        self.severity
485    }
486
487    fn write_arguments<B: MutableBuffer>(
488        self,
489        writer: &mut Encoder<B>,
490    ) -> Result<(), EncodingError> {
491        for arg in self.arguments {
492            writer.write_argument(arg)?;
493        }
494        Ok(())
495    }
496
497    fn timestamp(&self) -> zx::BootInstant {
498        self.timestamp
499    }
500}
501
502#[cfg(test)]
503impl RecordFields for &Record<'_> {
504    fn raw_severity(&self) -> RawSeverity {
505        self.severity
506    }
507
508    fn write_arguments<B: MutableBuffer>(
509        self,
510        writer: &mut Encoder<B>,
511    ) -> Result<(), EncodingError> {
512        for arg in &self.arguments {
513            writer.write_argument(arg)?;
514        }
515        Ok(())
516    }
517
518    fn timestamp(&self) -> zx::BootInstant {
519        self.timestamp
520    }
521}
522
523/// Analogous to `bytes::BufMut` with some additions to be able to write at specific offsets.
524pub trait MutableBuffer {
525    /// Returns the number of total bytes this container can store. Shared memory buffers are not
526    /// expected to resize and this should return the same value during the entire lifetime of the
527    /// buffer.
528    fn capacity(&self) -> usize;
529
530    /// Returns the current position into which the next write is expected.
531    fn cursor(&self) -> usize;
532
533    /// Advance the write cursor by `n` bytes.
534    ///
535    /// # Safety
536    ///
537    /// This is marked unsafe because a malformed caller may
538    /// cause a subsequent out-of-bounds write.
539    unsafe fn advance_cursor(&mut self, n: usize);
540
541    /// Write a copy of the `src` slice into the buffer, starting at the provided offset.
542    ///
543    /// # Safety
544    ///
545    /// Implementations are not expected to bounds check the requested copy, although they may do
546    /// so and still satisfy this trait's contract.
547    unsafe fn put_slice_at(&mut self, src: &[u8], offset: usize);
548
549    /// Returns whether the buffer has sufficient remaining capacity to write an incoming value.
550    fn has_remaining(&self, num_bytes: usize) -> bool;
551
552    /// Advances the write cursor without immediately writing any bytes to the buffer. The returned
553    /// struct offers the ability to later write to the provided portion of the buffer.
554    fn put_slot(&mut self, width: usize) -> Result<WriteSlot, EncodingError> {
555        if self.has_remaining(width) {
556            let slot = WriteSlot { range: self.cursor()..(self.cursor() + width) };
557            unsafe {
558                self.advance_cursor(width);
559            }
560            Ok(slot)
561        } else {
562            Err(EncodingError::BufferTooSmall)
563        }
564    }
565
566    /// Write `src` into the provided slot that was created at a previous point in the stream.
567    fn fill_slot(&mut self, slot: WriteSlot, src: &[u8]) {
568        assert_eq!(
569            src.len(),
570            slot.range.end - slot.range.start,
571            "WriteSlots can only insert exactly-sized content into the buffer"
572        );
573        unsafe {
574            self.put_slice_at(src, slot.range.start);
575        }
576    }
577
578    /// Writes the contents of the `src` buffer to `self`, starting at `self.cursor()` and
579    /// advancing the cursor by `src.len()`.
580    ///
581    /// # Panics
582    ///
583    /// This function panics if there is not enough remaining capacity in `self`.
584    fn put_slice(&mut self, src: &[u8]) -> Result<(), EncodingError> {
585        if self.has_remaining(src.len()) {
586            unsafe {
587                self.put_slice_at(src, self.cursor());
588                self.advance_cursor(src.len());
589            }
590            Ok(())
591        } else {
592            Err(EncodingError::NoCapacity)
593        }
594    }
595
596    /// Writes an unsigned 64 bit integer to `self` in little-endian byte order.
597    ///
598    /// Advances the cursor by 8 bytes.
599    ///
600    /// # Examples
601    ///
602    /// ```
603    /// use bytes::BufMut;
604    ///
605    /// let mut buf = vec![0; 8];
606    /// buf.put_u64_le_at(0x0102030405060708, 0);
607    /// assert_eq!(buf, b"\x08\x07\x06\x05\x04\x03\x02\x01");
608    /// ```
609    ///
610    /// # Panics
611    ///
612    /// This function panics if there is not enough remaining capacity in `self`.
613    fn put_u64_le(&mut self, n: u64) -> Result<(), EncodingError> {
614        self.put_slice(&n.to_le_bytes())
615    }
616
617    /// Writes a signed 64 bit integer to `self` in little-endian byte order.
618    ///
619    /// The cursor position is advanced by 8.
620    ///
621    /// # Examples
622    ///
623    /// ```
624    /// use bytes::BufMut;
625    ///
626    /// let mut buf = vec![0; 8];
627    /// buf.put_i64_le_at(0x0102030405060708, 0);
628    /// assert_eq!(buf, b"\x08\x07\x06\x05\x04\x03\x02\x01");
629    /// ```
630    ///
631    /// # Panics
632    ///
633    /// This function panics if there is not enough remaining capacity in `self`.
634    fn put_i64_le(&mut self, n: i64) -> Result<(), EncodingError> {
635        self.put_slice(&n.to_le_bytes())
636    }
637
638    /// Writes a double-precision IEEE 754 floating point number to `self`.
639    ///
640    /// The cursor position is advanced by 8.
641    ///
642    /// # Examples
643    ///
644    /// ```
645    /// use bytes::BufMut;
646    ///
647    /// let mut buf = vec![];
648    /// buf.put_i64_le(0x0102030405060708);
649    /// assert_eq!(buf, b"\x08\x07\x06\x05\x04\x03\x02\x01");
650    /// ```
651    ///
652    /// # Panics
653    ///
654    /// This function panics if there is not enough remaining capacity in `self`.
655    fn put_f64(&mut self, n: f64) -> Result<(), EncodingError> {
656        self.put_slice(&n.to_bits().to_ne_bytes())
657    }
658}
659
660/// A region of the buffer which was advanced past and can later be filled in.
661#[must_use]
662pub struct WriteSlot {
663    range: std::ops::Range<usize>,
664}
665
666/// Wrapper for a vector that allows us to implement necessary traits.
667#[derive(Debug, Default)]
668pub struct ResizableBuffer(Vec<u8>);
669
670impl From<Vec<u8>> for ResizableBuffer {
671    fn from(buf: Vec<u8>) -> Self {
672        Self(buf)
673    }
674}
675
676impl Deref for ResizableBuffer {
677    type Target = Vec<u8>;
678
679    // Required method
680    fn deref(&self) -> &Self::Target {
681        &self.0
682    }
683}
684
685impl ResizableBuffer {
686    /// Return the inner vector.
687    pub fn into_inner(self) -> Vec<u8> {
688        self.0
689    }
690}
691
692impl MutableBuffer for Cursor<ResizableBuffer> {
693    fn capacity(&self) -> usize {
694        self.get_ref().0.len()
695    }
696
697    fn cursor(&self) -> usize {
698        self.position() as usize
699    }
700
701    fn has_remaining(&self, _num_bytes: usize) -> bool {
702        true
703    }
704
705    unsafe fn advance_cursor(&mut self, n: usize) {
706        self.set_position(self.position() + n as u64);
707    }
708
709    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
710        let this = &mut self.get_mut().0;
711        if offset < this.len() {
712            let available = this.len() - offset;
713
714            // Copy the elements that fit into the buffer.
715            let min = available.min(to_put.len());
716            let dest = &mut this[offset..(offset + min)];
717            dest.copy_from_slice(&to_put[..min]);
718
719            // If we couldn't fit all elements, then extend the buffer with the remaining elements.
720            if available < to_put.len() {
721                this.extend_from_slice(&to_put[available..]);
722            }
723        } else {
724            // If the offset is bigger than the length, fill with zeros up to the offset and then
725            // write the slice.
726            this.resize(offset, 0);
727            this.extend_from_slice(to_put);
728        }
729    }
730}
731
732impl<T: MutableBuffer + ?Sized> MutableBuffer for &mut T {
733    fn has_remaining(&self, num_bytes: usize) -> bool {
734        (**self).has_remaining(num_bytes)
735    }
736    fn capacity(&self) -> usize {
737        (**self).capacity()
738    }
739
740    fn cursor(&self) -> usize {
741        (**self).cursor()
742    }
743
744    unsafe fn advance_cursor(&mut self, n: usize) {
745        (**self).advance_cursor(n);
746    }
747
748    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
749        (**self).put_slice_at(to_put, offset);
750    }
751}
752
753impl<T: MutableBuffer + ?Sized> MutableBuffer for Box<T> {
754    fn has_remaining(&self, num_bytes: usize) -> bool {
755        (**self).has_remaining(num_bytes)
756    }
757    fn capacity(&self) -> usize {
758        (**self).capacity()
759    }
760
761    fn cursor(&self) -> usize {
762        (**self).cursor()
763    }
764
765    unsafe fn advance_cursor(&mut self, n: usize) {
766        (**self).advance_cursor(n);
767    }
768
769    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
770        (**self).put_slice_at(to_put, offset);
771    }
772}
773
774impl MutableBuffer for Cursor<Vec<u8>> {
775    fn has_remaining(&self, num_bytes: usize) -> bool {
776        (self.cursor() + num_bytes) <= self.capacity()
777    }
778
779    fn capacity(&self) -> usize {
780        self.get_ref().len()
781    }
782
783    fn cursor(&self) -> usize {
784        self.position() as usize
785    }
786
787    unsafe fn advance_cursor(&mut self, n: usize) {
788        self.set_position(self.position() + n as u64);
789    }
790
791    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
792        let dest = &mut self.get_mut()[offset..(offset + to_put.len())];
793        dest.copy_from_slice(to_put);
794    }
795}
796
797impl MutableBuffer for Cursor<&mut [u8]> {
798    fn has_remaining(&self, num_bytes: usize) -> bool {
799        (self.cursor() + num_bytes) <= self.capacity()
800    }
801
802    fn capacity(&self) -> usize {
803        self.get_ref().len()
804    }
805
806    fn cursor(&self) -> usize {
807        self.position() as usize
808    }
809
810    unsafe fn advance_cursor(&mut self, n: usize) {
811        self.set_position(self.position() + n as u64);
812    }
813
814    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
815        let dest = &mut self.get_mut()[offset..(offset + to_put.len())];
816        dest.copy_from_slice(to_put);
817    }
818}
819
820impl<const N: usize> MutableBuffer for Cursor<[u8; N]> {
821    fn has_remaining(&self, num_bytes: usize) -> bool {
822        (self.cursor() + num_bytes) <= self.capacity()
823    }
824    fn capacity(&self) -> usize {
825        self.get_ref().len()
826    }
827
828    fn cursor(&self) -> usize {
829        self.position() as usize
830    }
831
832    unsafe fn advance_cursor(&mut self, n: usize) {
833        self.set_position(self.position() + n as u64);
834    }
835
836    unsafe fn put_slice_at(&mut self, to_put: &[u8], offset: usize) {
837        let dest = &mut self.get_mut()[offset..(offset + to_put.len())];
838        dest.copy_from_slice(to_put);
839    }
840}
841
842/// An error that occurred while encoding data to the stream format.
843#[derive(Debug, Error)]
844pub enum EncodingError {
845    /// The provided buffer is too small.
846    #[error("buffer is too small")]
847    BufferTooSmall,
848
849    /// We attempted to encode values which are not yet supported by this implementation of
850    /// the Fuchsia Tracing format.
851    #[error("unsupported value type")]
852    Unsupported,
853
854    /// We attempted to write to a buffer with no remaining capacity.
855    #[error("the buffer has no remaining capacity")]
856    NoCapacity,
857
858    /// Some other error happened. Useful for integrating with this crate, but providing custom
859    /// errors.
860    #[error(transparent)]
861    Other(Box<dyn std::error::Error + Send + Sync>),
862}
863
864impl EncodingError {
865    /// Treat a custom error as an encoding error.
866    pub fn other<E>(err: E) -> Self
867    where
868        E: std::error::Error + Send + Sync + 'static,
869    {
870        Self::Other(err.into())
871    }
872}
873
874impl From<TryFromSliceError> for EncodingError {
875    fn from(_: TryFromSliceError) -> Self {
876        EncodingError::BufferTooSmall
877    }
878}
879
880#[cfg(test)]
881mod tests {
882    use super::*;
883    use crate::parse::parse_record;
884
885    #[fuchsia::test]
886    fn build_basic_record() {
887        let mut encoder = Encoder::new(Cursor::new([0u8; 1024]), EncoderOpts::default());
888        encoder
889            .write_event(WriteEventParams::<_, &str, _> {
890                event: TestRecord {
891                    severity: Severity::Info as u8,
892                    timestamp: zx::BootInstant::from_nanos(12345),
893                    file: None,
894                    line: None,
895                    record_arguments: vec![],
896                },
897                tags: &[],
898                metatags: std::iter::empty(),
899                pid: zx::Koid::from_raw(0),
900                tid: zx::Koid::from_raw(0),
901                dropped: 0,
902            })
903            .expect("wrote event");
904        let (record, _) = parse_record(encoder.inner().get_ref()).expect("wrote valid record");
905        assert_eq!(
906            record,
907            Record {
908                timestamp: zx::BootInstant::from_nanos(12345),
909                severity: Severity::Info as u8,
910                arguments: vec![
911                    Argument::pid(zx::Koid::from_raw(0)),
912                    Argument::tid(zx::Koid::from_raw(0)),
913                ]
914            }
915        );
916    }
917
918    #[fuchsia::test]
919    fn build_records_with_location() {
920        let mut encoder = Encoder::new(Cursor::new([0u8; 1024]), EncoderOpts::default());
921        encoder
922            .write_event(WriteEventParams::<_, &str, _> {
923                event: TestRecord {
924                    severity: Severity::Error as u8,
925                    timestamp: zx::BootInstant::from_nanos(12345),
926                    file: Some("foo.rs"),
927                    line: Some(10),
928                    record_arguments: vec![],
929                },
930                tags: &[],
931                metatags: std::iter::empty(),
932                pid: zx::Koid::from_raw(0),
933                tid: zx::Koid::from_raw(0),
934                dropped: 0,
935            })
936            .expect("wrote event");
937        let (record, _) = parse_record(encoder.inner().get_ref()).expect("wrote valid record");
938        assert_eq!(
939            record,
940            Record {
941                timestamp: zx::BootInstant::from_nanos(12345),
942                severity: Severity::Error as u8,
943                arguments: vec![
944                    Argument::pid(zx::Koid::from_raw(0)),
945                    Argument::tid(zx::Koid::from_raw(0)),
946                    Argument::file("foo.rs"),
947                    Argument::line(10),
948                ]
949            }
950        );
951    }
952
953    #[fuchsia::test]
954    fn build_record_with_dropped_count() {
955        let mut encoder = Encoder::new(Cursor::new([0u8; 1024]), EncoderOpts::default());
956        encoder
957            .write_event(WriteEventParams::<_, &str, _> {
958                event: TestRecord {
959                    severity: Severity::Warn as u8,
960                    timestamp: zx::BootInstant::from_nanos(12345),
961                    file: None,
962                    line: None,
963                    record_arguments: vec![],
964                },
965                tags: &[],
966                metatags: std::iter::empty(),
967                pid: zx::Koid::from_raw(0),
968                tid: zx::Koid::from_raw(0),
969                dropped: 7,
970            })
971            .expect("wrote event");
972        let (record, _) = parse_record(encoder.inner().get_ref()).expect("wrote valid record");
973        assert_eq!(
974            record,
975            Record {
976                timestamp: zx::BootInstant::from_nanos(12345),
977                severity: Severity::Warn as u8,
978                arguments: vec![
979                    Argument::pid(zx::Koid::from_raw(0)),
980                    Argument::tid(zx::Koid::from_raw(0)),
981                    Argument::dropped(7),
982                ]
983            }
984        );
985    }
986
987    #[test]
988    fn resizable_vec_mutable_buffer() {
989        // Putting a slice at offset=len is equivalent to concatenating.
990        let mut vec = Cursor::new(ResizableBuffer(vec![1u8, 2, 3]));
991        unsafe {
992            vec.put_slice_at(&[4, 5, 6], 3);
993        }
994        assert_eq!(vec.get_ref().0, vec![1, 2, 3, 4, 5, 6]);
995
996        // Putting a slice at an offset inside the buffer, is equivalent to replacing the items
997        // there.
998        let mut vec = Cursor::new(ResizableBuffer(vec![1, 3, 7, 9, 11, 13, 15]));
999        unsafe {
1000            vec.put_slice_at(&[2, 4, 6], 2);
1001        }
1002        assert_eq!(vec.get_ref().0, vec![1, 3, 2, 4, 6, 13, 15]);
1003
1004        // Putting a slice at an index in range replaces all the items and extends if needed.
1005        let mut vec = Cursor::new(ResizableBuffer(vec![1, 2, 3]));
1006        unsafe {
1007            vec.put_slice_at(&[4, 5, 6, 7], 0);
1008        }
1009        assert_eq!(vec.get_ref().0, vec![4, 5, 6, 7]);
1010
1011        // Putting a slice at an offset beyond the buffer, fills with zeros the items in between.
1012        let mut vec = Cursor::new(ResizableBuffer(vec![1, 2, 3]));
1013        unsafe {
1014            vec.put_slice_at(&[4, 5, 6], 5);
1015        }
1016        assert_eq!(vec.get_ref().0, vec![1, 2, 3, 0, 0, 4, 5, 6]);
1017    }
1018}