1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
// Copyright © 2024 Collabora, Ltd.
// SPDX-License-Identifier: MIT
use std::io;
use std::marker::PhantomPinned;
use std::pin::Pin;
use crate::bindings;
struct MemStreamImpl {
stream: bindings::u_memstream,
buffer: *mut u8,
buffer_size: usize,
_pin: PhantomPinned,
}
/// A Rust memstream abstraction. Useful when interacting with C code that
/// expects a FILE* pointer.
///
/// The size of the buffer is managed by the C code automatically.
pub struct MemStream(Pin<Box<MemStreamImpl>>);
impl MemStream {
pub fn new() -> io::Result<Self> {
let mut stream_impl = Box::pin(MemStreamImpl {
stream: Default::default(),
buffer: std::ptr::null_mut(),
buffer_size: 0,
_pin: PhantomPinned,
});
unsafe {
let stream_impl = stream_impl.as_mut().get_unchecked_mut();
if !bindings::u_memstream_open(
&mut stream_impl.stream,
(&mut stream_impl.buffer as *mut *mut u8).cast(),
&mut stream_impl.buffer_size,
) {
return Err(io::Error::last_os_error());
}
if bindings::u_memstream_flush(&mut stream_impl.stream) != 0 {
return Err(io::Error::last_os_error());
}
}
Ok(Self(stream_impl))
}
// Safety: caller must ensure that inner is not moved through the returned
// reference.
unsafe fn inner_mut(&mut self) -> &mut MemStreamImpl {
unsafe { self.0.as_mut().get_unchecked_mut() }
}
/// Flushes the stream so written data appears in the stream
pub fn flush(&mut self) -> io::Result<()> {
unsafe {
let stream = self.inner_mut();
if bindings::u_memstream_flush(&mut stream.stream) != 0 {
return Err(io::Error::last_os_error());
}
}
Ok(())
}
/// Resets the MemStream
pub fn reset(&mut self) -> io::Result<()> {
*self = Self::new()?;
Ok(())
}
/// Resets the MemStream and returns its contents
pub fn take(&mut self) -> io::Result<Vec<u8>> {
let mut vec = Vec::new();
vec.extend_from_slice(self.as_slice()?);
self.reset()?;
Ok(vec)
}
/// Resets the MemStream and returns its contents as a UTF-8 string
pub fn take_utf8_string_lossy(&mut self) -> io::Result<String> {
let string = String::from_utf8_lossy(self.as_slice()?).into_owned();
self.reset()?;
Ok(string)
}
/// Returns the current position in the stream.
pub fn position(&self) -> usize {
unsafe { bindings::compiler_rs_ftell(self.c_file()) as usize }
}
/// Seek to a position relative to the start of the stream.
pub fn seek(&mut self, offset: u64) -> io::Result<()> {
let offset = offset.try_into().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "offset too large")
})?;
unsafe {
if bindings::compiler_rs_fseek(self.c_file(), offset, 0) != 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
}
/// Returns the underlying C file.
///
/// # Safety
///
/// The memstream abstraction assumes that the file is valid throughout its
/// lifetime.
pub unsafe fn c_file(&self) -> *mut bindings::FILE {
self.0.stream.f
}
/// Returns a slice view into the memstream
///
/// This is only safe with respect to other safe Rust methods. Even though
/// this takes a reference to the stream there is nothing preventing you
/// from modifying the stream through the FILE with unsafe C code.
///
/// This is conceptually the same as `AsRef`, but it flushes the stream
/// first, which means it takes &mut self as a receiver.
fn as_slice(&mut self) -> io::Result<&[u8]> {
// Make sure we have the most up-to-date data before returning a slice.
self.flush()?;
let pos = self.position();
if pos == 0 {
Ok(&[])
} else {
// SAFETY: this does not move the stream and we know that
// self.position() cannot exceed the stream size as per the
// open_memstream() API.
Ok(unsafe { std::slice::from_raw_parts(self.0.buffer, pos) })
}
}
}
impl Drop for MemStream {
fn drop(&mut self) {
// SAFETY: this does not move the stream.
unsafe {
bindings::u_memstream_close(&mut self.inner_mut().stream);
bindings::compiler_rs_free(self.0.buffer as *mut std::ffi::c_void);
}
}
}
#[test]
fn test_memstream() {
use std::ffi::CString;
let mut s = MemStream::new().unwrap();
let test_str = "Test string";
let test_c_str = CString::new(test_str).unwrap();
let test_bytes = test_c_str.as_bytes();
unsafe {
bindings::compiler_rs_fwrite(
test_bytes.as_ptr().cast(),
1,
test_bytes.len(),
s.c_file(),
);
}
let res = s.take_utf8_string_lossy().unwrap();
assert_eq!(res, test_str);
}
|