Bytes 结构体

Tokio Bytes 结构体,廉价的可克隆和可切分的连续内存块。

https://docs.rs/bytes/1.0.1/bytes/struct.Bytes.html

文档

介绍

廉价的可克隆和可切分的连续内存块。

Bytes 是一个高效的容器,用于存储和操作连续的内存片段。它主要用于网络代码中,但也可以应用于其他地方。

通过允许多个 Bytes 对象指向相同的底层内存,Bytes 有助于零拷贝(zero-copy )网络编程。

Bytes 没有单一的实现。它是一个接口,其确切的行为是通过 Bytes 的几个底层实现中的动态调度来实现的。

所有 Bytes 的实现必须满足以下要求:

  • 它们是廉价的可克隆的,因此可以在无限多的组件之间共享,例如通过修改引用计数。
  • 实例可以被切分以引用原始缓冲区的一个子集。
use bytes::Bytes;

let mut mem = Bytes::from("Hello world");
let a = mem.slice(0..5);

assert_eq!(a, "Hello");

let b = mem.split_to(6);

assert_eq!(mem, "world");
assert_eq!(b, "Hello ");

内存布局

Bytes 结构本身相当小,仅限于4个usize字段,用于跟踪 Bytes 句柄可以访问的底层内存段的信息。

Bytes 同时保留了一个指向包含完整内存片的共享状态的指针和一个指向手柄可见区域的起始点的指针。字节还跟踪其进入内存的长度。

共享

Bytes 包含一个vtable,它允许 Bytes 的实现者详细定义共享/克隆的实现方式。当 Bytes::clone() 被调用时,Bytes 将调用 vtable 函数来克隆后备存储,以便在多个Bytes实例之间共享它。

对于指向恒定内存的 Bytes 实现(例如,通过 Bytes::from_static() 创建),克隆实现将是无消耗的。

对于指向引用计数的共享存储的字节实现(例如 Arc<[u8]>),共享将通过增加引用计数来实现。

由于这种机制,多个 Bytes 实例可以指向同一个共享内存区域。每个 Bytes 实例可以指向该内存区域中的不同部分,而且 Bytes 实例可能有也可能没有对内存的重叠视图。

下图直观地展示了这样一个场景:2个 Bytes 实例利用基于 Arc 的后端存储,并提供对不同视图的访问。

   Arc ptrs                   +---------+
   ________________________ / | Bytes 2 |
  /                           +---------+
 /          +-----------+     |         |
|_________/ |  Bytes 1  |     |         |
|           +-----------+     |         |
|           |           | ___/ data     | tail
|      data |      tail |/              |
v           v           v               v
+-----+---------------------------------+-----+
| Arc |     |           |               |     |
+-----+---------------------------------+-----+

源码

结构体定义

pub struct Bytes {
    ptr: *const u8,
    len: usize,
    // inlined "trait object"
    data: AtomicPtr<()>,
    vtable: &'static Vtable,
}

pub(crate) struct Vtable {
    /// fn(data, ptr, len)
    pub clone: unsafe fn(&AtomicPtr<()>, *const u8, usize) -> Bytes,
    /// fn(data, ptr, len)
    pub drop: unsafe fn(&mut AtomicPtr<()>, *const u8, usize),
}

构造方法

Bytes::new() 方法创建一个新的空Bytes。这将不会分配,返回的字节句柄将是空的。

pub const fn new() -> Bytes {
    // Make it a named const to work around
    // "unsizing casts are not allowed in const fn"
    const EMPTY: &[u8] = &[];
    Bytes::from_static(EMPTY)
}

Bytes::from_static 从一个静态片断创建一个新的Bytes。返回的 Bytes 将直接指向静态片断。无需分配或复制。

pub const fn from_static(bytes: &'static [u8]) -> Bytes {
    Bytes {
        ptr: bytes.as_ptr(),
        len: bytes.len(),
        data: AtomicPtr::new(ptr::null_mut()),
        vtable: &STATIC_VTABLE,
    }
}

Bytes::copy_from_slice 通过复制,从 slice 中创建 Bytes 实例。

pub fn copy_from_slice(data: &[u8]) -> Self {
    data.to_vec().into()
}

slice()

slice()

返回所提供范围内的自己的一个片断。

这将增加底层内存的引用计数,并返回一个新的字节句柄,设置为该分片。

这个操作是O(1)。

use bytes::Bytes;

let a = Bytes::from(&b"hello world"[..]);
let b = a.slice(2..5);

assert_eq!(&b[..], b"llo");

代码实现:

pub fn slice(&self, range: impl RangeBounds<usize>) -> Bytes {
    use core::ops::Bound;

    let len = self.len();

    let begin = match range.start_bound() {
        Bound::Included(&n) => n,
        Bound::Excluded(&n) => n + 1,
        Bound::Unbounded => 0,
    };

    let end = match range.end_bound() {
        Bound::Included(&n) => n.checked_add(1).expect("out of range"),
        Bound::Excluded(&n) => n,
        Bound::Unbounded => len,
    };

    assert!(
        begin <= end,
        "range start must not be greater than end: {:?} <= {:?}",
        begin,
        end,
    );
    assert!(
        end <= len,
        "range end out of bounds: {:?} <= {:?}",
        end,
        len,
    );

    if end == begin {
        return Bytes::new();
    }

    // clone一个新 Bytes
    let mut ret = self.clone();

    // 新 Bytes 的长度为 end - begin
    ret.len = end - begin;
    // 新 Bytes 的 ptr 指向原来的 prt + 偏移量begin
    ret.ptr = unsafe { ret.ptr.offset(begin as isize) };

    ret
}

