use bytes::{Buf, BufMut, Bytes, BytesMut};
use crate::alloc::vec::Vec;
pub trait EncodeSink {
const IS_SEGMENTED: bool = false;
fn put_u8(&mut self, value: u8);
fn put_slice(&mut self, src: &[u8]);
fn put_u32_le(&mut self, value: u32);
fn put_u64_le(&mut self, value: u64);
#[inline]
fn put_i32_le(&mut self, value: i32) {
#[allow(clippy::cast_sign_loss)]
self.put_u32_le(value as u32);
}
#[inline]
fn put_i64_le(&mut self, value: i64) {
#[allow(clippy::cast_sign_loss)]
self.put_u64_le(value as u64);
}
#[inline]
fn put_f32_le(&mut self, value: f32) {
self.put_u32_le(value.to_bits());
}
#[inline]
fn put_f64_le(&mut self, value: f64) {
self.put_u64_le(value.to_bits());
}
#[inline]
fn put_shared(&mut self, bytes: Bytes) {
self.put_slice(&bytes);
}
}
impl<T: BufMut + ?Sized> EncodeSink for T {
#[inline]
fn put_u8(&mut self, value: u8) {
BufMut::put_u8(self, value);
}
#[inline]
fn put_slice(&mut self, src: &[u8]) {
BufMut::put_slice(self, src);
}
#[inline]
fn put_u32_le(&mut self, value: u32) {
BufMut::put_u32_le(self, value);
}
#[inline]
fn put_u64_le(&mut self, value: u64) {
BufMut::put_u64_le(self, value);
}
}
pub const DEFAULT_MIN_SEGMENT: usize = 4 * 1024;
#[derive(Debug)]
pub struct Rope {
segments: Vec<Bytes>,
tail: BytesMut,
backing: Option<Bytes>,
min_segment: usize,
}
impl Default for Rope {
fn default() -> Self {
Self::new()
}
}
impl Rope {
#[must_use]
pub fn new() -> Self {
Self::with_min_segment(DEFAULT_MIN_SEGMENT)
}
#[must_use]
pub fn with_min_segment(min_segment: usize) -> Self {
Self {
segments: Vec::new(),
tail: BytesMut::new(),
backing: None,
min_segment: min_segment.max(1),
}
}
#[must_use]
pub fn with_backing(mut self, backing: Bytes) -> Self {
self.backing = Some(backing);
self
}
#[must_use]
pub fn len(&self) -> usize {
self.segments.iter().map(Bytes::len).sum::<usize>() + self.tail.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.tail.is_empty() && self.segments.is_empty()
}
#[must_use]
pub fn segment_count(&self) -> usize {
self.segments.len() + usize::from(!self.tail.is_empty())
}
#[must_use]
pub fn into_segments(mut self) -> Vec<Bytes> {
self.flush_tail();
self.segments
}
#[must_use]
pub fn to_contiguous_bytes(&self) -> Bytes {
let mut out = BytesMut::with_capacity(self.len());
for segment in &self.segments {
BufMut::put_slice(&mut out, segment);
}
BufMut::put_slice(&mut out, &self.tail);
out.freeze()
}
fn flush_tail(&mut self) {
if !self.tail.is_empty() {
self.segments.push(self.tail.split().freeze());
}
}
}
impl EncodeSink for Rope {
const IS_SEGMENTED: bool = true;
#[inline]
fn put_u8(&mut self, value: u8) {
BufMut::put_u8(&mut self.tail, value);
}
#[inline]
fn put_slice(&mut self, src: &[u8]) {
if src.len() >= self.min_segment {
if let Some(segment) = self
.backing
.as_ref()
.and_then(|b| crate::view::try_slice_ref(b, src))
{
self.flush_tail();
self.segments.push(segment);
return;
}
}
BufMut::put_slice(&mut self.tail, src);
}
#[inline]
fn put_u32_le(&mut self, value: u32) {
BufMut::put_u32_le(&mut self.tail, value);
}
#[inline]
fn put_u64_le(&mut self, value: u64) {
BufMut::put_u64_le(&mut self.tail, value);
}
#[inline]
fn put_shared(&mut self, bytes: Bytes) {
if bytes.len() >= self.min_segment {
self.flush_tail();
self.segments.push(bytes);
} else {
BufMut::put_slice(&mut self.tail, &bytes);
}
}
}
impl EncodeSink for &mut Rope {
const IS_SEGMENTED: bool = true;
#[inline]
fn put_u8(&mut self, value: u8) {
Rope::put_u8(self, value);
}
#[inline]
fn put_slice(&mut self, src: &[u8]) {
Rope::put_slice(self, src);
}
#[inline]
fn put_u32_le(&mut self, value: u32) {
Rope::put_u32_le(self, value);
}
#[inline]
fn put_u64_le(&mut self, value: u64) {
Rope::put_u64_le(self, value);
}
#[inline]
fn put_shared(&mut self, bytes: Bytes) {
Rope::put_shared(self, bytes);
}
}
#[derive(Debug)]
pub struct RopeBuf {
segments: Vec<Bytes>,
pos: usize,
remaining: usize,
}
impl From<Rope> for RopeBuf {
fn from(rope: Rope) -> Self {
let segments = rope.into_segments();
let remaining = segments.iter().map(Bytes::len).sum();
Self {
segments,
pos: 0,
remaining,
}
}
}
impl Buf for RopeBuf {
fn remaining(&self) -> usize {
self.remaining
}
fn chunk(&self) -> &[u8] {
self.segments.get(self.pos).map_or(&[], |b| &b[..])
}
fn advance(&mut self, mut cnt: usize) {
assert!(cnt <= self.remaining, "advance past end of RopeBuf");
self.remaining -= cnt;
while cnt > 0 {
let front = &mut self.segments[self.pos];
if cnt < front.len() {
front.advance(cnt);
return;
}
cnt -= front.len();
*front = Bytes::new();
self.pos += 1;
}
}
#[cfg(feature = "std")]
fn chunks_vectored<'a>(&'a self, dst: &mut [std::io::IoSlice<'a>]) -> usize {
let mut n = 0;
for segment in &self.segments[self.pos..] {
if n == dst.len() {
break;
}
debug_assert!(!segment.is_empty());
dst[n] = std::io::IoSlice::new(segment);
n += 1;
}
n
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_matches_new() {
let mut rope = Rope::default();
rope.put_shared(Bytes::from_static(b"tiny"));
rope.put_shared(Bytes::new());
assert_eq!(rope.segment_count(), 1);
let segments = rope.into_segments();
assert_eq!(segments.len(), 1);
assert!(!segments[0].is_empty());
}
#[test]
fn mut_ref_forwarding_impl() {
fn write_through(mut sink: impl EncodeSink) {
sink.put_slice(b"via reborrow");
sink.put_shared(Bytes::from(crate::alloc::vec![9u8; 64]));
}
let mut rope = Rope::with_min_segment(64);
write_through(&mut rope);
assert_eq!(rope.segment_count(), 2);
assert_eq!(rope.len(), 12 + 64);
}
#[test]
fn advance_releases_consumed_segments() {
let payload = Bytes::from(crate::alloc::vec![3u8; 128]);
let mut rope = Rope::with_min_segment(64);
rope.put_shared(payload.clone());
rope.put_slice(b"after");
let mut buf = RopeBuf::from(rope);
assert!(payload.clone().try_into_mut().is_err(), "not unique yet");
buf.advance(128);
assert!(
payload.try_into_mut().is_ok(),
"consumed segment must be released before RopeBuf drop"
);
assert_eq!(buf.copy_to_bytes(buf.remaining()), &b"after"[..]);
}
#[test]
fn small_writes_coalesce() {
let mut rope = Rope::with_min_segment(16);
rope.put_u8(1);
rope.put_slice(b"abc");
rope.put_u32_le(7);
rope.put_u64_le(9);
assert_eq!(rope.segment_count(), 1);
let segments = rope.into_segments();
assert_eq!(segments.len(), 1);
assert_eq!(segments[0].len(), 1 + 3 + 4 + 8);
}
#[test]
fn large_shared_segment_is_not_copied() {
let payload = Bytes::from(crate::alloc::vec![0xAB; 64]);
let ptr = payload.as_ptr();
let mut rope = Rope::with_min_segment(64);
rope.put_slice(b"head");
rope.put_shared(payload);
rope.put_slice(b"tail");
let segments = rope.into_segments();
assert_eq!(segments.len(), 3);
assert_eq!(&segments[0][..], b"head");
assert!(core::ptr::eq(segments[1].as_ptr(), ptr), "must not copy");
assert_eq!(&segments[2][..], b"tail");
}
#[test]
fn small_shared_write_coalesces() {
let mut rope = Rope::with_min_segment(64);
rope.put_slice(b"a");
rope.put_shared(Bytes::from_static(b"bc"));
rope.put_slice(b"d");
let segments = rope.into_segments();
assert_eq!(segments.len(), 1);
assert_eq!(&segments[0][..], b"abcd");
}
#[test]
fn backed_slice_is_captured_zero_copy() {
let backing = Bytes::from(crate::alloc::vec![0x5A; 256]);
let inside: &[u8] = &backing[32..224];
let outside = crate::alloc::vec![0x5A; 192];
let mut rope = Rope::with_min_segment(64).with_backing(backing.clone());
rope.put_slice(inside);
rope.put_slice(&outside);
let segments = rope.into_segments();
assert_eq!(segments.len(), 2);
assert!(
core::ptr::eq(segments[0].as_ptr(), inside.as_ptr()),
"backed slice must be zero-copy"
);
assert!(
!core::ptr::eq(segments[1].as_ptr(), outside.as_ptr()),
"unbacked slice must be copied"
);
assert_eq!(&segments[1][..], &outside[..]);
}
#[test]
fn segments_concatenate_to_contiguous_output() {
let payload = Bytes::from(crate::alloc::vec![0x11; 128]);
let mut contiguous: Vec<u8> = Vec::new();
EncodeSink::put_u8(&mut contiguous, 0x0A);
EncodeSink::put_slice(&mut contiguous, &payload);
EncodeSink::put_u32_le(&mut contiguous, 42);
let mut rope = Rope::with_min_segment(64);
rope.put_u8(0x0A);
rope.put_shared(payload);
rope.put_u32_le(42);
assert_eq!(&rope.to_contiguous_bytes()[..], &contiguous[..]);
}
#[test]
fn rope_buf_traverses_segments() {
let mut rope = Rope::with_min_segment(4);
rope.put_slice(b"ab");
rope.put_shared(Bytes::from_static(b"cdefgh"));
rope.put_slice(b"ij");
let mut buf = RopeBuf::from(rope);
assert_eq!(buf.remaining(), 10);
assert_eq!(buf.chunk(), b"ab");
buf.advance(3); assert_eq!(buf.chunk(), b"defgh");
let rest = buf.copy_to_bytes(buf.remaining());
assert_eq!(&rest[..], b"defghij");
}
#[test]
fn max_threshold_never_segments() {
let mut rope = Rope::with_min_segment(usize::MAX);
rope.put_shared(Bytes::from(crate::alloc::vec![1u8; 1024]));
rope.put_slice(&crate::alloc::vec![2u8; 1024]);
assert_eq!(rope.segment_count(), 1);
}
}