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::{CPPMessageFormatter, CppArray, 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 if msg.is_null() || size == 0 {
31 return std::ptr::null_mut();
32 }
33 let managed_ptr = unsafe { std::slice::from_raw_parts(msg, size) };
35 let Ok(data) = message::from_structured(
36 MonikerWithUrl { moniker: "test_moniker".try_into().unwrap(), url: "".into() },
37 managed_ptr,
38 ) else {
39 return std::ptr::null_mut();
40 };
41 let Ok(item) = serde_json::to_string(&data) else {
42 return std::ptr::null_mut();
43 };
44 let Ok(c_string) = CString::new(format!("[{}]", item)) else {
45 return std::ptr::null_mut();
46 };
47 c_string.into_raw()
48}
49
50#[repr(C)]
58pub struct LogMessages<'a> {
59 messages: CppArray<'a, &'a LogMessage<'a>>,
60 error_str: *const c_char,
61 allocator: AliasableBox<Bump>,
62}
63
64#[derive(Error, Debug)]
65pub enum DecodeError {
66 #[error(transparent)]
67 Message(#[from] MessageError),
68 #[error(transparent)]
69 ParserError(#[from] ParseError),
70}
71
72pub type MessageParser = message::MessageParser;
73
74#[unsafe(no_mangle)]
75pub extern "C" fn fuchsia_new_message_parser() -> *mut MessageParser {
76 Box::into_raw(Box::new(MessageParser::default()))
77}
78
79#[unsafe(no_mangle)]
84pub unsafe extern "C" fn fuchsia_free_message_parser(parser: *mut MessageParser) {
85 if !parser.is_null() {
86 unsafe { drop(Box::from_raw(parser)) };
89 }
90}
91
92#[unsafe(no_mangle)]
109pub unsafe extern "C" fn fuchsia_decode_log_messages_to_struct<'a>(
110 msg: *const u8,
111 size: usize,
112 expect_extended_attribution: bool,
113 parser: *mut MessageParser,
114) -> LogMessages<'a> {
115 let allocator = AliasableBox::new(Bump::new());
116
117 let allocator_ref: &'a Bump = unsafe { allocator.get_ref() };
122
123 let maybe_parser = unsafe { parser.as_mut() };
126
127 let buf: &'a [u8] = unsafe { std::slice::from_raw_parts(msg, size) };
130
131 let messages = if let Some(parser) = maybe_parser {
132 fuchsia_decode_log_messages_to_struct_internal(buf, parser, allocator_ref)
133 } else {
134 fuchsia_decode_log_messages_to_struct_internal_legacy(
135 buf,
136 expect_extended_attribution,
137 allocator_ref,
138 )
139 };
140
141 match messages {
142 Ok(messages) => {
143 let messages: &[_] =
144 allocator_ref.alloc_slice_fill_iter(messages.into_iter().map(|m| &*m));
145 LogMessages { messages: messages.into(), error_str: std::ptr::null(), allocator }
146 }
147 Err(err) => LogMessages {
148 messages: CppArray::default(),
149 error_str: allocator_ref
150 .alloc_slice_copy(CString::new(err.to_string()).unwrap().as_bytes_with_nul())
151 .as_ptr() as *const c_char,
152 allocator,
153 },
154 }
155}
156
157fn fuchsia_decode_log_messages_to_struct_internal<'a>(
159 buf: &'a [u8],
160 parser: &mut MessageParser,
161 allocator: &'a Bump,
162) -> Result<Vec<&'a mut LogMessage<'a>>, DecodeError> {
163 let mut messages = vec![];
164 let mut current_slice = buf.as_ref();
165 let formatter = CPPMessageFormatter(allocator);
166 loop {
167 let (data, remaining) = parser.parse_next(current_slice, &formatter)?;
168
169 if let Some(data) = data {
170 messages.push(data);
171 }
172 if remaining.is_empty() {
173 break;
174 }
175 current_slice = remaining;
176 }
177
178 Ok(messages)
179}
180
181fn fuchsia_decode_log_messages_to_struct_internal_legacy<'a>(
183 buf: &'a [u8],
184 expect_extended_attribution: bool,
185 allocator: &'a Bump,
186) -> Result<Vec<&'a mut LogMessage<'a>>, DecodeError> {
187 let mut messages = vec![];
188 let mut current_slice = buf.as_ref();
189 loop {
190 let (data, remaining) = if expect_extended_attribution {
191 message::ffi::ffi_from_extended_record(current_slice, allocator)?
192 } else {
193 let (_, remaining_after_parse) =
194 diagnostics_log_encoding::parse::parse_record(current_slice)?;
195 let record_len = current_slice.len() - remaining_after_parse.len();
196 let record_slice = ¤t_slice[..record_len];
197 let (data, _) = message::ffi::ffi_from_extended_record(record_slice, allocator)?;
198 (data, remaining_after_parse)
199 };
200 messages.push(data);
201 if remaining.is_empty() {
202 break;
203 }
204 current_slice = remaining;
205 }
206
207 Ok(messages)
208}
209
210#[unsafe(no_mangle)]
215pub unsafe extern "C" fn fuchsia_free_decoded_log_message(msg: *mut c_char) {
216 let str_to_free = unsafe { CString::from_raw(msg) };
217 let _freer = str_to_free;
218}
219
220#[unsafe(no_mangle)]
225pub unsafe extern "C" fn fuchsia_free_log_messages(input: LogMessages<'_>) {
226 drop(input);
227}
228
229#[repr(C)]
231struct AliasableBox<T>(NonNull<T>);
232
233impl<T> AliasableBox<T> {
234 unsafe fn get_ref<'a>(&self) -> &'a T {
236 unsafe { self.0.as_ref() }
238 }
239}
240
241impl<T> AliasableBox<T> {
242 fn new(value: T) -> Self {
243 Self(unsafe { NonNull::new_unchecked(Box::into_raw(Box::new(value))) })
245 }
246}
247
248impl<T> Drop for AliasableBox<T> {
249 fn drop(&mut self) {
250 unsafe {
252 let _ = Box::from_raw(self.0.as_ptr());
253 }
254 }
255}
256
257impl<T> Deref for AliasableBox<T> {
258 type Target = T;
259 fn deref(&self) -> &Self::Target {
260 unsafe { self.0.as_ref() }
262 }
263}
264
265impl<T> DerefMut for AliasableBox<T> {
266 fn deref_mut(&mut self) -> &mut Self::Target {
267 unsafe { self.0.as_mut() }
269 }
270}