Skip to content

Commit

Permalink
Merge pull request #546 from CryZe/serde-borrow
Browse files Browse the repository at this point in the history
Borrow as much as possible in the parsers
  • Loading branch information
CryZe authored Jul 27, 2022
2 parents 06c76ef + ce4bce1 commit c7dbc7b
Show file tree
Hide file tree
Showing 16 changed files with 356 additions and 257 deletions.
2 changes: 1 addition & 1 deletion capi/src/parse_run_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use livesplit_core::run::parser::{composite::ParsedRun, TimerKind};
use std::{io::Write, os::raw::c_char};

/// type
pub type ParseRunResult = Option<ParsedRun>;
pub type ParseRunResult = Option<ParsedRun<'static>>;
/// type
pub type OwnedParseRunResult = Box<ParseRunResult>;

Expand Down
3 changes: 2 additions & 1 deletion capi/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ pub unsafe extern "C" fn Run_parse_file_handle(
Box::new(
file.read_to_end(buf)
.ok()
.and_then(|_| parser::composite::parse(buf, path, load_files).ok()),
.and_then(|_| parser::composite::parse(buf, path, load_files).ok())
.map(|p| p.into_owned()),
)
})
}
Expand Down
6 changes: 3 additions & 3 deletions src/layout/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ mod timer;
mod title;
mod total_playtime;

#[cfg(windows)]
#[cfg(all(windows, feature = "std"))]
mod font_resolving;

// One single row component is:
Expand Down Expand Up @@ -394,7 +394,7 @@ where
// receive the byte representation of individual tables we query for, so
// we can get the family name from the `name` table.

#[cfg(windows)]
#[cfg(all(windows, feature = "std"))]
let family = if let Some(info) =
font_resolving::FontInfo::from_gdi(original_family_name, bold_flag, italic_flag)
{
Expand All @@ -421,7 +421,7 @@ where
family.to_owned()
};

#[cfg(not(windows))]
#[cfg(not(all(windows, feature = "std")))]
let family = family.to_owned();

// The font might not exist on the user's system, so we still prefer to
Expand Down
5 changes: 3 additions & 2 deletions src/networking/splits_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ pub enum DownloadError {
pub async fn download_run(
client: &Client,
id: &str,
) -> Result<composite::ParsedRun, DownloadError> {
) -> Result<composite::ParsedRun<'static>, DownloadError> {
let bytes = api::run::download(client, id).await.context(Download)?;
composite::parse(&bytes, None, false).context(Parse)
let run = composite::parse(&bytes, None, false).context(Parse)?;
Ok(run.into_owned())
}

/// Asynchronously uploads a run to Splits.io. An object representing the ID of
Expand Down
20 changes: 15 additions & 5 deletions src/run/parser/composite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,24 @@ pub type Result<T> = StdResult<T, Error>;

/// A run parsed by the Composite Parser. This contains the Run itself and
/// information about which parser parsed it.
pub struct ParsedRun {
pub struct ParsedRun<'a> {
/// The parsed run.
pub run: Run,
/// The parser that parsed it.
pub kind: TimerKind,
pub kind: TimerKind<'a>,
}

const fn parsed(run: Run, kind: TimerKind) -> ParsedRun {
impl ParsedRun<'_> {
/// Returns an owned version of the parsed run.
pub fn into_owned(self) -> ParsedRun<'static> {
ParsedRun {
run: self.run,
kind: self.kind.into_owned(),
}
}
}

