1use crate::error_code::ErrorCode;
2use abi_stable::StableAbi;
3use std::ptr::NonNull;
4use tarantool::error::BoxError;
5use tarantool::error::TarantoolErrorCode;
6use tarantool::ffi::tarantool as ffi;
7
8#[repr(C)]
14#[derive(StableAbi, Clone, Copy, Debug)]
15pub struct FfiSafeBytes {
16 pointer: NonNull<u8>,
17 len: usize,
18}
19
20impl FfiSafeBytes {
21 #[inline(always)]
22 pub fn len(self) -> usize {
23 self.len
24 }
25
26 #[inline(always)]
27 pub fn is_empty(self) -> bool {
28 self.len == 0
29 }
30
31 #[inline(always)]
35 pub unsafe fn from_raw_parts(pointer: NonNull<u8>, len: usize) -> Self {
36 Self { pointer, len }
37 }
38
39 #[inline(always)]
40 pub fn into_raw_parts(self) -> (*mut u8, usize) {
41 (self.pointer.as_ptr(), self.len)
42 }
43
44 pub unsafe fn as_bytes<'a>(self) -> &'a [u8] {
54 std::slice::from_raw_parts(self.pointer.as_ptr(), self.len)
55 }
56}
57
58impl Default for FfiSafeBytes {
59 #[inline(always)]
60 fn default() -> Self {
61 Self {
62 pointer: NonNull::dangling(),
63 len: 0,
64 }
65 }
66}
67
68impl<'a> From<&'a [u8]> for FfiSafeBytes {
69 #[inline(always)]
70 fn from(value: &'a [u8]) -> Self {
71 Self {
72 pointer: as_non_null_ptr(value),
73 len: value.len(),
74 }
75 }
76}
77
78impl<'a> From<&'a str> for FfiSafeBytes {
79 #[inline(always)]
80 fn from(value: &'a str) -> Self {
81 Self {
82 pointer: as_non_null_ptr(value.as_bytes()),
83 len: value.len(),
84 }
85 }
86}
87
88#[repr(C)]
97#[derive(StableAbi, Clone, Copy, Debug)]
98pub struct FfiSafeStr {
99 pointer: NonNull<u8>,
100 len: usize,
101}
102
103impl FfiSafeStr {
104 #[inline(always)]
105 pub fn len(self) -> usize {
106 self.len
107 }
108
109 #[inline(always)]
110 pub fn is_empty(self) -> bool {
111 self.len == 0
112 }
113
114 #[inline(always)]
118 pub unsafe fn from_raw_parts(pointer: NonNull<u8>, len: usize) -> Self {
119 Self { pointer, len }
120 }
121
122 pub unsafe fn from_utf8_unchecked(bytes: &[u8]) -> Self {
125 let pointer = as_non_null_ptr(bytes);
126 let len = bytes.len();
127 Self { pointer, len }
128 }
129
130 #[inline(always)]
131 pub fn into_raw_parts(self) -> (*mut u8, usize) {
132 (self.pointer.as_ptr(), self.len)
133 }
134
135 #[inline]
145 pub unsafe fn as_str<'a>(self) -> &'a str {
146 if cfg!(debug_assertions) {
147 std::str::from_utf8(self.as_bytes()).expect("should only be used with valid utf8")
148 } else {
149 std::str::from_utf8_unchecked(self.as_bytes())
150 }
151 }
152
153 #[inline(always)]
163 pub unsafe fn as_bytes<'a>(self) -> &'a [u8] {
164 std::slice::from_raw_parts(self.pointer.as_ptr(), self.len)
165 }
166}
167
168impl Default for FfiSafeStr {
169 #[inline(always)]
170 fn default() -> Self {
171 Self {
172 pointer: NonNull::dangling(),
173 len: 0,
174 }
175 }
176}
177
178impl<'a> From<&'a str> for FfiSafeStr {
179 #[inline(always)]
180 fn from(value: &'a str) -> Self {
181 Self {
182 pointer: as_non_null_ptr(value.as_bytes()),
183 len: value.len(),
184 }
185 }
186}
187
188pub struct RegionGuard {
194 save_point: usize,
195}
196
197impl RegionGuard {
198 #[inline(always)]
200 #[allow(clippy::new_without_default)]
201 pub fn new() -> Self {
202 let save_point = unsafe { ffi::box_region_used() };
205 Self { save_point }
206 }
207
208 #[inline(always)]
210 pub fn used_at_creation(&self) -> usize {
211 self.save_point
212 }
213}
214
215impl Drop for RegionGuard {
216 fn drop(&mut self) {
217 unsafe { ffi::box_region_truncate(self.save_point) }
220 }
221}
222
223#[inline]
230fn allocate_on_region(size: usize) -> Result<&'static mut [u8], BoxError> {
231 let pointer = unsafe { ffi::box_region_alloc(size).cast::<u8>() };
233 if pointer.is_null() {
234 return Err(BoxError::last());
235 }
236 let region_slice = unsafe { std::slice::from_raw_parts_mut(pointer, size) };
238 Ok(region_slice)
239}
240
241#[inline]
251pub fn copy_to_region(data: &[u8]) -> Result<&'static [u8], BoxError> {
252 let region_slice = allocate_on_region(data.len())?;
253 region_slice.copy_from_slice(data);
254 Ok(region_slice)
255}
256
257pub struct RegionBuffer {
264 guard: RegionGuard,
265
266 start: *mut u8,
267 count: usize,
268}
269
270impl RegionBuffer {
271 #[inline(always)]
272 #[allow(clippy::new_without_default)]
273 pub fn new() -> Self {
274 Self {
275 guard: RegionGuard::new(),
276 start: NonNull::dangling().as_ptr(),
277 count: 0,
278 }
279 }
280
281 #[track_caller]
282 pub fn push(&mut self, data: &[u8]) -> Result<(), BoxError> {
283 let added_count = data.len();
284 let new_count = self.count + added_count;
285 unsafe {
286 let save_point = ffi::box_region_used();
287 let pointer: *mut u8 = ffi::box_region_alloc(added_count) as _;
288
289 if pointer.is_null() {
290 #[rustfmt::skip]
291 return Err(BoxError::new(TarantoolErrorCode::MemoryIssue, format!("failed to allocate {added_count} bytes on the region allocator")));
292 }
293
294 if self.start.is_null() || pointer == self.start.add(self.count) {
295 memcpy(pointer, data.as_ptr(), added_count);
297 self.count = new_count;
298 if self.start.is_null() {
299 self.start = pointer;
300 }
301 } else {
302 ffi::box_region_truncate(save_point);
304
305 let new_count = self.count + added_count;
306 let pointer: *mut u8 = ffi::box_region_alloc(new_count) as _;
307 memcpy(pointer, self.start, self.count);
308 memcpy(pointer.add(self.count), data.as_ptr(), added_count);
309 self.start = pointer;
310 self.count = new_count;
311 }
312 }
313
314 Ok(())
315 }
316
317 #[inline(always)]
318 pub fn get(&self) -> &[u8] {
319 if self.start.is_null() {
320 &[]
322 } else {
323 unsafe { std::slice::from_raw_parts(self.start, self.count) }
324 }
325 }
326
327 #[inline]
328 pub fn into_raw_parts(self) -> (&'static [u8], usize) {
329 let save_point = self.guard.used_at_creation();
330 std::mem::forget(self.guard);
331 if self.start.is_null() {
332 return (&[], save_point);
334 }
335 let slice = unsafe { std::slice::from_raw_parts(self.start, self.count) };
336 (slice, save_point)
337 }
338}
339
340impl std::io::Write for RegionBuffer {
341 #[inline(always)]
342 fn write(&mut self, data: &[u8]) -> std::io::Result<usize> {
343 if let Err(e) = self.push(data) {
344 #[rustfmt::skip]
345 return Err(std::io::Error::new(std::io::ErrorKind::OutOfMemory, e.message()));
346 }
347
348 Ok(data.len())
349 }
350
351 #[inline(always)]
352 fn flush(&mut self) -> std::io::Result<()> {
353 Ok(())
354 }
355}
356
357#[inline(always)]
358unsafe fn memcpy(destination: *mut u8, source: *const u8, count: usize) {
359 let to = std::slice::from_raw_parts_mut(destination, count);
360 let from = std::slice::from_raw_parts(source, count);
361 to.copy_from_slice(from)
362}
363
364pub struct DisplayErrorLocation<'a>(pub &'a BoxError);
370
371impl std::fmt::Display for DisplayErrorLocation<'_> {
372 #[inline]
373 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
374 if let Some((file, line)) = self.0.file().zip(self.0.line()) {
375 write!(f, "{file}:{line}: ")?;
376 }
377 Ok(())
378 }
379}
380
381pub struct DisplayAsHexBytesLimitted<'a>(pub &'a [u8]);
387
388impl std::fmt::Display for DisplayAsHexBytesLimitted<'_> {
389 #[inline]
390 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
391 if self.0.len() > 512 {
392 f.write_str("<too-big-to-display>")
393 } else {
394 tarantool::util::DisplayAsHexBytes(self.0).fmt(f)
395 }
396 }
397}
398
399#[inline(always)]
404fn as_non_null_ptr<T>(data: &[T]) -> NonNull<T> {
405 let pointer = data.as_ptr();
406 unsafe { NonNull::new_unchecked(pointer as *mut _) }
410}
411
412pub fn tarantool_error_to_box_error(e: tarantool::error::Error) -> BoxError {
414 match e {
415 tarantool::error::Error::Tarantool(e) => e,
416 other => BoxError::new(ErrorCode::Other, other.to_string()),
417 }
418}
419
420#[cfg(feature = "internal_test")]
425mod test {
426 use super::*;
427
428 #[tarantool::test]
429 fn region_buffer() {
430 #[derive(serde::Serialize, Debug)]
431 struct S {
432 name: String,
433 x: f32,
434 y: f32,
435 array: Vec<(i32, i32, bool)>,
436 }
437
438 let s = S {
439 name: "foo".into(),
440 x: 4.2,
441 y: 6.9,
442 array: vec![(1, 2, true), (3, 4, false)],
443 };
444
445 let vec = rmp_serde::to_vec(&s).unwrap();
446 let mut buffer = RegionBuffer::new();
447 rmp_serde::encode::write(&mut buffer, &s).unwrap();
448 assert_eq!(vec, buffer.get());
449 }
450}