vy_core/
buffer.rs

1#![allow(unknown_lints)]
2#![allow(clippy::manual_dangling_ptr)]
3
4// The MIT License (MIT)
5// Copyright (c) 2020 Ryohei Machida
6//
7// Permission is hereby granted, free of charge, to any person obtaining a
8// copy of this software and associated documentation files (the
9// "Software"), to deal in the Software without restriction, including
10// without limitation the rights to use, copy, modify, merge, publish,
11// distribute, sublicense, and/or sell copies of the Software, and to
12// permit persons to whom the Software is furnished to do so, subject to
13// the following conditions:
14//
15// The above copyright notice and this permission notice shall be included
16// in all copies or substantial portions of the Software.
17//
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26use alloc::{
27    alloc::{alloc, dealloc, handle_alloc_error, realloc},
28    boxed::Box,
29    string::String,
30};
31use core::{
32    alloc::Layout,
33    cmp, fmt,
34    mem::ManuallyDrop,
35    ops::{Add, AddAssign},
36    ptr, slice, str,
37};
38
39/// Buffer for rendered contents
40///
41/// This struct is quite simular to `String`, but some methods are
42/// re-implemented for faster buffering.
43pub struct Buffer {
44    data: *mut u8,
45    len: usize,
46    capacity: usize,
47}
48
49impl Buffer {
50    /// Create an empty buffer
51    #[inline]
52    pub const fn new() -> Buffer {
53        Self {
54            data: align_of::<u8>() as *mut u8, // dangling pointer
55            len: 0,
56            capacity: 0,
57        }
58    }
59
60    /// Create a empty buffer with a particular capacity
61    #[inline]
62    pub fn with_capacity(n: usize) -> Buffer {
63        if n == 0 {
64            Self::new()
65        } else {
66            Self {
67                data: safe_alloc(n),
68                len: 0,
69                capacity: n,
70            }
71        }
72    }
73
74    /// Extracts a string slice containing the entire buffer
75    #[inline]
76    pub fn as_str(&self) -> &str {
77        unsafe {
78            let bytes = slice::from_raw_parts(self.data, self.len);
79            str::from_utf8_unchecked(bytes)
80        }
81    }
82
83    /// Returns an unsafe mutable pointer to the inner data
84    #[inline]
85    pub fn as_mut_ptr(&self) -> *mut u8 {
86        self.data
87    }
88
89    /// Returns the length of this buffer in bytes
90    #[inline]
91    pub fn len(&self) -> usize {
92        self.len
93    }
94
95    /// Returns this buffer's capacity in bytes
96    #[inline]
97    pub fn capacity(&self) -> usize {
98        self.capacity
99    }
100
101    #[inline]
102    #[doc(hidden)]
103    pub unsafe fn _set_len(&mut self, new_len: usize) {
104        debug_assert!(new_len <= self.capacity);
105        self.len = new_len;
106    }
107
108    /// Increase the length of buffer by `additional` bytes
109    ///
110    /// # Safety
111    ///
112    /// - `additional` must be less than or equal to `capacity() - len()`
113    /// - The elements at `old_len..old_len + additional` must be initialized
114    #[inline]
115    pub unsafe fn advance(&mut self, additional: usize) {
116        self.len += additional;
117    }
118
119    /// Returns `true` if this buffer has a length of zero, and `false`
120    /// otherwise
121    #[inline]
122    pub fn is_empty(&self) -> bool {
123        self.len == 0
124    }
125
126    /// Same as String::reserve
127    ///
128    /// # Panics
129    ///
130    /// This method panics if `size` overflows `isize::MAX`.
131    #[inline]
132    pub fn reserve(&mut self, size: usize) {
133        if size <= self.capacity - self.len {
134            return;
135        }
136        self.reserve_internal(size);
137    }
138
139    /// Same as String::reserve except that undefined behaviour can result if
140    /// `size` overflows `isize::MAX`.
141    #[inline]
142    pub(crate) unsafe fn reserve_small(&mut self, size: usize) {
143        debug_assert!(size <= isize::MAX as usize);
144        if self.len + size <= self.capacity {
145            return;
146        }
147        self.reserve_internal(size);
148    }
149
150    #[inline]
151    #[doc(hidden)]
152    pub fn clear(&mut self) {
153        self.len = 0;
154    }
155
156    /// Converts a `Buffer` into a `String` without copy/realloc operation.
157    #[inline]
158    pub fn into_string(self) -> String {
159        debug_assert!(self.len <= self.capacity);
160        let buf = ManuallyDrop::new(self);
161
162        // SAFETY: This operations satisfy all requirements specified in
163        // https://doc.rust-lang.org/std/string/struct.String.html#safety
164        unsafe { String::from_raw_parts(buf.data, buf.len, buf.capacity) }
165    }
166
167    /// Appends a given string slice onto the end of this buffer
168    #[inline]
169    pub fn push_str(&mut self, data: &str) {
170        let size = data.len();
171
172        unsafe {
173            // SAFETY: this operation won't overflow because slice cannot
174            // exceeds isize::MAX bytes.
175            // https://doc.rust-lang.org/reference/behavior-considered-undefined.html
176            self.reserve_small(size);
177
178            let p = self.data.add(self.len);
179            ptr::copy_nonoverlapping(data.as_ptr(), p, size);
180            self.len += size;
181        }
182        debug_assert!(self.len <= self.capacity);
183    }
184
185    /// Appends the given `char` to the end of this buffer
186    #[inline]
187    pub fn push(&mut self, data: char) {
188        // Question: Is it safe to pass uninitialized memory to `encode_utf8`
189        // function?
190        unsafe {
191            self.reserve_small(4);
192            let bp = self.data.add(self.len) as *mut [u8; 4];
193            let result = data.encode_utf8(&mut *bp);
194            self.len += result.len();
195        }
196    }
197
198    #[inline]
199    fn reserve_internal(&mut self, size: usize) {
200        debug_assert!(size <= isize::MAX as usize);
201
202        let new_capacity = cmp::max(self.capacity * 2, self.capacity + size);
203        debug_assert!(new_capacity > self.capacity);
204        self.data =
205            unsafe { safe_realloc(self.data, self.capacity, new_capacity) };
206        self.capacity = new_capacity;
207
208        debug_assert!(!self.data.is_null());
209        debug_assert!(self.len <= self.capacity);
210    }
211}
212
213#[inline(never)]
214fn safe_alloc(capacity: usize) -> *mut u8 {
215    assert!(capacity > 0);
216    assert!(capacity <= isize::MAX as usize, "capacity is too large");
217
218    // SAFETY: capacity is non-zero, and always multiple of alignment (1).
219    unsafe {
220        let layout = Layout::from_size_align_unchecked(capacity, 1);
221        let data = alloc(layout);
222        if data.is_null() {
223            handle_alloc_error(layout);
224        }
225
226        data
227    }
228}
229
230/// # Safety
231///
232/// - if `capacity > 0`, `capacity` is the same value that was used to allocate
233///   the block of memory pointed by `ptr`.
234#[cold]
235#[inline(never)]
236unsafe fn safe_realloc(
237    ptr: *mut u8,
238    capacity: usize,
239    new_capacity: usize,
240) -> *mut u8 {
241    assert!(new_capacity > 0);
242    assert!(new_capacity <= isize::MAX as usize, "capacity is too large");
243
244    let data = if capacity == 0 {
245        let new_layout = Layout::from_size_align_unchecked(new_capacity, 1);
246        alloc(new_layout)
247    } else {
248        let old_layout = Layout::from_size_align_unchecked(capacity, 1);
249        realloc(ptr, old_layout, new_capacity)
250    };
251
252    if data.is_null() {
253        handle_alloc_error(Layout::from_size_align_unchecked(new_capacity, 1));
254    }
255
256    data
257}
258
259impl Clone for Buffer {
260    fn clone(&self) -> Self {
261        unsafe {
262            if self.is_empty() {
263                Self::new()
264            } else {
265                let buf = Self {
266                    data: safe_alloc(self.len),
267                    len: self.len,
268                    capacity: self.len,
269                };
270
271                ptr::copy_nonoverlapping(self.data, buf.data, self.len);
272                buf
273            }
274        }
275    }
276}
277
278impl fmt::Debug for Buffer {
279    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
280        self.as_str().fmt(f)
281    }
282}
283
284impl Drop for Buffer {
285    fn drop(&mut self) {
286        if self.capacity != 0 {
287            // SAFETY: when `self.capacity > 0`, `self.capacity` is the same
288            // value used for allocate the block of memory pointed
289            // by `self.data`.
290            unsafe {
291                let layout =
292                    Layout::from_size_align_unchecked(self.capacity, 1);
293                dealloc(self.data, layout);
294            }
295        }
296    }
297}
298
299impl fmt::Write for Buffer {
300    #[inline]
301    fn write_str(&mut self, s: &str) -> fmt::Result {
302        Buffer::push_str(self, s);
303        Ok(())
304    }
305}
306
307impl From<String> for Buffer {
308    /// Shrink the data and pass raw pointer directory to buffer
309    ///
310    /// This operation is `O(1)`
311    #[inline]
312    fn from(other: String) -> Buffer {
313        let bs = other.into_boxed_str();
314        let data = Box::leak(bs);
315        Buffer {
316            data: data.as_mut_ptr(),
317            len: data.len(),
318            capacity: data.len(),
319        }
320    }
321}
322
323impl From<&str> for Buffer {
324    #[inline]
325    fn from(other: &str) -> Buffer {
326        let mut buf = Buffer::with_capacity(other.len());
327
328        if !other.is_empty() {
329            // SAFETY: `Buffer.capacity()` should be same as `other.len()`, so
330            // if `other` is not empty, `buf.as_mut_ptr()` is
331            // supporsed to point to valid memory.
332            unsafe {
333                ptr::copy_nonoverlapping(
334                    other.as_ptr(),
335                    buf.as_mut_ptr(),
336                    other.len(),
337                );
338                buf.advance(other.len());
339            }
340        }
341
342        buf
343    }
344}
345
346impl Add<&str> for Buffer {
347    type Output = Buffer;
348
349    #[inline]
350    fn add(mut self, other: &str) -> Buffer {
351        self.push_str(other);
352        self
353    }
354}
355
356impl AddAssign<&str> for Buffer {
357    #[inline]
358    fn add_assign(&mut self, other: &str) {
359        self.push_str(other)
360    }
361}
362
363impl Default for Buffer {
364    #[inline]
365    fn default() -> Buffer {
366        Buffer::new()
367    }
368}
369
370unsafe impl Send for Buffer {}
371unsafe impl Sync for Buffer {}
372
373#[cfg(test)]
374mod tests {
375    use alloc::vec::Vec;
376    use std::{
377        sync::{Arc, Barrier, Mutex},
378        thread,
379    };
380
381    use super::*;
382
383    #[test]
384    fn push_str() {
385        let mut buffer = Buffer::new();
386        assert_eq!(buffer.len(), 0);
387        assert_eq!(buffer.capacity(), 0);
388
389        buffer.push_str("apple");
390        assert_eq!(buffer.len(), 5);
391        assert_eq!(buffer.capacity(), 5);
392
393        buffer.push_str("pie");
394        assert_eq!(buffer.len(), 8);
395        assert_eq!(buffer.capacity(), 10);
396
397        for _ in 0..16 {
398            buffer.push_str("zomg");
399        }
400
401        assert_eq!(buffer.len(), 72);
402        assert_eq!(buffer.capacity(), 80);
403    }
404
405    #[test]
406    fn with_capacity() {
407        let buffer = Buffer::with_capacity(1);
408        assert!(buffer.is_empty());
409        assert_eq!(buffer.len(), 0);
410        assert!(buffer.capacity() >= 1);
411    }
412
413    #[test]
414    fn string_conversion() {
415        // from empty string
416        let s = String::with_capacity(2);
417        assert!(s.capacity() >= 2);
418
419        let mut buf = Buffer::from(s);
420        assert_eq!(buf.as_str(), "");
421
422        // capacity should be shrinked for safety
423        assert_eq!(buf.capacity(), 0);
424
425        buf.push_str("abc");
426        assert_eq!(buf.as_str(), "abc");
427
428        // into non-empty string
429        let mut s = buf.into_string();
430        assert_eq!(s, "abc");
431
432        s += "defghijklmn";
433        assert_eq!(s, "abcdefghijklmn");
434
435        // from non-empty string
436        let mut buf = Buffer::from(s);
437        assert_eq!(buf.as_str(), "abcdefghijklmn");
438        buf.clear();
439        assert_eq!(buf.as_str(), "");
440
441        // into empty string
442        let buf = Buffer::default();
443        let mut s = buf.into_string();
444        assert_eq!(s, "");
445
446        s.push_str("apple");
447        assert_eq!(s, "apple");
448    }
449
450    #[test]
451    fn from_str() {
452        let buf = Buffer::from("abcdefgh");
453        assert_eq!(buf.as_str(), "abcdefgh");
454    }
455
456    #[test]
457    fn clone() {
458        use core::fmt::Write;
459
460        let mut s1 = Buffer::with_capacity(0);
461        let mut s2 = s1.clone();
462
463        s1.push('a');
464        s2.push_str("b");
465
466        assert_eq!(s1.as_str(), "a");
467        assert_eq!(s2.as_str(), "b");
468
469        let mut s1 = Buffer::from("foo");
470        let mut s2 = s1.clone();
471
472        s1 += "bar";
473        write!(s2, "baz").unwrap();
474
475        assert_eq!(s1.as_str(), "foobar");
476        assert_eq!(s2.as_str(), "foobaz");
477
478        s2.clear();
479        let _ = s2.clone();
480    }
481
482    #[test]
483    fn push() {
484        for initial_capacity in &[0, 4, 16] {
485            let mut s = Buffer::with_capacity(*initial_capacity);
486
487            s.push('a');
488            s.push('é');
489            s.push('A');
490            s.push('🄫');
491
492            assert_eq!(s.as_str(), "aéA🄫");
493        }
494    }
495
496    #[test]
497    fn multi_thread() {
498        const THREADS: usize = 8;
499        const ITERS: usize = 100;
500
501        let barrier = Arc::new(Barrier::new(THREADS));
502        let buffer = Arc::new(Mutex::new(Buffer::new()));
503        let mut handles = Vec::with_capacity(THREADS);
504
505        for _ in 0..THREADS {
506            let barrier = barrier.clone();
507            let buffer = buffer.clone();
508
509            handles.push(thread::spawn(move || {
510                barrier.wait();
511                for _ in 0..ITERS {
512                    buffer.lock().unwrap().push_str("a");
513                }
514            }));
515        }
516
517        for handle in handles {
518            handle.join().unwrap();
519        }
520
521        assert_eq!(
522            buffer.lock().unwrap().as_str(),
523            "a".repeat(ITERS * THREADS)
524        );
525    }
526
527    #[test]
528    #[should_panic]
529    fn reserve_overflow() {
530        let mut buf = Buffer::new();
531        buf.reserve(isize::MAX as usize + 1);
532    }
533
534    #[test]
535    #[should_panic]
536    fn empty_alloc() {
537        safe_alloc(0);
538    }
539}