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
/* SliceDeque is quite inconvenient, e.g.: - you still need to resort to `unsafe {}` to append data through `&mut [u8]` and advance tail position accordingly, - you still need to be careful with pointers to head/tail and actively prevent underruns/overruns, - borrow checker still messes up reader methods (e.g. `buf.move_head()` after `buf.as_slice()`). Hence we drop to a lower level thing. But even this lower level buffer is not without its own warts: it operates with an overall size of a mirrored buffer, not one of its halves, which results in a lot of unnecessary `* 2` on the user's side and lots of `/ 2`, `% 2`, and `assert!`s in the crate that implements it. And, yes, this is just utterly confusing: why report len() of X when you can only put X/2 elements inside? */ use slice_deque::{Buffer, AllocError}; /// Buffer that uses circular buffer implemented with mirrored memory maps pub struct MmapBuffer { buf: Buffer<u8>, /* We keep size of a `buf`'s size on our own because `buf.len()`: - returns twice the amount of data buffer can actually handle (i.e. size of mmaped region with two mirrors), which makes it confusing, and it's also an error waiting to happen, - it also causes an immutable borrowing of `buf`, thus making most of manipulations with `buf`'s content inconvenient. */ bufsize: usize, // position of data within the `buf` start: usize, len: usize, } impl super::Buffer for MmapBuffer { type Error = AllocError; fn new(size: usize) -> Result<Self, AllocError> { let buf = Buffer::uninitialized(size * 2)?; // slice-deque will round bufsize to the nearest page size or something, // so we query it back here let bufsize = buf.len() / 2; Ok(MmapBuffer { buf, bufsize, start: 0, len: 0, }) } fn filled(&self) -> &[u8] { &(unsafe { self.buf.as_slice() })[ self.start .. (self.start + self.len) ] } // make room for new data one way or the other fn enlarge(&mut self) -> Result<(), AllocError> { if self.start == 0 && self.len == self.bufsize { /* we used to have configurable increments for the bufsize now though we double buffer size, just like rust's vec/raw_vec do */ self.bufsize *= 2; let mut new = Buffer::uninitialized(self.bufsize * 2)?; // see .new() for th reasons why we read bufsize back self.bufsize = new.len() / 2; // move data at the start of new buffer unsafe { core::ptr::copy( self.buf.as_mut_slice()[self.start..].as_mut_ptr(), new.as_mut_slice().as_mut_ptr(), self.len, ); } self.start = 0; self.buf = new; } else { // there's plenty of room in the buffer, // nothing to do here } Ok(()) } /* return b-through-a: | a--b | a--b | |-b a-|-b a-| */ fn appendable(&mut self) -> &mut [u8] { let end = self.start + self.len; let remaining = self.bufsize - self.len; &mut (unsafe { self.buf.as_mut_slice() })[ end .. (end+remaining) ] } fn grow(&mut self, amount: usize) { self.len += amount; } /* returns reference to first half of the buffer up to the size of `amount`, which is going to be discarded after lifetime of returned slice comes to an end */ fn consume(&mut self, amount: usize) -> &[u8] { let start = self.start; let amount = std::cmp::min(amount, self.len()); self.start += amount; if self.start >= self.bufsize { // keep self.start within bufsize self.start -= self.bufsize; } self.len -= amount; &(unsafe { self.buf.as_mut_slice() })[ start .. (start+amount) ] } fn len(&self) -> usize { self.len } }