slice_ref()

返回一个相当于给定子集的自我片断。

当用其他工具处理 Bytes 缓冲区时,通常会得到一个 &[u8],它实际上是 Bytes 的一个slice,也就是它的一个子集。这个函数将 &[u8] 变成另一个 Bytes,就像我们调用 self.slice() 的偏移量来对应子集一样。

这个操作是O(1)。

use bytes::Bytes;

let bytes = Bytes::from(&b"012345678"[..]);
let as_slice = bytes.as_ref();
let subset = &as_slice[2..6];
let subslice = bytes.slice_ref(&subset);
assert_eq!(&subslice[..], b"2345");

代码实现:

pub fn slice_ref(&self, subset: &[u8]) -> Bytes {
    // Empty slice and empty Bytes may have their pointers reset
    // so explicitly allow empty slice to be a subslice of any slice.
    if subset.is_empty() {
        return Bytes::new();
    }

    let bytes_p = self.as_ptr() as usize;
    let bytes_len = self.len();

    let sub_p = subset.as_ptr() as usize;
    let sub_len = subset.len();

    assert!(
        sub_p >= bytes_p,
        "subset pointer ({:p}) is smaller than self pointer ({:p})",
        sub_p as *const u8,
        bytes_p as *const u8,
    );
    assert!(
        sub_p + sub_len <= bytes_p + bytes_len,
        "subset is out of bounds: self = ({:p}, {}), subset = ({:p}, {})",
        bytes_p as *const u8,
        bytes_len,
        sub_p as *const u8,
        sub_len,
    );

    let sub_offset = sub_p - bytes_p;

    self.slice(sub_offset..(sub_offset + sub_len))
}

split()

split_off()

在给定的索引处将字节分成两个。

之后self包含元素 [0, at),而返回的Bytes包含元素 [at, len)

这是一个O(1)操作,只是增加了引用计数并设置了一些索引。

use bytes::Bytes;

let mut a = Bytes::from(&b"hello world"[..]);
let b = a.split_off(5);

assert_eq!(&a[..], b"hello");
assert_eq!(&b[..], b" world");

代码实现:

pub fn split_off(&mut self, at: usize) -> Bytes {
    assert!(
        at <= self.len(),
        "split_off out of bounds: {:?} <= {:?}",
        at,
        self.len(),
    );

    if at == self.len() {
        return Bytes::new();
    }

    if at == 0 {
        return mem::replace(self, Bytes::new());
    }

    let mut ret = self.clone();

    self.len = at;

    unsafe { ret.inc_start(at) };

    ret
}

split_to()

在给定的索引处将字节分成两个。

之后self包含元素 [at, len),而返回的 Bytes包含元素 [0, at)

这是一个O(1)操作,只是增加了引用计数和设置了一些索引。

use bytes::Bytes;

let mut a = Bytes::from(&b"hello world"[..]);
let b = a.split_to(5);

assert_eq!(&a[..], b" world");
assert_eq!(&b[..], b"hello");

代码实现:

pub fn split_to(&mut self, at: usize) -> Bytes {
    assert!(
        at <= self.len(),
        "split_to out of bounds: {:?} <= {:?}",
        at,
        self.len(),
    );

    if at == self.len() {
        return mem::replace(self, Bytes::new());
    }

    if at == 0 {
        return Bytes::new();
    }

    let mut ret = self.clone();

    unsafe { self.inc_start(at) };

    ret.len = at;
    ret
}

truncate()

缩短缓冲区的长度,保留第一个len字节,放弃其余的字节。

如果len大于缓冲区的当前长度,则没有影响。

split_off 方法可以模拟Truncate,但是这导致多余的字节被返回而不是丢弃。

use bytes::Bytes;

let mut buf = Bytes::from(&b"hello world"[..]);
buf.truncate(5);
assert_eq!(buf, b"hello"[..]);

代码实现:

pub fn truncate(&mut self, len: usize) {
    if len < self.len {
        // The Vec "promotable" vtables do not store the capacity,
        // so we cannot truncate while using this repr. We *have* to
        // promote using `split_off` so the capacity can be stored.
        if self.vtable as *const Vtable == &PROMOTABLE_EVEN_VTABLE
            || self.vtable as *const Vtable == &PROMOTABLE_ODD_VTABLE
        {
            drop(self.split_off(len));
        } else {
            self.len = len;
        }
    }
}

clear()

清除缓冲区,删除所有数据。

use bytes::Bytes;

let mut buf = Bytes::from(&b"hello world"[..]);
buf.clear();
assert!(buf.is_empty());

代码实现:

pub fn clear(&mut self) {
    self.truncate(0);
}