const fn parsed(run: Run, kind: TimerKind<'_>) -> ParsedRun<'_> {
ParsedRun { run, kind }
}

Expand All @@ -74,7 +84,7 @@ pub fn parse_and_fix<R>(
source: &[u8],
path: Option<PathBuf>,
load_files: bool,
) -> Result<ParsedRun> {
) -> Result<ParsedRun<'_>> {
let mut run = parse(source, path, load_files)?;
run.run.fix_splits();
Ok(run)
Expand All @@ -86,7 +96,7 @@ pub fn parse_and_fix<R>(
/// additional files, like external images are allowed to be loaded. If you are
/// using livesplit-core in a server-like environment, set this to `false`. Only
/// client-side applications should set this to `true`.
pub fn parse(source: &[u8], path: Option<PathBuf>, load_files: bool) -> Result<ParsedRun> {
pub fn parse(source: &[u8], path: Option<PathBuf>, load_files: bool) -> Result<ParsedRun<'_>> {
if let Ok(source) = simdutf8::basic::from_utf8(source) {
let files_path = if load_files { path.clone() } else { None };

Expand Down
11 changes: 6 additions & 5 deletions src/run/parser/flitter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ pub use self::s_expressions::Error;
pub type Result<T> = StdResult<T, Error>;

#[derive(Deserialize)]
struct Splits {
title: String,
category: String,
struct Splits<'a> {
title: &'a str,
category: &'a str,
attempts: u32,
split_names: Vec<String>,
#[serde(borrow)]
split_names: Vec<&'a str>,
golds: Option<Vec<Gold>>,
personal_best: Option<Comparison>,
world_record: Option<Comparison>,
Expand All @@ -39,7 +40,7 @@ struct Split {

/// Attempts to parse a Flitter splits file.
pub fn parse(source: &str) -> Result<Run> {
let splits: Splits = self::s_expressions::from_str(source)?;
let splits: Splits<'_> = self::s_expressions::from_str(source)?;

let mut run = Run::new();

Expand Down
25 changes: 14 additions & 11 deletions src/run/parser/flitter/s_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
use crate::platform::prelude::*;
use core::{fmt::Display, num::ParseIntError};
use serde::de::{self, DeserializeOwned, DeserializeSeed, MapAccess, SeqAccess, Visitor};
use serde::{
de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor},
Deserialize,
};

/// The Error types for splits files that couldn't be parsed by the Flitter
/// Parser.
Expand Down Expand Up @@ -80,7 +83,7 @@ impl<'source> Deserializer<'source> {
}
}

fn parse_ident(&mut self) -> &str {
fn parse_ident(&mut self) -> &'source str {
if let Some((pos, _)) = self
.source
.char_indices()
Expand All @@ -94,7 +97,7 @@ impl<'source> Deserializer<'source> {
}
}

fn parse_string(&mut self) -> Result<&str> {
fn parse_string(&mut self) -> Result<&'source str> {
if let Some(rem) = self.source.strip_prefix('"') {
if let Some((in_str, after)) = rem.split_once('"') {
self.source = after;
Expand Down Expand Up @@ -137,9 +140,9 @@ impl<'source> Deserializer<'source> {
}
}

pub fn from_str<T>(source: &str) -> Result<T>
pub fn from_str<'de, T>(source: &'de str) -> Result<T>
where
T: DeserializeOwned,
T: 'de + Deserialize<'de>,
{
let mut deserializer = Deserializer::from_str(source);
let t = T::deserialize(&mut deserializer)?;
Expand All @@ -151,7 +154,7 @@ where
}
}

impl<'de, 'source> de::Deserializer<'de> for &mut Deserializer<'source> {
impl<'de> de::Deserializer<'de> for &mut Deserializer<'de> {
type Error = Error;

fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
Expand Down Expand Up @@ -236,13 +239,13 @@ impl<'de, 'source> de::Deserializer<'de> for &mut Deserializer<'source> {
where
V: Visitor<'de>,
{
visitor.visit_str(self.parse_string()?)
visitor.visit_borrowed_str(self.parse_string()?)
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_str(self.parse_string()?)
visitor.visit_borrowed_str(self.parse_string()?)
}
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
where
Expand Down Expand Up @@ -355,7 +358,7 @@ impl<'de, 'source> de::Deserializer<'de> for &mut Deserializer<'source> {
where
V: Visitor<'de>,
{
visitor.visit_str(self.parse_ident())
visitor.visit_borrowed_str(self.parse_ident())
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value>
where
Expand All @@ -366,7 +369,7 @@ impl<'de, 'source> de::Deserializer<'de> for &mut Deserializer<'source> {
}
}

impl<'de, 'source> SeqAccess<'de> for &mut Deserializer<'source> {
impl<'de> SeqAccess<'de> for &mut Deserializer<'de> {
type Error = Error;

fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
Expand All @@ -382,7 +385,7 @@ impl<'de, 'source> SeqAccess<'de> for &mut Deserializer<'source> {
}
}

impl<'de, 'source> MapAccess<'de> for &mut Deserializer<'source> {
impl<'de> MapAccess<'de> for &mut Deserializer<'de> {
type Error = Error;

fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
Expand Down
3 changes: 3 additions & 0 deletions src/run/parser/llanfair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ pub fn parse(source: &[u8]) -> Result<Run> {

// Seek to the first byte of the first segment
cursor = cursor.get(0x8F..).context(ReadSegment)?;

run.segments_mut().reserve(segment_count as usize);

for _ in 0..segment_count {
#[cfg(feature = "std")]
let mut icon = None;
Expand Down
44 changes: 23 additions & 21 deletions src/run/parser/source_live_timer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Provides the parser for the SourceLiveTimer splits files.
use crate::{platform::prelude::*, GameTime, Run, Segment, TimeSpan};
use alloc::borrow::Cow;
use core::result::Result as StdResult;
use serde::Deserialize;
use serde_json::Error as JsonError;
Expand All @@ -23,23 +24,27 @@ pub type Result<T> = StdResult<T, Error>;

#[allow(non_snake_case)]
#[derive(Deserialize)]
struct Splits {
Category: String,
RunName: Option<String>,
Splits: Option<Vec<Split>>,
struct Splits<'a> {
#[serde(borrow)]
Category: Cow<'a, str>,
#[serde(borrow)]
RunName: Option<Cow<'a, str>>,
Splits: Option<Vec<Split<'a>>>,
}

#[allow(non_snake_case)]
#[derive(Deserialize)]
struct Split {
Map: String,
Name: Option<String>,
struct Split<'a> {
#[serde(borrow)]
Map: Cow<'a, str>,
#[serde(borrow)]
Name: Option<Cow<'a, str>>,
Ticks: Option<u64>,
BestSegment: Option<u64>,
}

fn time_span_from_ticks(category_name: &str, ticks: u64) -> TimeSpan {
let seconds = if category_name.starts_with("Portal 2") {
fn time_span_from_ticks(is_portal2: bool, ticks: u64) -> TimeSpan {
let seconds = if is_portal2 {
ticks as f64 / 30.0
} else {
// Game is either Portal or Half Life 2
Expand All @@ -51,7 +56,8 @@ fn time_span_from_ticks(category_name: &str, ticks: u64) -> TimeSpan {

/// Attempts to parse a SourceLiveTimer splits file.
pub fn parse(source: &str) -> Result<Run> {
let splits: Splits = serde_json::from_str(source).map_err(|source| Error::Json { source })?;
let splits: Splits<'_> =
serde_json::from_str(source).map_err(|source| Error::Json { source })?;

let mut run = Run::new();

Expand All @@ -74,27 +80,23 @@ pub fn parse(source: &str) -> Result<Run> {
}

if let Some(segments) = splits.Splits {
for split in segments {
let name = if let Some(name) = split.Name {
name.to_owned()
} else {
split.Map.to_owned()
};
let is_portal2 = run.category_name().starts_with("Portal 2");

let mut segment = Segment::new(name);
run.segments_mut().extend(segments.into_iter().map(|split| {
let mut segment = Segment::new(split.Name.unwrap_or(split.Map));

if let Some(ticks) = split.Ticks {
let pb_split_time = Some(time_span_from_ticks(run.category_name(), ticks));
let pb_split_time = Some(time_span_from_ticks(is_portal2, ticks));
segment.set_personal_best_split_time(GameTime(pb_split_time).into());
}

if let Some(best) = split.BestSegment {
let best_segment_time = Some(time_span_from_ticks(run.category_name(), best));
let best_segment_time = Some(time_span_from_ticks(is_portal2, best));
segment.set_best_segment_time(GameTime(best_segment_time).into());
}

run.push_segment(segment);
}
segment
}));
}

Ok(run)
Expand Down
Loading

0 comments on commit c7dbc7b

Please sign in to comment.