Skip to content

Commit

Permalink
ds: 统一从ring_slice中读取number的策略
Browse files Browse the repository at this point in the history
  • Loading branch information
icycrystal4 committed Oct 31, 2023
1 parent 8c836d1 commit b0f30b1
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 5 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ members = [
"metrics",
"ds",
"rt",
"procs",
"tests",
"tests_integration",
]
Expand Down
1 change: 1 addition & 0 deletions ds/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"

[dependencies]
log = { path = "../log" }
procs = { path = "../procs" }
minstant = "*"
tokio.workspace = true

Expand Down
24 changes: 24 additions & 0 deletions ds/src/mem/bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::RingSlice;
use procs::impl_number_ringslice;
// 如果方法名中没有包含be或者le,则默认为be
#[impl_number_ringslice(default = "be")]
pub trait ByteOrder {
fn u16_le(&self, oft: usize) -> u16;
fn i16_le(&self, oft: usize) -> i16;
fn u24_le(&self, oft: usize) -> u32;
fn i24_le(&self, oft: usize) -> i32;
fn u32_le(&self, oft: usize) -> u32;
fn i32_le(&self, oft: usize) -> i32;
fn u40_le(&self, oft: usize) -> u64;
fn i40_le(&self, oft: usize) -> i64;
fn u48_le(&self, oft: usize) -> u64;
fn i48_le(&self, oft: usize) -> i64;
fn u56_le(&self, oft: usize) -> u64;
fn i56_le(&self, oft: usize) -> i64;
fn u64_le(&self, oft: usize) -> u64;
fn i64_le(&self, oft: usize) -> i64;

fn u16_be(&self, oft: usize) -> u16;
fn u32_be(&self, oft: usize) -> u32;
fn u64_be(&self, oft: usize) -> u64;
}
3 changes: 3 additions & 0 deletions ds/src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ pub use malloc::*;

pub mod arena;

mod bytes;
pub use bytes::*;

use std::sync::atomic::{AtomicI64, Ordering::Relaxed};
pub static BUF_TX: Buffers = Buffers::new();
pub static BUF_RX: Buffers = Buffers::new();
Expand Down
6 changes: 3 additions & 3 deletions ds/src/mem/ring_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ impl RingSlice {
#[inline]
pub fn copy_to_r<R: RangeBounds<usize>>(&self, s: &mut [u8], r: R) {
let (start, end) = self.range(r);
assert!(start <= end && end <= s.len());
assert!(start <= end && end - start <= s.len());
assert!(end - start <= s.len());
if start == end {
return;
Expand Down Expand Up @@ -336,12 +336,12 @@ impl RingSlice {
self.cap as usize
}
#[inline(always)]
fn start(&self) -> usize {
pub(super) fn start(&self) -> usize {
self.start as usize
}

#[inline(always)]
fn mask(&self, oft: usize) -> usize {
pub(super) fn mask(&self, oft: usize) -> usize {
(self.mask & oft as u32) as usize
}

Expand Down
12 changes: 12 additions & 0 deletions procs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "procs"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true

[dependencies]
syn = { version = "1.0", features = ["full"] }
quote = "1.0"
proc-macro2 = "1.0"
181 changes: 181 additions & 0 deletions procs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse_macro_input, punctuated::Punctuated, token::Comma, AttributeArgs, FnArg, Ident,
ItemTrait, NestedMeta, PatType, ReturnType, Type,
};

#[derive(Clone, Copy)]
enum Endianess {
Big,
Little,
}

impl<'a> From<&'a str> for Endianess {
fn from(s: &'a str) -> Self {
match s {
"be" | "" => Endianess::Big,
"le" => Endianess::Little,
_ => panic!("invalid endianess"),
}
}
}

#[proc_macro_attribute]
pub fn impl_number_ringslice(args: TokenStream, input: TokenStream) -> TokenStream {
// 解析宏属性和trait定义
let attr_args = parse_macro_input!(args as AttributeArgs);
let input_trait = parse_macro_input!(input as ItemTrait);

let default_endianess = parse_default_endianess(&attr_args);
let trait_methods = input_trait.items.iter().filter_map(|item| {
if let syn::TraitItem::Method(method) = item {
Some(method)
} else {
None
}
});

let mut method_impls = Vec::new();

for method in trait_methods {
// method_name: i32_le u24_be ...
let method_name = &method.sig.ident;

let (sign, bits, endianess) = parse_method_name(method_name, default_endianess);
assert!(bits % 8 == 0, "invalid bits:{method_name}");

// u8, i8, i32, u32, ...
let ty = parse_return_type(&method.sig.output);
let ty_name = ty.to_string();
let ty_sign = ty_name.as_bytes()[0];
let ty_bits = ty_name[1..].parse::<usize>().expect("{ty_name} not valid");

assert_eq!(sign, ty_sign, "sign not match:{method_name} {ty_name}");
assert!(ty_bits.is_power_of_two(), "invalid bits:{method_name}");
assert!(ty_bits >= bits, "invalid bits:{method_name} {ty_name}");
let method_args = &method.sig.inputs;
assert!(validate_args(method_args), "invalid args:{method_name}");

// 我们假设有一个参数oft: usize

let from_endianess = match endianess {
Endianess::Big => quote! { from_be_bytes },
Endianess::Little => quote! { from_le_bytes },
};
// 小端的oft为0,大端为(ty_bits - bits) / 8
let copy_oft = match endianess {
Endianess::Big => ((ty_bits - bits) as usize) / 8,
Endianess::Little => 0,
};
// 如果是i24_le,则需要处理符号
let post = if sign == b'i' && bits < ty_bits {
let shift = (ty_bits - bits) as usize;
quote! {
let v = (v << #shift) as #ty >> #shift;
}
} else {
quote! {}
};
let copy_len = bits / 8;
let size = ty_bits / 8;

let method_impl = quote! {
#[inline(always)]
fn #method_name(&self, oft: usize) -> #ty{
debug_assert!(self.len() >= oft + #copy_len);
let oft_start = self.mask(oft + self.start());
let len = self.cap() - oft_start; // 从oft_start到cap的长度
let v = if len >= #size {
let b = unsafe { std::slice::from_raw_parts(self.ptr().add(oft_start), #size) };
#ty::#from_endianess(b[..#size].try_into().unwrap())
} else {
// 分段读取
let mut b = [0u8; #size];
use std::ptr::copy_nonoverlapping as copy;
unsafe { copy(self.ptr().add(oft_start), b.as_mut_ptr().add(#copy_oft), len) };
unsafe { copy(self.ptr(), b.as_mut_ptr().add(len + #copy_oft), #copy_len - len) };
#ty::#from_endianess(b)
};
#post
v
}
};
method_impls.push(method_impl);
}

let trait_name = &input_trait.ident;
let expanded = quote! {
#input_trait

impl #trait_name for RingSlice {
#(#method_impls)*
}
};

TokenStream::from(expanded)
}

fn parse_default_endianess(args: &AttributeArgs) -> Endianess {
// 确定默认字节序,这里我们假设只有一个参数,要么是`be`要么是`le`
if let Some(NestedMeta::Meta(syn::Meta::NameValue(nv))) = args.first() {
if nv.path.is_ident("default") {
if let syn::Lit::Str(ref endianess) = nv.lit {
return endianess.value().as_str().into();
}
}
}
Endianess::Big
}

// method_name: "i16_le":
// 返回 i 16 le
fn parse_method_name(method: &Ident, default_endianess: Endianess) -> (u8, usize, Endianess) {
let name_str = method.to_string();
let sign = name_str.as_bytes()[0];
let mut bits_endianess = name_str[1..].split('_');
let bits = bits_endianess
.next()
.expect("invalid method name")
.parse()
.expect("method parse bits");
let endianess = bits_endianess
.next()
.map(|s| s.into())
.unwrap_or(default_endianess);
(sign, bits, endianess)
}

fn validate_args(method_args: &Punctuated<FnArg, Comma>) -> bool {
// 检查参数的数量是否为2
if method_args.len() == 2 {
// 检查第一个参数是否是`self`的引用
if let Some(FnArg::Receiver(_)) = method_args.first() {
// 检查第二个参数是否是`usize`
if let Some(FnArg::Typed(PatType { ty, .. })) = method_args.last() {
if let Type::Path(type_path) = &**ty {
if let Some(last_segment) = type_path.path.segments.last() {
// 确认类型是否为`usize`
return last_segment.ident == "usize";
}
}
}
}
}
false
}

fn parse_return_type(return_type: &ReturnType) -> &Ident {
match return_type {
ReturnType::Type(_, ty) => {
if let Type::Path(path) = &**ty {
&path.path.segments.first().unwrap().ident
} else {
panic!("invalid return type")
}
}
ReturnType::Default => panic!("missing return type"),
}
}
13 changes: 12 additions & 1 deletion tests/src/benches/ring_slice.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use criterion::{black_box, Criterion};
use ds::RingSlice;
use ds::{ByteOrder, RingSlice};
pub(super) fn bench_iter(c: &mut Criterion) {
let cap = 128;
// 前cap-4个字节是数字,后4个字节是非数字
Expand Down Expand Up @@ -132,6 +132,17 @@ pub(super) fn bench_read_num(c: &mut Criterion) {
});
});
});
group.bench_function("u64_le_procs", |b| {
b.iter(|| {
black_box({
let mut t = 0u64;
for i in 0..runs {
t = t.wrapping_add(rs.u64_le(i) as u64);
}
t
});
});
});
group.bench_function("u64_le_copy", |b| {
b.iter(|| {
black_box({
Expand Down
14 changes: 13 additions & 1 deletion tests/src/ring_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{mem::size_of, num::NonZeroUsize};
use bytes::BufMut;

use byteorder::{BigEndian, ByteOrder, LittleEndian};
use ds::RingSlice;
use ds::{ByteOrder as RingSliceByteOrder, RingSlice};
use rand::Rng;
#[test]
fn test_ring_slice() {
Expand Down Expand Up @@ -112,6 +112,16 @@ fn test_read_number() {
assert_eq!(LittleEndian::read_i32(slice), rs.read_i32_le_cmp(i));
assert_eq!(BigEndian::read_u64(slice), rs.read_u64_be_cmp(i));
assert_eq!(LittleEndian::read_i64(slice), rs.read_i64_le_cmp(i));

// RingSliceByteOrder
assert_eq!(BigEndian::read_u16(slice), rs.u16_be(i));
assert_eq!(LittleEndian::read_i16(slice), rs.i16_le(i));
assert_eq!(BigEndian::read_u32(slice), rs.u32_be(i));
assert_eq!(LittleEndian::read_i32(slice), rs.i32_le(i));
assert_eq!(BigEndian::read_u64(slice), rs.u64_be(i));
assert_eq!(LittleEndian::read_i64(slice), rs.i64_le(i));
assert_eq!(LittleEndian::read_i24(slice), rs.i24_le(i));
assert_eq!(LittleEndian::read_u48(slice), rs.u48_le(i));
}
}
}
Expand All @@ -130,6 +140,8 @@ fn read_number_one() {
let rs = RingSlice::from_vec(&v);
assert_eq!(rs.read_i24_le_cmp(0), LittleEndian::read_i24(&v));
assert_eq!(rs.read_i24_be_cmp(0), BigEndian::read_i24(&v));

assert_eq!(rs.i24_le(0), LittleEndian::read_i24(&v));
}

#[test]
Expand Down

0 comments on commit b0f30b1

Please sign in to comment.