Skip to main content

unsafe_libyaml/
lib.rs

1//! A line-by-line translation of libyaml from C to unsafe Rust.
2
3#![no_std]
4#![allow(non_camel_case_types, non_snake_case, unsafe_op_in_unsafe_fn, unused_assignments)]
5#![warn(clippy::pedantic)]
6#![allow(
7    clippy::bool_to_int_with_if,
8    clippy::cast_lossless,
9    clippy::cast_possible_truncation,
10    clippy::cast_possible_wrap,
11    clippy::cast_ptr_alignment,
12    clippy::cast_sign_loss,
13    clippy::collapsible_if,
14    clippy::doc_markdown,
15    clippy::fn_params_excessive_bools,
16    clippy::if_not_else,
17    clippy::items_after_statements,
18    clippy::let_underscore_untyped,
19    clippy::manual_range_contains,
20    clippy::manual_swap,
21    clippy::missing_panics_doc,
22    clippy::missing_safety_doc,
23    clippy::module_name_repetitions,
24    clippy::must_use_candidate,
25    clippy::nonminimal_bool,
26    clippy::ptr_as_ptr,
27    clippy::redundant_else,
28    clippy::similar_names,
29    clippy::single_match,
30    clippy::single_match_else,
31    clippy::too_many_arguments,
32    clippy::too_many_lines,
33    clippy::uninlined_format_args,
34    clippy::unnecessary_cast,
35    clippy::unreadable_literal,
36    clippy::while_immutable_condition, // https://github.com/rust-lang/rust-clippy/issues/3548
37)]
38
39extern crate alloc;
40
41use core::mem::size_of;
42
43mod libc {
44    pub use core::{
45        ffi::c_void,
46        primitive::{i8 as c_char, i32 as c_int, i64 as c_long, u8 as c_uchar, u32 as c_uint, u64 as c_ulong},
47    };
48}
49
50#[macro_use]
51mod externs {
52    use alloc::alloc::{self as rust, Layout};
53    use core::{
54        mem::{self, MaybeUninit},
55        ptr, slice,
56    };
57
58    use crate::{
59        libc,
60        ops::{ForceAdd as _, ForceInto as _, die},
61    };
62
63    const HEADER: usize = {
64        let need_len = mem::size_of::<usize>();
65        // Round up to multiple of MALLOC_ALIGN.
66        (need_len + MALLOC_ALIGN - 1) & !(MALLOC_ALIGN - 1)
67    };
68
69    // `max_align_t` may be bigger than this, but libyaml does not use `long
70    // double` or u128.
71    const MALLOC_ALIGN: usize = {
72        let int_align = mem::align_of::<libc::c_ulong>();
73        let ptr_align = mem::align_of::<usize>();
74        if int_align >= ptr_align { int_align } else { ptr_align }
75    };
76
77    pub unsafe fn malloc(size: libc::c_ulong) -> *mut libc::c_void {
78        let size = HEADER.force_add(size.force_into());
79        let layout = Layout::from_size_align(size, MALLOC_ALIGN).ok().unwrap_or_else(die);
80        let memory = rust::alloc(layout);
81        if memory.is_null() {
82            rust::handle_alloc_error(layout);
83        }
84        memory.cast::<usize>().write(size);
85        memory.add(HEADER).cast()
86    }
87
88    pub unsafe fn realloc(ptr: *mut libc::c_void, new_size: libc::c_ulong) -> *mut libc::c_void {
89        let mut memory = ptr.cast::<u8>().sub(HEADER);
90        let size = memory.cast::<usize>().read();
91        let layout = Layout::from_size_align_unchecked(size, MALLOC_ALIGN);
92        let new_size = HEADER.force_add(new_size.force_into());
93        let new_layout = Layout::from_size_align(new_size, MALLOC_ALIGN).ok().unwrap_or_else(die);
94        memory = rust::realloc(memory, layout, new_size);
95        if memory.is_null() {
96            rust::handle_alloc_error(new_layout);
97        }
98        memory.cast::<usize>().write(new_size);
99        memory.add(HEADER).cast()
100    }
101
102    pub unsafe fn free(ptr: *mut libc::c_void) {
103        let memory = ptr.cast::<u8>().sub(HEADER);
104        let size = memory.cast::<usize>().read();
105        let layout = Layout::from_size_align_unchecked(size, MALLOC_ALIGN);
106        rust::dealloc(memory, layout);
107    }
108
109    pub unsafe fn memcmp(lhs: *const libc::c_void, rhs: *const libc::c_void, count: libc::c_ulong) -> libc::c_int {
110        let lhs = slice::from_raw_parts(lhs.cast::<u8>(), count as usize);
111        let rhs = slice::from_raw_parts(rhs.cast::<u8>(), count as usize);
112        lhs.cmp(rhs) as libc::c_int
113    }
114
115    pub unsafe fn memcpy(dest: *mut libc::c_void, src: *const libc::c_void, count: libc::c_ulong) -> *mut libc::c_void {
116        ptr::copy_nonoverlapping(src.cast::<MaybeUninit<u8>>(), dest.cast::<MaybeUninit<u8>>(), count as usize);
117        dest
118    }
119
120    pub unsafe fn memmove(
121        dest: *mut libc::c_void,
122        src: *const libc::c_void,
123        count: libc::c_ulong,
124    ) -> *mut libc::c_void {
125        ptr::copy(src.cast::<MaybeUninit<u8>>(), dest.cast::<MaybeUninit<u8>>(), count as usize);
126        dest
127    }
128
129    pub unsafe fn memset(dest: *mut libc::c_void, ch: libc::c_int, count: libc::c_ulong) -> *mut libc::c_void {
130        ptr::write_bytes(dest.cast::<u8>(), ch as u8, count as usize);
131        dest
132    }
133
134    pub unsafe fn strcmp(lhs: *const libc::c_char, rhs: *const libc::c_char) -> libc::c_int {
135        let lhs = slice::from_raw_parts(lhs.cast::<u8>(), strlen(lhs) as usize);
136        let rhs = slice::from_raw_parts(rhs.cast::<u8>(), strlen(rhs) as usize);
137        lhs.cmp(rhs) as libc::c_int
138    }
139
140    pub unsafe fn strdup(src: *const libc::c_char) -> *mut libc::c_char {
141        let len = strlen(src);
142        let dest = malloc(len + 1);
143        memcpy(dest, src.cast(), len + 1);
144        dest.cast()
145    }
146
147    pub unsafe fn strlen(str: *const libc::c_char) -> libc::c_ulong {
148        let mut end = str;
149        while *end != 0 {
150            end = end.add(1);
151        }
152        end.offset_from(str) as libc::c_ulong
153    }
154
155    pub unsafe fn strncmp(lhs: *const libc::c_char, rhs: *const libc::c_char, mut count: libc::c_ulong) -> libc::c_int {
156        let mut lhs = lhs.cast::<u8>();
157        let mut rhs = rhs.cast::<u8>();
158        while count > 0 && *lhs != 0 && *lhs == *rhs {
159            lhs = lhs.add(1);
160            rhs = rhs.add(1);
161            count -= 1;
162        }
163        if count == 0 {
164            0
165        } else {
166            (*lhs).cmp(&*rhs) as libc::c_int
167        }
168    }
169
170    macro_rules! __assert {
171        (false $(,)?) => {
172            $crate::externs::__assert_fail(stringify!(false), file!(), line!())
173        };
174        ($assertion:expr $(,)?) => {
175            if !$assertion {
176                $crate::externs::__assert_fail(stringify!($assertion), file!(), line!());
177            }
178        };
179    }
180
181    pub(crate) unsafe fn __assert_fail(__assertion: &'static str, __file: &'static str, __line: u32) -> ! {
182        struct Abort;
183        impl Drop for Abort {
184            fn drop(&mut self) {
185                panic!();
186            }
187        }
188        let _abort_on_panic = Abort;
189        panic!("{}:{}: Assertion `{}` failed.", __file, __line, __assertion);
190    }
191}
192
193mod fmt {
194    use core::{
195        fmt::{self, Write},
196        ptr,
197    };
198
199    use crate::yaml::yaml_char_t;
200
201    pub struct WriteToPtr {
202        ptr: *mut yaml_char_t,
203    }
204
205    impl WriteToPtr {
206        pub unsafe fn new(ptr: *mut yaml_char_t) -> Self {
207            WriteToPtr {
208                ptr,
209            }
210        }
211
212        pub fn write_fmt(&mut self, args: fmt::Arguments) {
213            let _ = Write::write_fmt(self, args);
214        }
215    }
216
217    impl Write for WriteToPtr {
218        fn write_str(&mut self, s: &str) -> fmt::Result {
219            unsafe {
220                ptr::copy_nonoverlapping(s.as_ptr(), self.ptr, s.len());
221                self.ptr = self.ptr.add(s.len());
222            }
223            Ok(())
224        }
225    }
226}
227
228trait PointerExt: Sized {
229    fn c_offset_from(self, origin: Self) -> isize;
230}
231
232impl<T> PointerExt for *const T {
233    fn c_offset_from(self, origin: *const T) -> isize {
234        (self as isize - origin as isize) / size_of::<T>() as isize
235    }
236}
237
238impl<T> PointerExt for *mut T {
239    fn c_offset_from(self, origin: *mut T) -> isize {
240        (self as isize - origin as isize) / size_of::<T>() as isize
241    }
242}
243
244#[macro_use]
245mod macros;
246
247mod api;
248mod dumper;
249mod emitter;
250mod loader;
251mod ops;
252mod parser;
253mod reader;
254mod scanner;
255mod success;
256mod writer;
257mod yaml;
258
259#[doc(hidden)]
260pub use crate::yaml::{
261    yaml_break_t::*, yaml_emitter_state_t::*, yaml_encoding_t::*, yaml_error_type_t::*, yaml_event_type_t::*,
262    yaml_mapping_style_t::*, yaml_node_type_t::*, yaml_parser_state_t::*, yaml_scalar_style_t::*,
263    yaml_sequence_style_t::*, yaml_token_type_t::*,
264};
265pub use crate::{
266    api::{
267        yaml_alias_event_initialize, yaml_document_add_mapping, yaml_document_add_scalar, yaml_document_add_sequence,
268        yaml_document_append_mapping_pair, yaml_document_append_sequence_item, yaml_document_delete,
269        yaml_document_end_event_initialize, yaml_document_get_node, yaml_document_get_root_node,
270        yaml_document_initialize, yaml_document_start_event_initialize, yaml_emitter_delete, yaml_emitter_initialize,
271        yaml_emitter_set_break, yaml_emitter_set_canonical, yaml_emitter_set_encoding, yaml_emitter_set_indent,
272        yaml_emitter_set_output, yaml_emitter_set_output_string, yaml_emitter_set_unicode, yaml_emitter_set_width,
273        yaml_event_delete, yaml_mapping_end_event_initialize, yaml_mapping_start_event_initialize, yaml_parser_delete,
274        yaml_parser_initialize, yaml_parser_set_encoding, yaml_parser_set_input, yaml_parser_set_input_string,
275        yaml_scalar_event_initialize, yaml_sequence_end_event_initialize, yaml_sequence_start_event_initialize,
276        yaml_stream_end_event_initialize, yaml_stream_start_event_initialize, yaml_token_delete,
277    },
278    dumper::{yaml_emitter_close, yaml_emitter_dump, yaml_emitter_open},
279    emitter::yaml_emitter_emit,
280    loader::yaml_parser_load,
281    parser::yaml_parser_parse,
282    scanner::yaml_parser_scan,
283    writer::yaml_emitter_flush,
284    yaml::{
285        yaml_alias_data_t, yaml_break_t, yaml_document_t, yaml_emitter_state_t, yaml_emitter_t, yaml_encoding_t,
286        yaml_error_type_t, yaml_event_t, yaml_event_type_t, yaml_mapping_style_t, yaml_mark_t, yaml_node_item_t,
287        yaml_node_pair_t, yaml_node_t, yaml_node_type_t, yaml_parser_state_t, yaml_parser_t, yaml_read_handler_t,
288        yaml_scalar_style_t, yaml_sequence_style_t, yaml_simple_key_t, yaml_stack_t, yaml_tag_directive_t,
289        yaml_token_t, yaml_token_type_t, yaml_version_directive_t, yaml_write_handler_t,
290    },
291};