core: add FromWStr trait and WStr::parse
This commit is contained in:
parent
8863b54db0
commit
f49ce49d28
|
@ -762,11 +762,11 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
let frame_path = arg.coerce_to_string(self)?;
|
let frame_path = arg.coerce_to_string(self)?;
|
||||||
if let Some((clip, frame)) = self.resolve_variable_path(target, frame_path.borrow())? {
|
if let Some((clip, frame)) = self.resolve_variable_path(target, frame_path.borrow())? {
|
||||||
if let Some(clip) = clip.as_display_object().and_then(|o| o.as_movie_clip()) {
|
if let Some(clip) = clip.as_display_object().and_then(|o| o.as_movie_clip()) {
|
||||||
let frame = frame.to_utf8_lossy(); // TODO: avoid this UTF8 conversion.
|
|
||||||
if let Ok(frame) = frame.parse().map(f64_to_wrapping_u32) {
|
if let Ok(frame) = frame.parse().map(f64_to_wrapping_u32) {
|
||||||
// First try to parse as a frame number.
|
// First try to parse as a frame number.
|
||||||
call_frame = Some((clip, frame));
|
call_frame = Some((clip, frame));
|
||||||
} else if let Some(frame) = clip.frame_label_to_number(&frame) {
|
// TODO(moulins): remove this UTF8 conversion
|
||||||
|
} else if let Some(frame) = clip.frame_label_to_number(&frame.to_utf8_lossy()) {
|
||||||
// Otherwise, it's a frame label.
|
// Otherwise, it's a frame label.
|
||||||
call_frame = Some((clip, frame.into()));
|
call_frame = Some((clip, frame.into()));
|
||||||
}
|
}
|
||||||
|
@ -1331,9 +1331,10 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(FrameControl::Continue);
|
return Ok(FrameControl::Continue);
|
||||||
} else if window_target.as_str().starts_with("_level") && window_target.len() > 6 {
|
} else if window_target.starts_with(WStr::from_units(b"_level")) && window_target.len() > 6
|
||||||
|
{
|
||||||
// target of `_level#` indicates a `loadMovieNum` call.
|
// target of `_level#` indicates a `loadMovieNum` call.
|
||||||
match window_target[6..].parse::<i32>() {
|
match window_target.slice(6..).parse::<i32>() {
|
||||||
Ok(level_id) => {
|
Ok(level_id) => {
|
||||||
let fetch = self.context.navigator.fetch(&url, RequestOptions::get());
|
let fetch = self.context.navigator.fetch(&url, RequestOptions::get());
|
||||||
let level = self.resolve_level(level_id);
|
let level = self.resolve_level(level_id);
|
||||||
|
|
|
@ -1007,12 +1007,11 @@ pub fn goto_frame<'gc>(
|
||||||
activation.resolve_variable_path(movie_clip.into(), frame_path.borrow())?
|
activation.resolve_variable_path(movie_clip.into(), frame_path.borrow())?
|
||||||
{
|
{
|
||||||
if let Some(clip) = clip.as_display_object().and_then(|o| o.as_movie_clip()) {
|
if let Some(clip) = clip.as_display_object().and_then(|o| o.as_movie_clip()) {
|
||||||
// TODO(moulins): we need WStr::parse for avoiding allocation here.
|
|
||||||
let frame = frame.to_string();
|
|
||||||
if let Ok(frame) = frame.parse().map(f64_to_wrapping_i32) {
|
if let Ok(frame) = frame.parse().map(f64_to_wrapping_i32) {
|
||||||
// First try to parse as a frame number.
|
// First try to parse as a frame number.
|
||||||
call_frame = Some((clip, frame));
|
call_frame = Some((clip, frame));
|
||||||
} else if let Some(frame) = clip.frame_label_to_number(&frame) {
|
// TODO(moulins): remove this UTF8 conversion
|
||||||
|
} else if let Some(frame) = clip.frame_label_to_number(&frame.to_utf8_lossy()) {
|
||||||
// Otherwise, it's a frame label.
|
// Otherwise, it's a frame label.
|
||||||
call_frame = Some((clip, frame as i32));
|
call_frame = Some((clip, frame as i32));
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ fn set_align<'gc>(
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_string(activation)?
|
.coerce_to_string(activation)?
|
||||||
|
.as_str()
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
activation
|
activation
|
||||||
|
@ -107,6 +108,7 @@ fn set_scale_mode<'gc>(
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_string(activation)?
|
.coerce_to_string(activation)?
|
||||||
|
.as_str()
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
activation
|
activation
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::avm1::{Object, ObjectPtr, ScriptObject, TDisplayObject, TObject, Valu
|
||||||
use crate::avm_warn;
|
use crate::avm_warn;
|
||||||
use crate::context::UpdateContext;
|
use crate::context::UpdateContext;
|
||||||
use crate::display_object::{DisplayObject, EditText, MovieClip, TDisplayObjectContainer};
|
use crate::display_object::{DisplayObject, EditText, MovieClip, TDisplayObjectContainer};
|
||||||
use crate::string::{AvmString, BorrowWStr};
|
use crate::string::{AvmString, BorrowWStr, WStr};
|
||||||
use crate::types::Percent;
|
use crate::types::Percent;
|
||||||
use gc_arena::{Collect, GcCell, MutationContext};
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -118,15 +118,15 @@ impl<'gc> StageObject<'gc> {
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
case_sensitive: bool,
|
case_sensitive: bool,
|
||||||
) -> Option<Value<'gc>> {
|
) -> Option<Value<'gc>> {
|
||||||
let name = name.as_str();
|
if let Some(slice) = name.try_slice(0..6) {
|
||||||
if let Some(slice) = name.get(0..6) {
|
let level_prefix = WStr::from_units(b"_level");
|
||||||
let is_level = if case_sensitive {
|
let is_level = if case_sensitive {
|
||||||
slice == "_level"
|
slice == level_prefix
|
||||||
} else {
|
} else {
|
||||||
slice.eq_ignore_ascii_case("_level")
|
slice.eq_ignore_case(level_prefix)
|
||||||
};
|
};
|
||||||
if is_level {
|
if is_level {
|
||||||
if let Some(level_id) = name.get(6..).and_then(|v| v.parse::<i32>().ok()) {
|
if let Some(level_id) = name.try_slice(6..).and_then(|v| v.parse::<i32>().ok()) {
|
||||||
let level = context
|
let level = context
|
||||||
.stage
|
.stage
|
||||||
.child_by_depth(level_id)
|
.child_by_depth(level_id)
|
||||||
|
@ -918,7 +918,7 @@ fn set_quality<'gc>(
|
||||||
_this: DisplayObject<'gc>,
|
_this: DisplayObject<'gc>,
|
||||||
val: Value<'gc>,
|
val: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
if let Ok(quality) = val.coerce_to_string(activation)?.parse() {
|
if let Ok(quality) = val.coerce_to_string(activation)?.as_str().parse() {
|
||||||
activation
|
activation
|
||||||
.context
|
.context
|
||||||
.stage
|
.stage
|
||||||
|
|
|
@ -6,9 +6,10 @@ use crate::ecma_conversions::{
|
||||||
f64_to_string, f64_to_wrapping_i16, f64_to_wrapping_i32, f64_to_wrapping_u16,
|
f64_to_string, f64_to_wrapping_i16, f64_to_wrapping_i32, f64_to_wrapping_u16,
|
||||||
f64_to_wrapping_u32,
|
f64_to_wrapping_u32,
|
||||||
};
|
};
|
||||||
use crate::string::{AvmString, WStr};
|
use crate::string::{AvmString, BorrowWStr, Integer, WStr};
|
||||||
use gc_arena::Collect;
|
use gc_arena::Collect;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::num::Wrapping;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Collect)]
|
#[derive(Debug, Clone, Copy, Collect)]
|
||||||
#[collect(no_drop)]
|
#[collect(no_drop)]
|
||||||
|
@ -125,8 +126,7 @@ impl<'gc> Value<'gc> {
|
||||||
match self {
|
match self {
|
||||||
Value::Bool(true) => 1.0,
|
Value::Bool(true) => 1.0,
|
||||||
Value::Number(v) => v,
|
Value::Number(v) => v,
|
||||||
// TODO: avoid this conversion to UTF8?
|
Value::String(v) => v.parse().unwrap_or(0.0),
|
||||||
Value::String(v) => v.to_utf8_lossy().parse().unwrap_or(0.0),
|
|
||||||
_ => 0.0,
|
_ => 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,36 +158,19 @@ impl<'gc> Value<'gc> {
|
||||||
|
|
||||||
if activation.swf_version() >= 6 {
|
if activation.swf_version() >= 6 {
|
||||||
if let Some(v) = v.strip_prefix(WStr::from_units(b"0x")) {
|
if let Some(v) = v.strip_prefix(WStr::from_units(b"0x")) {
|
||||||
let mut n: u32 = 0;
|
// Flash allows the '-' sign here.
|
||||||
for c in &v {
|
return match Wrapping::<i32>::from_wstr_radix(v, 16) {
|
||||||
n = n.wrapping_shl(4);
|
Ok(n) => f64::from(n.0 as i32),
|
||||||
n |= match u8::try_from(c) {
|
Err(_) => f64::NAN,
|
||||||
Ok(b'0'..=b'9') => c as u32 - b'0' as u32,
|
};
|
||||||
Ok(b'A'..=b'F') => c as u32 - b'A' as u32 + 10,
|
} else if v.starts_with(b'0')
|
||||||
Ok(b'a'..=b'f') => c as u32 - b'a' as u32 + 10,
|
|
||||||
_ => return f64::NAN,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return f64::from(n as i32);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.starts_with(b'0')
|
|
||||||
|| v.starts_with(WStr::from_units(b"+0"))
|
|| v.starts_with(WStr::from_units(b"+0"))
|
||||||
|| v.starts_with(WStr::from_units(b"-0")))
|
|| v.starts_with(WStr::from_units(b"-0"))
|
||||||
&& v.slice(1..)
|
|
||||||
.iter()
|
|
||||||
.all(|c| c >= b'0'.into() && c <= b'7'.into())
|
|
||||||
{
|
{
|
||||||
let trimmed = v.trim_start_matches(&b"+-"[..]);
|
// Flash allows the '-' sign here.
|
||||||
let mut n: u32 = 0;
|
if let Ok(n) = Wrapping::<i32>::from_wstr_radix(v.borrow(), 8) {
|
||||||
for c in &trimmed {
|
return f64::from(n.0);
|
||||||
n = n.wrapping_shl(3);
|
|
||||||
n |= (c - b'0' as u16) as u32;
|
|
||||||
}
|
}
|
||||||
if v.starts_with(b'-') {
|
|
||||||
n = n.wrapping_neg();
|
|
||||||
}
|
|
||||||
return f64::from(n as i32);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,8 +184,6 @@ impl<'gc> Value<'gc> {
|
||||||
f64::NAN
|
f64::NAN
|
||||||
} else {
|
} else {
|
||||||
v.trim_start_matches(&b"\t\n\r "[..])
|
v.trim_start_matches(&b"\t\n\r "[..])
|
||||||
// TODO: avoid this conversion to UTF8?
|
|
||||||
.to_utf8_lossy()
|
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap_or(f64::NAN)
|
.unwrap_or(f64::NAN)
|
||||||
}
|
}
|
||||||
|
@ -450,8 +431,7 @@ impl<'gc> Value<'gc> {
|
||||||
if swf_version >= 7 {
|
if swf_version >= 7 {
|
||||||
!v.is_empty()
|
!v.is_empty()
|
||||||
} else {
|
} else {
|
||||||
// TODO: avoid this conversion to UTF8?
|
let num = v.parse().unwrap_or(0.0);
|
||||||
let num = v.to_utf8_lossy().parse().unwrap_or(0.0);
|
|
||||||
num != 0.0
|
num != 0.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -299,6 +299,7 @@ pub fn set_align<'gc>(
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_string(activation)?
|
.coerce_to_string(activation)?
|
||||||
|
.as_str()
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
activation
|
activation
|
||||||
|
@ -530,6 +531,7 @@ pub fn set_scale_mode<'gc>(
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_string(activation)?
|
.coerce_to_string(activation)?
|
||||||
|
.as_str()
|
||||||
.parse()
|
.parse()
|
||||||
{
|
{
|
||||||
activation
|
activation
|
||||||
|
@ -640,6 +642,7 @@ pub fn set_quality<'gc>(
|
||||||
.get(0)
|
.get(0)
|
||||||
.unwrap_or(&Value::Undefined)
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_string(activation)?
|
.coerce_to_string(activation)?
|
||||||
|
.as_str()
|
||||||
.parse()
|
.parse()
|
||||||
{
|
{
|
||||||
activation
|
activation
|
||||||
|
|
|
@ -18,6 +18,7 @@ use crate::display_object::{
|
||||||
};
|
};
|
||||||
use crate::events::{ClipEvent, ClipEventResult};
|
use crate::events::{ClipEvent, ClipEventResult};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::string::{FromWStr, WStr};
|
||||||
use crate::vminterface::{AvmType, Instantiator};
|
use crate::vminterface::{AvmType, Instantiator};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use gc_arena::{Collect, GcCell, MutationContext};
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
|
@ -828,6 +829,22 @@ impl FromStr for StageDisplayState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromWStr for StageDisplayState {
|
||||||
|
type Err = ParseEnumError;
|
||||||
|
|
||||||
|
fn from_wstr(s: WStr<'_>) -> Result<Self, Self::Err> {
|
||||||
|
if s.eq_ignore_case(WStr::from_units(b"fullscreen")) {
|
||||||
|
Ok(StageDisplayState::FullScreen)
|
||||||
|
} else if s.eq_ignore_case(WStr::from_units(b"fullscreeninteractive")) {
|
||||||
|
Ok(StageDisplayState::FullScreenInteractive)
|
||||||
|
} else if s.eq_ignore_case(WStr::from_units(b"normal")) {
|
||||||
|
Ok(StageDisplayState::Normal)
|
||||||
|
} else {
|
||||||
|
Err(ParseEnumError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// The alignment of the stage.
|
/// The alignment of the stage.
|
||||||
/// This controls the position of the movie after scaling to fill the viewport.
|
/// This controls the position of the movie after scaling to fill the viewport.
|
||||||
|
|
|
@ -12,6 +12,7 @@ mod common;
|
||||||
mod avm;
|
mod avm;
|
||||||
mod buf;
|
mod buf;
|
||||||
mod ops;
|
mod ops;
|
||||||
|
mod parse;
|
||||||
mod pattern;
|
mod pattern;
|
||||||
mod raw;
|
mod raw;
|
||||||
mod slice;
|
mod slice;
|
||||||
|
@ -25,6 +26,7 @@ pub use avm::AvmString;
|
||||||
pub use buf::WString;
|
pub use buf::WString;
|
||||||
pub use common::{BorrowWStr, BorrowWStrMut, Units};
|
pub use common::{BorrowWStr, BorrowWStrMut, Units};
|
||||||
pub use ops::{Iter, Split, WStrToUtf8};
|
pub use ops::{Iter, Split, WStrToUtf8};
|
||||||
|
pub use parse::{FromWStr, Integer};
|
||||||
pub use pattern::Pattern;
|
pub use pattern::Pattern;
|
||||||
pub use slice::{WStr, WStrMut};
|
pub use slice::{WStr, WStrMut};
|
||||||
|
|
||||||
|
|
|
@ -153,6 +153,11 @@ macro_rules! impl_str_methods {
|
||||||
crate::string::ops::str_cmp_ignore_case($deref, other)
|
crate::string::ops::str_cmp_ignore_case($deref, other)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn parse<T: crate::string::FromWStr>($self: $receiver) -> Result<T, T::Err> {
|
||||||
|
T::from_wstr($deref)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` is the string contains only LATIN1 characters.
|
/// Returns `true` is the string contains only LATIN1 characters.
|
||||||
///
|
///
|
||||||
/// Note that this doesn't necessarily means that `self.is_wide()` is `false`.
|
/// Note that this doesn't necessarily means that `self.is_wide()` is `false`.
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
use std::num::Wrapping;
|
||||||
|
|
||||||
|
use super::WStr;
|
||||||
|
|
||||||
|
/// Analog of [`std::str::FromStr`], but for Ruffle's [`WStr<'_>`].
|
||||||
|
pub trait FromWStr: Sized {
|
||||||
|
type Err;
|
||||||
|
|
||||||
|
fn from_wstr(s: WStr<'_>) -> Result<Self, Self::Err>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error returned by [`Integer::from_str_radix`].
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
#[error("failed to parse integer")]
|
||||||
|
pub struct ParseIntError(());
|
||||||
|
|
||||||
|
/// Trait implemented for all integer types that can be parsed from a [`WStr<'_>`].
|
||||||
|
pub trait Integer: FromWStr<Err = ParseIntError> {
|
||||||
|
fn from_wstr_radix(s: WStr<'_>, radix: u32) -> Result<Self, Self::Err>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWStr for f64 {
|
||||||
|
type Err = std::num::ParseFloatError;
|
||||||
|
|
||||||
|
fn from_wstr(s: WStr<'_>) -> Result<Self, Self::Err> {
|
||||||
|
// TODO(moulins): avoid the utf8 conversion when we know the string can't
|
||||||
|
// possibly represent a floating point number.
|
||||||
|
s.to_utf8_lossy().parse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod int_parse {
|
||||||
|
use super::*;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
pub trait IntParse: Sized {
|
||||||
|
const SIGNED: bool;
|
||||||
|
|
||||||
|
fn from_digit(n: u32) -> Self;
|
||||||
|
fn checked_add(self, n: u32) -> Option<Self>;
|
||||||
|
fn checked_sub(self, n: u32) -> Option<Self>;
|
||||||
|
fn checked_mul(self, n: u32) -> Option<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_str_radix<T: IntParse>(s: WStr<'_>, radix: u32) -> Option<T> {
|
||||||
|
assert!(
|
||||||
|
radix >= 2 && radix <= 36,
|
||||||
|
"from_str_radix: radix must be between 2 and 36, got {}",
|
||||||
|
radix
|
||||||
|
);
|
||||||
|
|
||||||
|
let (is_neg, digits) = match s.try_get(0).map(u8::try_from) {
|
||||||
|
Some(Ok(b'-')) => (true, s.slice(1..)),
|
||||||
|
Some(Ok(b'+')) => (false, s.slice(1..)),
|
||||||
|
Some(_) => (false, s),
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_neg && !T::SIGNED {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
digits.iter().try_fold(T::from_digit(0), |num, c| {
|
||||||
|
let byte = u8::try_from(c).ok()?;
|
||||||
|
let digit = (byte as char).to_digit(radix)?;
|
||||||
|
let num = num.checked_mul(radix)?;
|
||||||
|
if is_neg {
|
||||||
|
num.checked_sub(digit)
|
||||||
|
} else {
|
||||||
|
num.checked_add(digit)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_int_parse {
|
||||||
|
($($ty:ty)*) => { $(
|
||||||
|
impl int_parse::IntParse for $ty {
|
||||||
|
#[allow(unused_comparisons)]
|
||||||
|
const SIGNED: bool = <$ty>::MIN < 0;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_digit(n: u32) -> Self {
|
||||||
|
n as $ty
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn checked_add(self, n: u32) -> Option<Self> {
|
||||||
|
<$ty>::checked_add(self, n as $ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn checked_sub(self, n: u32) -> Option<Self> {
|
||||||
|
<$ty>::checked_sub(self, n as $ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn checked_mul(self, n: u32) -> Option<Self> {
|
||||||
|
<$ty>::checked_mul(self, n as $ty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)* }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_int_parse! { u32 i32 usize }
|
||||||
|
|
||||||
|
macro_rules! impl_wrapping_int_parse {
|
||||||
|
($($ty:ty)*) => { $(
|
||||||
|
impl int_parse::IntParse for Wrapping<$ty> {
|
||||||
|
#[allow(unused_comparisons)]
|
||||||
|
const SIGNED: bool = <$ty>::MIN < 0;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_digit(n: u32) -> Self {
|
||||||
|
Self(n as $ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn checked_add(self, n: u32) -> Option<Self> {
|
||||||
|
Some(self + Self::from_digit(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn checked_sub(self, n: u32) -> Option<Self> {
|
||||||
|
Some(self - Self::from_digit(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn checked_mul(self, n: u32) -> Option<Self> {
|
||||||
|
Some(self * Self::from_digit(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)* }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_wrapping_int_parse! { u32 i32 usize }
|
||||||
|
|
||||||
|
macro_rules! impl_from_str_int {
|
||||||
|
($($ty:ty)*) => { $(
|
||||||
|
impl Integer for $ty {
|
||||||
|
#[inline]
|
||||||
|
fn from_wstr_radix(s: WStr<'_>, radix: u32) -> Result<Self, ParseIntError> {
|
||||||
|
int_parse::from_str_radix(s, radix).ok_or(ParseIntError(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Integer for Wrapping<$ty> {
|
||||||
|
#[inline]
|
||||||
|
fn from_wstr_radix(s: WStr<'_>, radix: u32) -> Result<Self, ParseIntError> {
|
||||||
|
int_parse::from_str_radix(s, radix).ok_or(ParseIntError(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWStr for $ty {
|
||||||
|
type Err = ParseIntError;
|
||||||
|
#[inline]
|
||||||
|
fn from_wstr(s: WStr<'_>) -> Result<Self, Self::Err> {
|
||||||
|
int_parse::from_str_radix(s, 10).ok_or(ParseIntError(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWStr for Wrapping<$ty> {
|
||||||
|
type Err = ParseIntError;
|
||||||
|
#[inline]
|
||||||
|
fn from_wstr(s: WStr<'_>) -> Result<Self, Self::Err> {
|
||||||
|
int_parse::from_str_radix(s, 10).ok_or(ParseIntError(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)* }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_str_int! { u32 i32 usize }
|
Loading…
Reference in New Issue