mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 10:14:06 +02:00
remove the need to read raw game event before transforming into game event
This commit is contained in:
parent
b517df0e57
commit
36077864ad
6 changed files with 6032 additions and 5027 deletions
5423
codegen/out.rs
5423
codegen/out.rs
File diff suppressed because it is too large
Load diff
|
|
@ -225,8 +225,10 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
|
||||||
let span = Span::call_site();
|
let span = Span::call_site();
|
||||||
|
|
||||||
let imports = quote!(
|
let imports = quote!(
|
||||||
use super::gamevent::{FromGameEventValue, FromRawGameEvent, GameEventValue, RawGameEvent};
|
use super::gamevent::{FromGameEventValue, GameEventDefinition, GameEventEntry, RawGameEvent};
|
||||||
use crate::Result;
|
use crate::demo::Stream;
|
||||||
|
use crate::{ParseError, Result};
|
||||||
|
use bitbuffer::{BitRead, LittleEndian};
|
||||||
);
|
);
|
||||||
|
|
||||||
let event_definitions = events.iter().map(|event| {
|
let event_definitions = events.iter().map(|event| {
|
||||||
|
|
@ -239,42 +241,37 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
|
||||||
|
|
||||||
let name = Ident::new(&format!("{}Event", get_event_name(&event.name)), span);
|
let name = Ident::new(&format!("{}Event", get_event_name(&event.name)), span);
|
||||||
|
|
||||||
let entry_constructors = event.entries.iter().map(|entry| {
|
let entry_readers = event.entries.iter().map(|entry| {
|
||||||
let name_str = get_entry_name(&entry.name);
|
let name_str = get_entry_name(&entry.name);
|
||||||
let name = Ident::new(&name_str, span);
|
let name = Ident::new(&name_str, span);
|
||||||
let ty = Ident::new(get_type_name(entry.kind), span);
|
let ty = Ident::new(get_type_name(entry.kind), span);
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
#name: #ty::from_value(iter.next(), #name_str)?,
|
#name: read_value::<#ty>(stream, iter.next(), #name_str)?,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let iter = if event.entries.len() > 0 {
|
let definition_iter = if event.entries.len() > 0 {
|
||||||
quote!(
|
quote!(
|
||||||
let mut iter = values.into_iter();
|
let mut iter = definition.entries.iter();
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
quote!()
|
quote!()
|
||||||
};
|
};
|
||||||
|
|
||||||
let param_name = if event.entries.len() > 0 {
|
|
||||||
quote!(values)
|
|
||||||
} else {
|
|
||||||
quote!(_values)
|
|
||||||
};
|
|
||||||
|
|
||||||
quote!(
|
quote!(
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct #name {
|
pub struct #name {
|
||||||
#(#fields)*
|
#(#fields)*
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromRawGameEvent for #name {
|
impl #name {
|
||||||
fn from_raw_event(#param_name: Vec<GameEventValue>) -> Result<Self> {
|
#[allow(unused_variables)]
|
||||||
#iter
|
fn read(stream: &mut Stream, definition: &GameEventDefinition) -> Result<Self> {
|
||||||
|
#definition_iter
|
||||||
|
|
||||||
Ok(#name {
|
Ok(#name {
|
||||||
#(#entry_constructors)*
|
#(#entry_readers)*
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -315,7 +312,7 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
|
||||||
quote!(GameEventType::#variant_name => #name_str,)
|
quote!(GameEventType::#variant_name => #name_str,)
|
||||||
});
|
});
|
||||||
|
|
||||||
let from_raw_events = events.iter().map(|event| {
|
let read_events = events.iter().map(|event| {
|
||||||
let name = get_event_name(&event.name);
|
let name = get_event_name(&event.name);
|
||||||
let variant_name = Ident::new(&name, span);
|
let variant_name = Ident::new(&name, span);
|
||||||
let struct_name = Ident::new(&format!("{}Event", name), span);
|
let struct_name = Ident::new(&format!("{}Event", name), span);
|
||||||
|
|
@ -323,13 +320,13 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
|
||||||
if should_box_event(&name) {
|
if should_box_event(&name) {
|
||||||
quote!(
|
quote!(
|
||||||
GameEventType::#variant_name => {
|
GameEventType::#variant_name => {
|
||||||
GameEvent::#variant_name(<Box<#struct_name>>::from_raw_event(event.values)?)
|
GameEvent::#variant_name(Box::new(<#struct_name>::read(stream, definition)?))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
quote!(
|
quote!(
|
||||||
GameEventType::#variant_name => {
|
GameEventType::#variant_name => {
|
||||||
GameEvent::#variant_name(#struct_name::from_raw_event(event.values)?)
|
GameEvent::#variant_name(#struct_name::read(stream, definition)?)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -347,6 +344,27 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
|
||||||
quote!(
|
quote!(
|
||||||
#imports
|
#imports
|
||||||
|
|
||||||
|
fn read_value<'a, T: FromGameEventValue + BitRead<'a, LittleEndian> + Default>(
|
||||||
|
stream: &mut Stream<'a>,
|
||||||
|
entry: Option<&GameEventEntry>,
|
||||||
|
name: &'static str,
|
||||||
|
) -> Result<T> {
|
||||||
|
let entry = match entry {
|
||||||
|
Some(entry) => entry,
|
||||||
|
None => {
|
||||||
|
return Ok(T::default());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if T::value_type() != entry.kind {
|
||||||
|
return Err(ParseError::InvalidGameEvent {
|
||||||
|
expected_type: T::value_type(),
|
||||||
|
name,
|
||||||
|
found_type: entry.kind,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(T::read(stream)?)
|
||||||
|
}
|
||||||
|
|
||||||
#(#event_definitions)*
|
#(#event_definitions)*
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -377,10 +395,10 @@ pub fn generate_game_events(demo: Demo) -> TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameEvent {
|
impl GameEvent {
|
||||||
pub fn from_raw_event(event: RawGameEvent) -> Result<Self> {
|
pub fn read(stream: &mut Stream, definition: &GameEventDefinition) -> Result<Self> {
|
||||||
Ok(match event.event_type {
|
Ok(match definition.event_type {
|
||||||
#(#from_raw_events)*
|
#(#read_events)*
|
||||||
GameEventType::Unknown => GameEvent::Unknown(event),
|
GameEventType::Unknown => GameEvent::Unknown(RawGameEvent::read(stream, definition)?),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,8 +1,9 @@
|
||||||
use bitbuffer::{BitRead, BitWrite};
|
use bitbuffer::{BitRead, BitWrite};
|
||||||
|
|
||||||
use crate::{ParseError, Result};
|
use crate::{GameEventError, ParseError, Result, Stream};
|
||||||
|
|
||||||
pub use super::gameevent_gen::{GameEvent, GameEventType};
|
pub use super::gameevent_gen::{GameEvent, GameEventType};
|
||||||
|
use crate::demo::handle_utf8_error;
|
||||||
use crate::demo::message::gameevent::GameEventTypeId;
|
use crate::demo::message::gameevent::GameEventTypeId;
|
||||||
use parse_display::Display;
|
use parse_display::Display;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
@ -65,6 +66,21 @@ pub enum GameEventValue {
|
||||||
Local,
|
Local,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_event_value(stream: &mut Stream, definition: &GameEventEntry) -> Result<GameEventValue> {
|
||||||
|
Ok(match definition.kind {
|
||||||
|
GameEventValueType::String => {
|
||||||
|
GameEventValue::String(stream.read().or_else(handle_utf8_error)?)
|
||||||
|
}
|
||||||
|
GameEventValueType::Float => GameEventValue::Float(stream.read()?),
|
||||||
|
GameEventValueType::Long => GameEventValue::Long(stream.read()?),
|
||||||
|
GameEventValueType::Short => GameEventValue::Short(stream.read()?),
|
||||||
|
GameEventValueType::Byte => GameEventValue::Byte(stream.read()?),
|
||||||
|
GameEventValueType::Boolean => GameEventValue::Boolean(stream.read()?),
|
||||||
|
GameEventValueType::Local => GameEventValue::Local,
|
||||||
|
GameEventValueType::None => return Err(GameEventError::NoneValue.into()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
impl GameEventValue {
|
impl GameEventValue {
|
||||||
pub fn get_type(&self) -> GameEventValueType {
|
pub fn get_type(&self) -> GameEventValueType {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -81,6 +97,8 @@ impl GameEventValue {
|
||||||
|
|
||||||
pub trait FromGameEventValue: Sized {
|
pub trait FromGameEventValue: Sized {
|
||||||
fn from_value(value: Option<GameEventValue>, name: &'static str) -> Result<Self>;
|
fn from_value(value: Option<GameEventValue>, name: &'static str) -> Result<Self>;
|
||||||
|
|
||||||
|
fn value_type() -> GameEventValueType;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromGameEventValue for String {
|
impl FromGameEventValue for String {
|
||||||
|
|
@ -95,6 +113,10 @@ impl FromGameEventValue for String {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_type() -> GameEventValueType {
|
||||||
|
GameEventValueType::String
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromGameEventValue for f32 {
|
impl FromGameEventValue for f32 {
|
||||||
|
|
@ -109,6 +131,10 @@ impl FromGameEventValue for f32 {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_type() -> GameEventValueType {
|
||||||
|
GameEventValueType::Float
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromGameEventValue for u32 {
|
impl FromGameEventValue for u32 {
|
||||||
|
|
@ -125,6 +151,10 @@ impl FromGameEventValue for u32 {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_type() -> GameEventValueType {
|
||||||
|
GameEventValueType::Long
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromGameEventValue for u16 {
|
impl FromGameEventValue for u16 {
|
||||||
|
|
@ -141,6 +171,10 @@ impl FromGameEventValue for u16 {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_type() -> GameEventValueType {
|
||||||
|
GameEventValueType::Short
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromGameEventValue for u8 {
|
impl FromGameEventValue for u8 {
|
||||||
|
|
@ -157,6 +191,10 @@ impl FromGameEventValue for u8 {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_type() -> GameEventValueType {
|
||||||
|
GameEventValueType::Byte
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromGameEventValue for bool {
|
impl FromGameEventValue for bool {
|
||||||
|
|
@ -171,6 +209,10 @@ impl FromGameEventValue for bool {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_type() -> GameEventValueType {
|
||||||
|
GameEventValueType::Boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromGameEventValue for () {
|
impl FromGameEventValue for () {
|
||||||
|
|
@ -184,6 +226,10 @@ impl FromGameEventValue for () {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_type() -> GameEventValueType {
|
||||||
|
GameEventValueType::Local
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -192,6 +238,20 @@ pub struct RawGameEvent {
|
||||||
pub values: Vec<GameEventValue>,
|
pub values: Vec<GameEventValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RawGameEvent {
|
||||||
|
pub fn read(stream: &mut Stream, definition: &GameEventDefinition) -> Result<Self> {
|
||||||
|
let mut values: Vec<GameEventValue> = Vec::with_capacity(definition.entries.len());
|
||||||
|
for entry in &definition.entries {
|
||||||
|
values.push(read_event_value(stream, &entry)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(RawGameEvent {
|
||||||
|
event_type: definition.event_type,
|
||||||
|
values,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait FromRawGameEvent: Sized {
|
pub trait FromRawGameEvent: Sized {
|
||||||
fn from_raw_event(values: Vec<GameEventValue>) -> Result<Self>;
|
fn from_raw_event(values: Vec<GameEventValue>) -> Result<Self>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,13 @@
|
||||||
use bitbuffer::{BitRead, LittleEndian};
|
use bitbuffer::{BitRead, BitWrite, BitWriteStream, LittleEndian};
|
||||||
use parse_display::Display;
|
use parse_display::Display;
|
||||||
|
|
||||||
use crate::demo::gameevent_gen::GameEventType;
|
use crate::demo::gameevent_gen::GameEventType;
|
||||||
use crate::demo::gamevent::{
|
use crate::demo::gamevent::{
|
||||||
GameEvent, GameEventDefinition, GameEventEntry, GameEventValue, GameEventValueType,
|
GameEvent, GameEventDefinition, GameEventEntry, GameEventValueType, RawGameEvent,
|
||||||
RawGameEvent,
|
|
||||||
};
|
};
|
||||||
use crate::demo::handle_utf8_error;
|
use crate::demo::parser::{Encode, ParseBitSkip};
|
||||||
use crate::demo::parser::ParseBitSkip;
|
|
||||||
use crate::{GameEventError, Parse, ParseError, ParserState, ReadResult, Result, Stream};
|
use crate::{GameEventError, Parse, ParseError, ParserState, ReadResult, Result, Stream};
|
||||||
|
|
||||||
fn read_event_value(stream: &mut Stream, definition: &GameEventEntry) -> Result<GameEventValue> {
|
|
||||||
Ok(match definition.kind {
|
|
||||||
GameEventValueType::String => {
|
|
||||||
GameEventValue::String(stream.read().or_else(handle_utf8_error)?)
|
|
||||||
}
|
|
||||||
GameEventValueType::Float => GameEventValue::Float(stream.read()?),
|
|
||||||
GameEventValueType::Long => GameEventValue::Long(stream.read()?),
|
|
||||||
GameEventValueType::Short => GameEventValue::Short(stream.read()?),
|
|
||||||
GameEventValueType::Byte => GameEventValue::Byte(stream.read()?),
|
|
||||||
GameEventValueType::Boolean => GameEventValue::Boolean(stream.read()?),
|
|
||||||
GameEventValueType::Local => GameEventValue::Local,
|
|
||||||
GameEventValueType::None => return Err(GameEventError::NoneValue.into()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GameEventMessage {
|
pub struct GameEventMessage {
|
||||||
pub event: Box<GameEvent>,
|
pub event: Box<GameEvent>,
|
||||||
|
|
@ -46,31 +29,30 @@ impl Parse<'_> for GameEventMessage {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let raw_event = match state.event_definitions.get(usize::from(event_type)) {
|
let event = match state.event_definitions.get(usize::from(event_type)) {
|
||||||
Some(definition) => {
|
Some(definition) => GameEvent::read(&mut data, definition)?,
|
||||||
let mut values: Vec<GameEventValue> = Vec::with_capacity(definition.entries.len());
|
|
||||||
for entry in &definition.entries {
|
|
||||||
values.push(read_event_value(&mut data, &entry)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
RawGameEvent {
|
|
||||||
event_type: definition.event_type,
|
|
||||||
values,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
None => {
|
||||||
return Err(ParseError::MalformedGameEvent(GameEventError::UnknownType(
|
return Err(ParseError::MalformedGameEvent(GameEventError::UnknownType(
|
||||||
event_type,
|
event_type,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let event = GameEvent::from_raw_event(raw_event)?;
|
|
||||||
Ok(GameEventMessage {
|
Ok(GameEventMessage {
|
||||||
event: Box::new(event),
|
event: Box::new(event),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Encode for GameEventMessage {
|
||||||
|
fn encode(
|
||||||
|
&self,
|
||||||
|
stream: &mut BitWriteStream<LittleEndian>,
|
||||||
|
_state: &ParserState,
|
||||||
|
) -> Result<()> {
|
||||||
|
Ok(stream.reserve_length(11, |_stream| Ok(()))?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ParseBitSkip<'_> for GameEventMessage {
|
impl ParseBitSkip<'_> for GameEventMessage {
|
||||||
fn parse_skip(stream: &mut Stream) -> Result<()> {
|
fn parse_skip(stream: &mut Stream) -> Result<()> {
|
||||||
let length: u16 = stream.read_sized(11)?;
|
let length: u16 = stream.read_sized(11)?;
|
||||||
|
|
@ -78,7 +60,7 @@ impl ParseBitSkip<'_> for GameEventMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(BitRead, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display)]
|
#[derive(BitRead, BitWrite, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display)]
|
||||||
pub struct GameEventTypeId(#[size = 9] u16);
|
pub struct GameEventTypeId(#[size = 9] u16);
|
||||||
|
|
||||||
impl From<GameEventTypeId> for usize {
|
impl From<GameEventTypeId> for usize {
|
||||||
|
|
@ -123,6 +105,22 @@ impl BitRead<'_, LittleEndian> for GameEventDefinition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BitWrite<LittleEndian> for GameEventDefinition {
|
||||||
|
fn write(&self, stream: &mut BitWriteStream<LittleEndian>) -> ReadResult<()> {
|
||||||
|
self.id.write(stream)?;
|
||||||
|
self.event_type.as_str().write(stream)?;
|
||||||
|
self.name.write(stream)?;
|
||||||
|
|
||||||
|
for entry in self.entries.iter() {
|
||||||
|
entry.kind.write(stream)?;
|
||||||
|
entry.name.write(stream)?;
|
||||||
|
}
|
||||||
|
GameEventValueType::None.write(stream)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BitRead<'_, LittleEndian> for GameEventListMessage {
|
impl BitRead<'_, LittleEndian> for GameEventListMessage {
|
||||||
fn read(stream: &mut Stream) -> ReadResult<Self> {
|
fn read(stream: &mut Stream) -> ReadResult<Self> {
|
||||||
let count: u16 = stream.read_sized(9)?;
|
let count: u16 = stream.read_sized(9)?;
|
||||||
|
|
@ -133,3 +131,17 @@ impl BitRead<'_, LittleEndian> for GameEventListMessage {
|
||||||
Ok(GameEventListMessage { event_list })
|
Ok(GameEventListMessage { event_list })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BitWrite<LittleEndian> for GameEventListMessage {
|
||||||
|
fn write(&self, stream: &mut BitWriteStream<LittleEndian>) -> ReadResult<()> {
|
||||||
|
(self.event_list.len() as u16).write(stream)?;
|
||||||
|
stream.reserve_length(20, |stream| {
|
||||||
|
for event in self.event_list.iter() {
|
||||||
|
event.write(stream)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use bitbuffer::{BitError, BitRead, LittleEndian};
|
use bitbuffer::{BitError, BitRead, BitWrite, BitWriteStream, LittleEndian};
|
||||||
|
|
||||||
pub use self::messagetypeanalyser::MessageTypeAnalyser;
|
pub use self::messagetypeanalyser::MessageTypeAnalyser;
|
||||||
|
|
||||||
|
|
@ -30,6 +30,19 @@ impl<'a, T: BitRead<'a, LittleEndian>> Parse<'a> for T {
|
||||||
Self::read(stream).map_err(ParseError::from)
|
Self::read(stream).map_err(ParseError::from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub trait Encode: Sized {
|
||||||
|
fn encode(&self, stream: &mut BitWriteStream<LittleEndian>, state: &ParserState) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BitWrite<LittleEndian>> Encode for T {
|
||||||
|
fn encode(
|
||||||
|
&self,
|
||||||
|
stream: &mut BitWriteStream<LittleEndian>,
|
||||||
|
_state: &ParserState,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.write(stream).map_err(ParseError::from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait ParseBitSkip<'a> {
|
pub trait ParseBitSkip<'a> {
|
||||||
fn parse_skip(stream: &mut Stream<'a>) -> Result<()>;
|
fn parse_skip(stream: &mut Stream<'a>) -> Result<()>;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue