1use 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
16pub struct Encoder<B> {
19 pub(crate) buf: B,
20 options: EncoderOpts,
22}
23
24#[derive(Default)]
26pub struct EncoderOpts {
27 pub always_log_file_line: bool,
31}
32
33pub struct WriteEventParams<'a, E, T, MS> {
35 pub event: E,
37 pub tags: &'a [T],
39 pub metatags: MS,
41 pub pid: zx::Koid,
43 pub tid: zx::Koid,
45 pub dropped: u64,
47}
48
49impl<B> Encoder<B>
50where
51 B: MutableBuffer,
52{
53 pub fn new(buf: B, options: EncoderOpts) -> Self {
55 Self { buf, options }
56 }
57
58 pub fn inner(&self) -> &B {
60 &self.buf
61 }
62
63 pub fn take(self) -> B {
65 self.buf
66 }
67
68 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 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 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 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 let starting_idx = self.buf.cursor();
139 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 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 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 fn write_u64(&mut self, n: u64) -> Result<(), EncodingError> {
214 self.buf.put_u64_le(n).map_err(|_| EncodingError::BufferTooSmall)
215 }
216
217 fn write_i64(&mut self, n: i64) -> Result<(), EncodingError> {
219 self.buf.put_i64_le(n).map_err(|_| EncodingError::BufferTooSmall)
220 }
221
222 fn write_f64(&mut self, n: f64) -> Result<(), EncodingError> {
224 self.buf.put_f64(n).map_err(|_| EncodingError::BufferTooSmall)
225 }
226
227 fn write_string(&mut self, src: &str) -> Result<(), EncodingError> {
229 self.write_bytes(src.as_bytes())
230 }
231
232 #[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 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
261pub trait WriteArgumentValue<B>: private::Sealed {
263 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
389pub trait RecordEvent {
391 fn raw_severity(&self) -> RawSeverity;
393 fn file(&self) -> Option<&str>;
395 fn line(&self) -> Option<u32>;
397 fn target(&self) -> &str;
399 fn write_arguments<B: MutableBuffer>(
401 self,
402 writer: &mut Encoder<B>,
403 ) -> Result<(), EncodingError>;
404 fn timestamp(&self) -> zx::BootInstant;
406}
407
408pub trait RecordFields {
410 fn raw_severity(&self) -> RawSeverity;
412
413 fn timestamp(&self) -> zx::BootInstant;
415
416 fn write_arguments<B: MutableBuffer>(
418 self,
419 writer: &mut Encoder<B>,
420 ) -> Result<(), EncodingError>;
421}
422
423pub struct TestRecord<'a> {
425 pub severity: RawSeverity,
427 pub timestamp: zx::BootInstant,
429 pub file: Option<&'a str>,
431 pub line: Option<u32>,
433 pub record_arguments: Vec<Argument<'a>>,
435}
436
437impl TestRecord<'_> {
438 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
523pub trait MutableBuffer {
525 fn capacity(&self) -> usize;
529
530 fn cursor(&self) -> usize;
532
533 unsafe fn advance_cursor(&mut self, n: usize);
540
541 unsafe fn put_slice_at(&mut self, src: &[u8], offset: usize);
548
549 fn has_remaining(&self, num_bytes: usize) -> bool;
551
552 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 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 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 fn put_u64_le(&mut self, n: u64) -> Result<(), EncodingError> {
614 self.put_slice(&n.to_le_bytes())
615 }
616
617 fn put_i64_le(&mut self, n: i64) -> Result<(), EncodingError> {
635 self.put_slice(&n.to_le_bytes())
636 }
637
638 fn put_f64(&mut self, n: f64) -> Result<(), EncodingError> {
656 self.put_slice(&n.to_bits().to_ne_bytes())
657 }
658}
659
660#[must_use]
662pub struct WriteSlot {
663 range: std::ops::Range<usize>,
664}
665
666#[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 fn deref(&self) -> &Self::Target {
681 &self.0
682 }
683}
684
685impl ResizableBuffer {
686 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 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 available < to_put.len() {
721 this.extend_from_slice(&to_put[available..]);
722 }
723 } else {
724 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#[derive(Debug, Error)]
844pub enum EncodingError {
845 #[error("buffer is too small")]
847 BufferTooSmall,
848
849 #[error("unsupported value type")]
852 Unsupported,
853
854 #[error("the buffer has no remaining capacity")]
856 NoCapacity,
857
858 #[error(transparent)]
861 Other(Box<dyn std::error::Error + Send + Sync>),
862}
863
864impl EncodingError {
865 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 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 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 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 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}