use super::super::types::OsuHitObject;
pub fn parse_hit_object(line: &str) -> Option<OsuHitObject> {
parse_hit_object_bytes(line.as_bytes())
}
pub fn parse_hit_object_bytes(line: &[u8]) -> Option<OsuHitObject> {
let mut iter = memchr::memchr_iter(b',', line);
let mut start = 0;
let mut next_field = || {
if let Some(end) = iter.next() {
let field = &line[start..end];
start = end + 1;
Some(field)
} else if start <= line.len() {
let field = &line[start..];
start = line.len() + 1; Some(field)
} else {
None
}
};
let f_x = next_field()?;
let f_y = next_field()?;
let f_time = next_field()?;
let f_type = next_field()?;
let f_sound = next_field()?;
let x: i32 = atoi::atoi(f_x)?;
let y: i32 = atoi::atoi(f_y)?;
let time: i32 = atoi::atoi(f_time)?;
let object_type: u8 = atoi::atoi(f_type)?;
let hit_sound: u8 = atoi::atoi(f_sound)?;
let extras_start = start;
let end_time = if (object_type & 128) != 0 {
let rest = if start < line.len() {
&line[start..]
} else {
&[]
};
let end_pos = memchr::memchr(b',', rest).unwrap_or(rest.len());
let f_extras = &rest[..end_pos];
let mut extra_iter = memchr::memchr_iter(b':', f_extras);
if let Some(colon_pos) = extra_iter.next() {
atoi::atoi(&f_extras[..colon_pos])
} else {
atoi::atoi(f_extras)
}
} else {
None
};
let extras = if extras_start < line.len() {
unsafe { compact_str::CompactString::from_utf8_unchecked(&line[extras_start..]) }
} else {
compact_str::CompactString::new("")
};
Some(OsuHitObject {
x,
y,
time,
object_type,
hit_sound,
end_time,
extras,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_hit_object_tap() {
let line = "402,192,1694,5,0,0:0:0:0:";
let ho = parse_hit_object(line).unwrap();
assert_eq!(ho.x, 402);
assert_eq!(ho.y, 192);
assert_eq!(ho.time, 1694);
assert_eq!(ho.object_type, 5);
assert_eq!(ho.hit_sound, 0);
}
}