log_decoder_c_bindings/
lib.rs1use bumpalo::Bump;
5use diagnostics_log_encoding;
6use diagnostics_log_encoding::parse::ParseError;
7use diagnostics_message::error::MessageError;
8use diagnostics_message::ffi::{CPPArray, CPPMessageFormatter, LogMessage};
9use diagnostics_message::{self as message, MonikerWithUrl};
10use std::ffi::CString;
11use std::ops::{Deref, DerefMut};
12use std::os::raw::c_char;
13use std::ptr::NonNull;
14use thiserror::Error;
15
16#[unsafe(no_mangle)]
26pub unsafe extern "C" fn fuchsia_decode_log_message_to_json(
27 msg: *const u8,
28 size: usize,
29) -> *mut c_char {
30 let managed_ptr = unsafe { std::slice::from_raw_parts(msg, size) };
31 let data = &message::from_structured(
32 MonikerWithUrl { moniker: "test_moniker".try_into().unwrap(), url: "".into() },
33 managed_ptr,
34 )
35 .unwrap();
36 let item = serde_json::to_string(&data).unwrap();
37 CString::new(format!("[{}]", item)).unwrap().into_raw()
38}
39
40#[repr(C)]
48pub struct LogMessages<'a> {
49 messages: CPPArray<'a, &'a LogMessage<'a>>,
50 error_str: *const c_char,
51 allocator: AliasableBox<Bump>,
52}
53
54#[derive(Error, Debug)]
55pub enum DecodeError {
56 #[error(transparent)]
57 Message(#[from] MessageError),
58 #[error(transparent)]
59 ParserError(#[from] ParseError),
60}
61
62pub type MessageParser = message::MessageParser;
63
64#[unsafe(no_mangle)]
65pub extern "C" fn fuchsia_new_message_parser() -> *mut MessageParser {
66 Box::into_raw(Box::new(MessageParser::default()))
67}
68
69#[unsafe(no_mangle)]
74pub unsafe extern "C" fn fuchsia_free_message_parser(parser: *mut MessageParser) {
75 if !parser.is_null() {
76 unsafe { drop(Box::from_raw(parser)) };
79 }
80}
81
82#[unsafe(no_mangle)]
99pub unsafe extern "C" fn fuchsia_decode_log_messages_to_struct<'a>(
100 msg: *const u8,
101 size: usize,
102 expect_extended_attribution: bool,
103 parser: *mut MessageParser,
104) -> LogMessages<'a> {
105 let allocator = AliasableBox::new(Bump::new());
106
107 let allocator_ref: &'a Bump = unsafe { allocator.get_ref() };
112
113 let maybe_parser = unsafe { parser.as_mut() };
116
117 struct Buf(*const u8, usize);
119 impl Deref for Buf {
120 type Target = [u8];
121 fn deref(&self) -> &Self::Target {
122 unsafe { std::slice::from_raw_parts(self.0, self.1) }
125 }
126 }
127 let buf = &Buf(msg, size);
128
129 let messages = if let Some(parser) = maybe_parser {
130 fuchsia_decode_log_messages_to_struct_internal(buf, parser, allocator_ref)
131 } else {
132 fuchsia_decode_log_messages_to_struct_internal_legacy(
133 buf,
134 expect_extended_attribution,
135 allocator_ref,
136 )
137 };
138
139 match messages {
140 Ok(messages) => {
141 let messages: &[_] =
142 allocator_ref.alloc_slice_fill_iter(messages.into_iter().map(|m| &*m));
143 LogMessages { messages: messages.into(), error_str: std::ptr::null(), allocator }
144 }
145 Err(err) => LogMessages {
146 messages: CPPArray::default(),
147 error_str: allocator_ref
148 .alloc_slice_copy(CString::new(err.to_string()).unwrap().as_bytes_with_nul())
149 .as_ptr() as *const c_char,
150 allocator,
151 },
152 }
153}
154
155fn fuchsia_decode_log_messages_to_struct_internal<'a>(
157 buf: &[u8],
158 parser: &mut MessageParser,
159 allocator: &'a Bump,
160) -> Result<Vec<&'a mut LogMessage<'a>>, DecodeError> {
161 let mut messages = vec![];
162 let mut current_slice = buf.as_ref();
163 let formatter = CPPMessageFormatter(allocator);
164 loop {
165 let (data, remaining) = parser.parse_next(current_slice, &formatter)?;
166
167 if let Some(data) = data {
168 messages.push(data);
169 }
170 if remaining.is_empty() {
171 break;
172 }
173 current_slice = remaining;
174 }
175
176 Ok(messages)
177}
178
179fn fuchsia_decode_log_messages_to_struct_internal_legacy<'a>(
181 buf: &[u8],
182 expect_extended_attribution: bool,
183 allocator: &'a Bump,
184) -> Result<Vec<&'a mut LogMessage<'a>>, DecodeError> {
185 let mut messages = vec![];
186 let mut current_slice = buf.as_ref();
187 loop {
188 let (data, remaining) = if expect_extended_attribution {
189 message::ffi::ffi_from_extended_record(current_slice, allocator)?
190 } else {
191 let (_, remaining_after_parse) =
192 diagnostics_log_encoding::parse::parse_record(current_slice)?;
193 let record_len = current_slice.len() - remaining_after_parse.len();
194 let record_slice = ¤t_slice[..record_len];
195 let (data, _) = message::ffi::ffi_from_extended_record(record_slice, allocator)?;
196 (data, remaining_after_parse)
197 };
198 messages.push(data);
199 if remaining.is_empty() {
200 break;
201 }
202 current_slice = remaining;
203 }
204
205 Ok(messages)
206}
207
208#[unsafe(no_mangle)]
213pub unsafe extern "C" fn fuchsia_free_decoded_log_message(msg: *mut c_char) {
214 let str_to_free = unsafe { CString::from_raw(msg) };
215 let _freer = str_to_free;
216}
217
218#[unsafe(no_mangle)]
223pub unsafe extern "C" fn fuchsia_free_log_messages(input: LogMessages<'_>) {
224 drop(input);
225}
226
227#[repr(C)]
229struct AliasableBox<T>(NonNull<T>);
230
231impl<T> AliasableBox<T> {
232 unsafe fn get_ref<'a>(&self) -> &'a T {
234 unsafe { self.0.as_ref() }
236 }
237}
238
239impl<T> AliasableBox<T> {
240 fn new(value: T) -> Self {
241 Self(unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(value))) })
243 }
244}
245
246impl<T> Drop for AliasableBox<T> {
247 fn drop(&mut self) {
248 unsafe {
250 let _ = Box::from_raw(self.0.as_ptr());
251 }
252 }
253}
254
255impl<T> Deref for AliasableBox<T> {
256 type Target = T;
257 fn deref(&self) -> &Self::Target {
258 unsafe { self.0.as_ref() }
260 }
261}
262
263impl<T> DerefMut for AliasableBox<T> {
264 fn deref_mut(&mut self) -> &mut Self::Target {
265 unsafe { self.0.as_mut() }
267 }
268}