You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
225 lines
6.2 KiB
225 lines
6.2 KiB
use core::{mem, slice};
|
|
use crate::error::Error;
|
|
|
|
pub struct MsgDef {
|
|
pub fields: &'static [FieldDef],
|
|
pub defaults: &'static [u8],
|
|
pub is_experimental: bool,
|
|
pub wire_id: Option<u16>,
|
|
pub offset: u16,
|
|
}
|
|
|
|
impl MsgDef {
|
|
pub fn for_name(msg_name: u16) -> Option<Self> {
|
|
find_msg_offset_by_name(msg_name).map(|msg_offset| unsafe {
|
|
// SAFETY: We are taking the offset right out of the definitions so we can be
|
|
// sure it's to be trusted.
|
|
get_msg(msg_offset)
|
|
})
|
|
}
|
|
|
|
pub fn for_wire_id(wire_id: u16) -> Option<Self> {
|
|
find_msg_offset_by_wire(wire_id).map(|msg_offset| unsafe {
|
|
// SAFETY: We are taking the offset right out of the definitions so we can be
|
|
// sure it's to be trusted.
|
|
get_msg(msg_offset)
|
|
})
|
|
}
|
|
|
|
pub fn field(&self, tag: u8) -> Option<&FieldDef> {
|
|
self.fields.iter().find(|field| field.tag == tag)
|
|
}
|
|
}
|
|
|
|
#[repr(C, packed)]
|
|
pub struct FieldDef {
|
|
pub tag: u8,
|
|
flags_and_type: u8,
|
|
enum_or_msg_offset: u16,
|
|
pub name: u16,
|
|
}
|
|
|
|
impl FieldDef {
|
|
pub fn get_type(&self) -> FieldType {
|
|
match self.ftype() {
|
|
0 => FieldType::UVarInt,
|
|
1 => FieldType::SVarInt,
|
|
2 => FieldType::Bool,
|
|
3 => FieldType::Bytes,
|
|
4 => FieldType::String,
|
|
5 => FieldType::Enum(unsafe { get_enum(self.enum_or_msg_offset) }),
|
|
6 => FieldType::Msg(unsafe { get_msg(self.enum_or_msg_offset) }),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
pub fn is_required(&self) -> bool {
|
|
self.flags() & 0b_1000_0000 != 0
|
|
}
|
|
|
|
pub fn is_repeated(&self) -> bool {
|
|
self.flags() & 0b_0100_0000 != 0
|
|
}
|
|
|
|
pub fn is_experimental(&self) -> bool {
|
|
self.flags() & 0b_0010_0000 != 0
|
|
}
|
|
|
|
fn flags(&self) -> u8 {
|
|
self.flags_and_type & 0xF0
|
|
}
|
|
|
|
fn ftype(&self) -> u8 {
|
|
self.flags_and_type & 0x0F
|
|
}
|
|
}
|
|
|
|
pub enum FieldType {
|
|
UVarInt,
|
|
SVarInt,
|
|
Bool,
|
|
Bytes,
|
|
String,
|
|
Enum(EnumDef),
|
|
Msg(MsgDef),
|
|
}
|
|
|
|
pub const PRIMITIVE_TYPE_VARINT: u8 = 0;
|
|
pub const PRIMITIVE_TYPE_LENGTH_DELIMITED: u8 = 2;
|
|
|
|
impl FieldType {
|
|
pub fn primitive_type(&self) -> u8 {
|
|
match self {
|
|
FieldType::UVarInt | FieldType::SVarInt | FieldType::Bool | FieldType::Enum(_) => {
|
|
PRIMITIVE_TYPE_VARINT
|
|
}
|
|
FieldType::Bytes | FieldType::String | FieldType::Msg(_) => {
|
|
PRIMITIVE_TYPE_LENGTH_DELIMITED
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct EnumDef {
|
|
pub values: &'static [u16],
|
|
}
|
|
|
|
#[repr(C, packed)]
|
|
struct NameDef {
|
|
msg_name: u16,
|
|
msg_offset: u16,
|
|
}
|
|
|
|
static ENUM_DEFS: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/../../../../proto_enums.data"));
|
|
static MSG_DEFS: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/../../../..//proto_msgs.data"));
|
|
static NAME_DEFS: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/../../../..//proto_names.data"));
|
|
static WIRE_DEFS: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/../../../..//proto_wire.data"));
|
|
|
|
pub fn find_name_by_msg_offset(msg_offset: u16) -> Result<u16, Error> {
|
|
let name_defs: &[NameDef] = unsafe {
|
|
slice::from_raw_parts(
|
|
NAME_DEFS.as_ptr().cast(),
|
|
NAME_DEFS.len() / mem::size_of::<NameDef>(),
|
|
)
|
|
};
|
|
|
|
name_defs.iter()
|
|
.filter(|def| def.msg_offset == msg_offset)
|
|
.next()
|
|
.map(|def| def.msg_name)
|
|
.ok_or(Error::Missing)
|
|
}
|
|
|
|
fn find_msg_offset_by_name(msg_name: u16) -> Option<u16> {
|
|
let name_defs: &[NameDef] = unsafe {
|
|
slice::from_raw_parts(
|
|
NAME_DEFS.as_ptr().cast(),
|
|
NAME_DEFS.len() / mem::size_of::<NameDef>(),
|
|
)
|
|
};
|
|
name_defs
|
|
.binary_search_by_key(&msg_name, |def| def.msg_name)
|
|
.map(|i| name_defs[i].msg_offset)
|
|
.ok()
|
|
}
|
|
|
|
fn find_msg_offset_by_wire(wire_id: u16) -> Option<u16> {
|
|
#[repr(C, packed)]
|
|
struct WireDef {
|
|
wire_id: u16,
|
|
msg_offset: u16,
|
|
}
|
|
|
|
let wire_defs: &[WireDef] = unsafe {
|
|
slice::from_raw_parts(
|
|
WIRE_DEFS.as_ptr().cast(),
|
|
WIRE_DEFS.len() / mem::size_of::<WireDef>(),
|
|
)
|
|
};
|
|
wire_defs
|
|
.binary_search_by_key(&wire_id, |def| def.wire_id)
|
|
.map(|i| wire_defs[i].msg_offset)
|
|
.ok()
|
|
}
|
|
|
|
pub unsafe fn get_msg(msg_offset: u16) -> MsgDef {
|
|
// #[repr(C, packed)]
|
|
// struct MsgDef {
|
|
// fields_count: u8,
|
|
// defaults_size: u8,
|
|
// flags_and_wire_id: u16,
|
|
// fields: [Field],
|
|
// defaults: [u8],
|
|
// }
|
|
|
|
// SAFETY: `msg_offset` has to point to a beginning of a valid message
|
|
// definition inside `MSG_DEFS`.
|
|
unsafe {
|
|
let ptr = MSG_DEFS.as_ptr().add(msg_offset as usize);
|
|
let fields_count = ptr.offset(0).read() as usize;
|
|
let defaults_size = ptr.offset(1).read() as usize;
|
|
|
|
let flags_and_wire_id_lo = ptr.offset(2).read();
|
|
let flags_and_wire_id_hi = ptr.offset(3).read();
|
|
let flags_and_wire_id = u16::from_le_bytes([flags_and_wire_id_lo, flags_and_wire_id_hi]);
|
|
|
|
let is_experimental = flags_and_wire_id & 0x8000 != 0;
|
|
let wire_id = match flags_and_wire_id & 0x7FFF {
|
|
0x7FFF => None,
|
|
some_wire_id => Some(some_wire_id),
|
|
};
|
|
|
|
let fields_size = fields_count * mem::size_of::<FieldDef>();
|
|
let fields_ptr = ptr.offset(4);
|
|
let defaults_ptr = ptr.offset(4).add(fields_size);
|
|
|
|
MsgDef {
|
|
fields: slice::from_raw_parts(fields_ptr.cast(), fields_count),
|
|
defaults: slice::from_raw_parts(defaults_ptr.cast(), defaults_size),
|
|
is_experimental,
|
|
wire_id,
|
|
offset: msg_offset,
|
|
}
|
|
}
|
|
}
|
|
|
|
unsafe fn get_enum(enum_offset: u16) -> EnumDef {
|
|
// #[repr(C, packed)]
|
|
// struct EnumDef {
|
|
// count: u8,
|
|
// vals: [u16],
|
|
// }
|
|
|
|
// SAFETY: `enum_offset` has to point to a beginning of a valid enum
|
|
// definition inside `ENUM_DEFS`.
|
|
unsafe {
|
|
let ptr = ENUM_DEFS.as_ptr().add(enum_offset as usize);
|
|
let count = ptr.offset(0).read() as usize;
|
|
let vals = ptr.offset(1);
|
|
|
|
EnumDef {
|
|
values: slice::from_raw_parts(vals.cast(), count),
|
|
}
|
|
}
|
|
}
|