-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8c836d1
commit b0f30b1
Showing
9 changed files
with
250 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ members = [ | |
"metrics", | ||
"ds", | ||
"rt", | ||
"procs", | ||
"tests", | ||
"tests_integration", | ||
] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters