core: use WStrings for display object's frame and scene labels
This commit is contained in:
parent
84b4e33036
commit
87400b829d
|
@ -765,8 +765,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
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));
|
||||||
// TODO(moulins): remove this UTF8 conversion
|
} else if let Some(frame) = clip.frame_label_to_number(frame) {
|
||||||
} 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()));
|
||||||
}
|
}
|
||||||
|
@ -1407,9 +1406,8 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
|
||||||
fn action_goto_label(&mut self, label: &'_ SwfStr) -> Result<FrameControl<'gc>, Error<'gc>> {
|
fn action_goto_label(&mut self, label: &'_ SwfStr) -> Result<FrameControl<'gc>, Error<'gc>> {
|
||||||
if let Some(clip) = self.target_clip() {
|
if let Some(clip) = self.target_clip() {
|
||||||
if let Some(clip) = clip.as_movie_clip() {
|
if let Some(clip) = clip.as_movie_clip() {
|
||||||
if let Some(frame) =
|
let label = WString::from_utf8(&label.to_str_lossy(self.encoding()));
|
||||||
clip.frame_label_to_number(&label.to_str_lossy(self.encoding()))
|
if let Some(frame) = clip.frame_label_to_number(label.borrow()) {
|
||||||
{
|
|
||||||
clip.goto_frame(&mut self.context, frame, true);
|
clip.goto_frame(&mut self.context, frame, true);
|
||||||
} else {
|
} else {
|
||||||
avm_warn!(self, "GoToLabel: Frame label '{:?}' not found", label);
|
avm_warn!(self, "GoToLabel: Frame label '{:?}' not found", label);
|
||||||
|
|
|
@ -1010,8 +1010,7 @@ pub fn goto_frame<'gc>(
|
||||||
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));
|
||||||
// TODO(moulins): remove this UTF8 conversion
|
} else if let Some(frame) = clip.frame_label_to_number(frame) {
|
||||||
} 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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -580,15 +580,15 @@ pub fn set_type<'gc>(
|
||||||
activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
) -> Result<(), Error<'gc>> {
|
) -> Result<(), Error<'gc>> {
|
||||||
match value
|
let value = value.coerce_to_string(activation)?.to_ascii_lowercase();
|
||||||
.coerce_to_string(activation)?
|
|
||||||
.to_ascii_lowercase()
|
if value == b"input" {
|
||||||
.as_str()
|
this.set_editable(true, &mut activation.context);
|
||||||
{
|
} else if value == b"dynamic" {
|
||||||
"input" => this.set_editable(true, &mut activation.context),
|
this.set_editable(false, &mut activation.context);
|
||||||
"dynamic" => this.set_editable(false, &mut activation.context),
|
} else {
|
||||||
value => log::warn!("Invalid TextField.type: {}", value),
|
log::warn!("Invalid TextField.type: {}", value);
|
||||||
};
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::avm2::object::{ArrayObject, Object, TObject};
|
||||||
use crate::avm2::value::Value;
|
use crate::avm2::value::Value;
|
||||||
use crate::avm2::Error;
|
use crate::avm2::Error;
|
||||||
use crate::display_object::{MovieClip, Scene, TDisplayObject};
|
use crate::display_object::{MovieClip, Scene, TDisplayObject};
|
||||||
use crate::string::AvmString;
|
use crate::string::{AvmString, BorrowWStr, WString};
|
||||||
use crate::tag_utils::SwfMovie;
|
use crate::tag_utils::SwfMovie;
|
||||||
use gc_arena::{GcCell, MutationContext};
|
use gc_arena::{GcCell, MutationContext};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -111,7 +111,7 @@ pub fn current_frame_label<'gc>(
|
||||||
if start_frame < mc.current_frame() {
|
if start_frame < mc.current_frame() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(AvmString::new(activation.context.gc_context, label).into())
|
Some(AvmString::new_ucs2(activation.context.gc_context, label).into())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or(Value::Null));
|
.unwrap_or(Value::Null));
|
||||||
|
@ -133,7 +133,7 @@ pub fn current_label<'gc>(
|
||||||
return Ok(mc
|
return Ok(mc
|
||||||
.current_label()
|
.current_label()
|
||||||
.map(|(label, _start_frame)| {
|
.map(|(label, _start_frame)| {
|
||||||
AvmString::new(activation.context.gc_context, label).into()
|
AvmString::new_ucs2(activation.context.gc_context, label).into()
|
||||||
})
|
})
|
||||||
.unwrap_or(Value::Null));
|
.unwrap_or(Value::Null));
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ fn labels_for_scene<'gc>(
|
||||||
let mut frame_labels = Vec::with_capacity(labels.len());
|
let mut frame_labels = Vec::with_capacity(labels.len());
|
||||||
|
|
||||||
for (name, frame) in labels {
|
for (name, frame) in labels {
|
||||||
let name: Value<'gc> = AvmString::new(activation.context.gc_context, name).into();
|
let name: Value<'gc> = AvmString::new_ucs2(activation.context.gc_context, name).into();
|
||||||
let local_frame = frame - scene_start + 1;
|
let local_frame = frame - scene_start + 1;
|
||||||
let args = [name, local_frame.into()];
|
let args = [name, local_frame.into()];
|
||||||
let frame_label = frame_label_class.construct(activation, &args)?;
|
let frame_label = frame_label_class.construct(activation, &args)?;
|
||||||
|
@ -186,7 +186,7 @@ pub fn current_labels<'gc>(
|
||||||
.and_then(|dobj| dobj.as_movie_clip())
|
.and_then(|dobj| dobj.as_movie_clip())
|
||||||
{
|
{
|
||||||
let scene = mc.current_scene().unwrap_or_else(|| Scene {
|
let scene = mc.current_scene().unwrap_or_else(|| Scene {
|
||||||
name: "".to_string(),
|
name: WString::default(),
|
||||||
start: 0,
|
start: 0,
|
||||||
length: mc.total_frames(),
|
length: mc.total_frames(),
|
||||||
});
|
});
|
||||||
|
@ -207,7 +207,7 @@ pub fn current_scene<'gc>(
|
||||||
.and_then(|dobj| dobj.as_movie_clip())
|
.and_then(|dobj| dobj.as_movie_clip())
|
||||||
{
|
{
|
||||||
let scene = mc.current_scene().unwrap_or_else(|| Scene {
|
let scene = mc.current_scene().unwrap_or_else(|| Scene {
|
||||||
name: "".to_string(),
|
name: WString::default(),
|
||||||
start: 0,
|
start: 0,
|
||||||
length: mc.total_frames(),
|
length: mc.total_frames(),
|
||||||
});
|
});
|
||||||
|
@ -240,7 +240,7 @@ pub fn scenes<'gc>(
|
||||||
let mut mc_scenes = mc.scenes();
|
let mut mc_scenes = mc.scenes();
|
||||||
if mc.scenes().is_empty() {
|
if mc.scenes().is_empty() {
|
||||||
mc_scenes.push(Scene {
|
mc_scenes.push(Scene {
|
||||||
name: "".to_string(),
|
name: WString::default(),
|
||||||
start: 0,
|
start: 0,
|
||||||
length: mc.total_frames(),
|
length: mc.total_frames(),
|
||||||
});
|
});
|
||||||
|
@ -364,7 +364,7 @@ pub fn goto_frame<'gc>(
|
||||||
let scene = match args.get(1).cloned().unwrap_or(Value::Null) {
|
let scene = match args.get(1).cloned().unwrap_or(Value::Null) {
|
||||||
Value::Null => None,
|
Value::Null => None,
|
||||||
v => mc
|
v => mc
|
||||||
.scene_label_to_number(&v.coerce_to_string(activation)?)
|
.scene_label_to_number(v.coerce_to_string(activation)?.borrow())
|
||||||
.map(|v| v.saturating_sub(1)),
|
.map(|v| v.saturating_sub(1)),
|
||||||
}
|
}
|
||||||
.unwrap_or(0) as u32;
|
.unwrap_or(0) as u32;
|
||||||
|
@ -380,7 +380,7 @@ pub fn goto_frame<'gc>(
|
||||||
//If the user specified a scene, we need to validate that
|
//If the user specified a scene, we need to validate that
|
||||||
//the requested frame exists within that scene.
|
//the requested frame exists within that scene.
|
||||||
let scene = scene.coerce_to_string(activation)?;
|
let scene = scene.coerce_to_string(activation)?;
|
||||||
if !mc.frame_exists_within_scene(&frame_or_label, &scene) {
|
if !mc.frame_exists_within_scene(frame_or_label.borrow(), scene.borrow()) {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"ArgumentError: Frame label {} not found in scene {}",
|
"ArgumentError: Frame label {} not found in scene {}",
|
||||||
frame_or_label, scene
|
frame_or_label, scene
|
||||||
|
@ -389,7 +389,8 @@ pub fn goto_frame<'gc>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mc.frame_label_to_number(&frame_or_label).ok_or_else(|| {
|
mc.frame_label_to_number(frame_or_label.borrow())
|
||||||
|
.ok_or_else(|| {
|
||||||
format!(
|
format!(
|
||||||
"ArgumentError: {} is not a valid frame label.",
|
"ArgumentError: {} is not a valid frame label.",
|
||||||
frame_or_label
|
frame_or_label
|
||||||
|
|
|
@ -31,7 +31,7 @@ use crate::drawing::Drawing;
|
||||||
use crate::events::{ButtonKeyCode, ClipEvent, ClipEventResult};
|
use crate::events::{ButtonKeyCode, ClipEvent, ClipEventResult};
|
||||||
use crate::font::Font;
|
use crate::font::Font;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::string::AvmString;
|
use crate::string::{AvmString, WStr, WString};
|
||||||
use crate::tag_utils::{self, DecodeResult, SwfMovie, SwfSlice, SwfStream};
|
use crate::tag_utils::{self, DecodeResult, SwfMovie, SwfSlice, SwfStream};
|
||||||
use crate::vminterface::{AvmObject, AvmType, Instantiator};
|
use crate::vminterface::{AvmObject, AvmType, Instantiator};
|
||||||
use gc_arena::{Collect, Gc, GcCell, MutationContext};
|
use gc_arena::{Collect, Gc, GcCell, MutationContext};
|
||||||
|
@ -662,7 +662,7 @@ impl<'gc> MovieClip<'gc> {
|
||||||
fn scene_and_frame_labels(
|
fn scene_and_frame_labels(
|
||||||
self,
|
self,
|
||||||
reader: &mut SwfStream<'_>,
|
reader: &mut SwfStream<'_>,
|
||||||
static_data: &mut MovieClipStatic,
|
static_data: &mut MovieClipStatic<'gc>,
|
||||||
) -> DecodeResult {
|
) -> DecodeResult {
|
||||||
let mut sfl_data = reader.read_define_scene_and_frame_label_data()?;
|
let mut sfl_data = reader.read_define_scene_and_frame_label_data()?;
|
||||||
sfl_data
|
sfl_data
|
||||||
|
@ -677,7 +677,7 @@ impl<'gc> MovieClip<'gc> {
|
||||||
.map(|fld| fld.frame_num + 1)
|
.map(|fld| fld.frame_num + 1)
|
||||||
.unwrap_or_else(|| static_data.total_frames as u32 + 1);
|
.unwrap_or_else(|| static_data.total_frames as u32 + 1);
|
||||||
|
|
||||||
let label = label.to_string_lossy(reader.encoding());
|
let label = WString::from_utf8(&label.to_string_lossy(reader.encoding()));
|
||||||
static_data.scene_labels.insert(
|
static_data.scene_labels.insert(
|
||||||
label.clone(),
|
label.clone(),
|
||||||
Scene {
|
Scene {
|
||||||
|
@ -690,7 +690,7 @@ impl<'gc> MovieClip<'gc> {
|
||||||
|
|
||||||
for FrameLabelData { frame_num, label } in sfl_data.frame_labels {
|
for FrameLabelData { frame_num, label } in sfl_data.frame_labels {
|
||||||
static_data.frame_labels.insert(
|
static_data.frame_labels.insert(
|
||||||
label.to_string_lossy(reader.encoding()),
|
WString::from_utf8(&label.to_string_lossy(reader.encoding())),
|
||||||
frame_num as u16 + 1,
|
frame_num as u16 + 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -903,10 +903,10 @@ impl<'gc> MovieClip<'gc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Yield the current frame label as a tuple of string and frame number.
|
/// Yield the current frame label as a tuple of string and frame number.
|
||||||
pub fn current_label(self) -> Option<(String, FrameNumber)> {
|
pub fn current_label(self) -> Option<(WString, FrameNumber)> {
|
||||||
let read = self.0.read();
|
let read = self.0.read();
|
||||||
let current_frame = read.current_frame();
|
let current_frame = read.current_frame();
|
||||||
let mut best: Option<(&str, FrameNumber)> = None;
|
let mut best: Option<(&WString, FrameNumber)> = None;
|
||||||
|
|
||||||
for (label, frame) in read.static_data.frame_labels.iter() {
|
for (label, frame) in read.static_data.frame_labels.iter() {
|
||||||
if *frame > current_frame {
|
if *frame > current_frame {
|
||||||
|
@ -918,16 +918,20 @@ impl<'gc> MovieClip<'gc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
best.map(|(s, fnum)| (s.to_string(), fnum))
|
best.map(|(s, fnum)| (s.clone(), fnum))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Yield a list of labels and frame-numbers in the current scene.
|
/// Yield a list of labels and frame-numbers in the current scene.
|
||||||
///
|
///
|
||||||
/// Labels are returned sorted by frame number.
|
/// Labels are returned sorted by frame number.
|
||||||
pub fn labels_in_range(self, from: FrameNumber, to: FrameNumber) -> Vec<(String, FrameNumber)> {
|
pub fn labels_in_range(
|
||||||
|
self,
|
||||||
|
from: FrameNumber,
|
||||||
|
to: FrameNumber,
|
||||||
|
) -> Vec<(WString, FrameNumber)> {
|
||||||
let read = self.0.read();
|
let read = self.0.read();
|
||||||
|
|
||||||
let mut values: Vec<(String, FrameNumber)> = read
|
let mut values: Vec<(WString, FrameNumber)> = read
|
||||||
.static_data
|
.static_data
|
||||||
.frame_labels
|
.frame_labels
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -958,24 +962,25 @@ impl<'gc> MovieClip<'gc> {
|
||||||
write.avm2_class = constr;
|
write.avm2_class = constr;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame_label_to_number(self, frame_label: &str) -> Option<FrameNumber> {
|
pub fn frame_label_to_number(self, frame_label: WStr<'_>) -> Option<FrameNumber> {
|
||||||
// Frame labels are case insensitive.
|
// Frame labels are case insensitive (ASCII).
|
||||||
|
// TODO: Should be case sensitive in AVM2.
|
||||||
let label = frame_label.to_ascii_lowercase();
|
let label = frame_label.to_ascii_lowercase();
|
||||||
self.0.read().static_data.frame_labels.get(&label).copied()
|
self.0.read().static_data.frame_labels.get(&label).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scene_label_to_number(self, scene_label: &str) -> Option<FrameNumber> {
|
pub fn scene_label_to_number(self, scene_label: WStr<'_>) -> Option<FrameNumber> {
|
||||||
//TODO: Are scene labels also case insensitive?
|
// Never used in AVM1, so always be case sensitive.
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
.static_data
|
.static_data
|
||||||
.scene_labels
|
.scene_labels
|
||||||
.get(scene_label)
|
.get(&WString::from(scene_label))
|
||||||
.map(|Scene { start, .. }| start)
|
.map(|Scene { start, .. }| start)
|
||||||
.copied()
|
.copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frame_exists_within_scene(self, frame_label: &str, scene_label: &str) -> bool {
|
pub fn frame_exists_within_scene(self, frame_label: WStr<'_>, scene_label: WStr<'_>) -> bool {
|
||||||
let scene = self.scene_label_to_number(scene_label);
|
let scene = self.scene_label_to_number(scene_label);
|
||||||
let frame = self.frame_label_to_number(frame_label);
|
let frame = self.frame_label_to_number(frame_label);
|
||||||
|
|
||||||
|
@ -2132,9 +2137,11 @@ impl<'gc> TInteractiveObject<'gc> for MovieClip<'gc> {
|
||||||
event: ClipEvent,
|
event: ClipEvent,
|
||||||
) -> ClipEventResult {
|
) -> ClipEventResult {
|
||||||
let frame_name = match event {
|
let frame_name = match event {
|
||||||
ClipEvent::RollOut | ClipEvent::ReleaseOutside => Some("_up"),
|
ClipEvent::RollOut | ClipEvent::ReleaseOutside => Some(WStr::from_units(b"_up")),
|
||||||
ClipEvent::RollOver | ClipEvent::Release | ClipEvent::DragOut => Some("_over"),
|
ClipEvent::RollOver | ClipEvent::Release | ClipEvent::DragOut => {
|
||||||
ClipEvent::Press | ClipEvent::DragOver => Some("_down"),
|
Some(WStr::from_units(b"_over"))
|
||||||
|
}
|
||||||
|
ClipEvent::Press | ClipEvent::DragOver => Some(WStr::from_units(b"_down")),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3102,14 +3109,17 @@ impl<'gc, 'a> MovieClipData<'gc> {
|
||||||
reader: &mut SwfStream<'a>,
|
reader: &mut SwfStream<'a>,
|
||||||
tag_len: usize,
|
tag_len: usize,
|
||||||
cur_frame: FrameNumber,
|
cur_frame: FrameNumber,
|
||||||
static_data: &mut MovieClipStatic,
|
static_data: &mut MovieClipStatic<'gc>,
|
||||||
) -> DecodeResult {
|
) -> DecodeResult {
|
||||||
let frame_label = reader.read_frame_label(tag_len)?;
|
let frame_label = reader.read_frame_label(tag_len)?;
|
||||||
// Frame labels are case insensitive (ASCII).
|
let mut label = frame_label
|
||||||
let label = frame_label
|
|
||||||
.label
|
.label
|
||||||
.to_str_lossy(reader.encoding())
|
.to_str_lossy(reader.encoding())
|
||||||
.to_ascii_lowercase();
|
.into_owned();
|
||||||
|
|
||||||
|
// Frame labels are case insensitive (ASCII).
|
||||||
|
label.make_ascii_lowercase();
|
||||||
|
let label = WString::from_utf8_owned(label);
|
||||||
if let std::collections::hash_map::Entry::Vacant(v) = static_data.frame_labels.entry(label)
|
if let std::collections::hash_map::Entry::Vacant(v) = static_data.frame_labels.entry(label)
|
||||||
{
|
{
|
||||||
v.insert(cur_frame);
|
v.insert(cur_frame);
|
||||||
|
@ -3366,7 +3376,7 @@ impl<'gc, 'a> MovieClip<'gc> {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
pub name: String,
|
pub name: WString,
|
||||||
pub start: FrameNumber,
|
pub start: FrameNumber,
|
||||||
pub length: FrameNumber,
|
pub length: FrameNumber,
|
||||||
}
|
}
|
||||||
|
@ -3374,7 +3384,7 @@ pub struct Scene {
|
||||||
impl Default for Scene {
|
impl Default for Scene {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Scene {
|
Scene {
|
||||||
name: "".to_string(),
|
name: WString::default(),
|
||||||
start: 0,
|
start: 0,
|
||||||
length: u16::MAX,
|
length: u16::MAX,
|
||||||
}
|
}
|
||||||
|
@ -3388,9 +3398,9 @@ impl Default for Scene {
|
||||||
struct MovieClipStatic<'gc> {
|
struct MovieClipStatic<'gc> {
|
||||||
id: CharacterId,
|
id: CharacterId,
|
||||||
swf: SwfSlice,
|
swf: SwfSlice,
|
||||||
frame_labels: HashMap<String, FrameNumber>,
|
frame_labels: HashMap<WString, FrameNumber>,
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
scene_labels: HashMap<String, Scene>,
|
scene_labels: HashMap<WString, Scene>,
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
audio_stream_info: Option<swf::SoundStreamHead>,
|
audio_stream_info: Option<swf::SoundStreamHead>,
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
|
|
|
@ -174,6 +174,12 @@ macro_rules! impl_str_methods {
|
||||||
crate::string::ops::WStrToUtf8::new($deref).to_utf8_lossy()
|
crate::string::ops::WStrToUtf8::new($deref).to_utf8_lossy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a new string with all ASCII characters mapped to their lowercase equivalent.
|
||||||
|
#[inline]
|
||||||
|
pub fn to_ascii_lowercase($self: $receiver) -> crate::string::WString {
|
||||||
|
crate::string::ops::str_to_ascii_lowercase($deref)
|
||||||
|
}
|
||||||
|
|
||||||
/// Analogue of [`str::find`].
|
/// Analogue of [`str::find`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn find<$($pat_gen)* P: crate::string::Pattern<$pat_lt>>($self: $pat_self, pattern: P) -> Option<usize> {
|
pub fn find<$($pat_gen)* P: crate::string::Pattern<$pat_lt>>($self: $pat_self, pattern: P) -> Option<usize> {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::hash::Hasher;
|
||||||
use std::slice::Iter as SliceIter;
|
use std::slice::Iter as SliceIter;
|
||||||
|
|
||||||
use super::pattern::{SearchStep, Searcher};
|
use super::pattern::{SearchStep, Searcher};
|
||||||
use super::{utils, Pattern, Units, WStr};
|
use super::{utils, Pattern, Units, WStr, WString};
|
||||||
|
|
||||||
pub struct Iter<'a> {
|
pub struct Iter<'a> {
|
||||||
inner: Units<SliceIter<'a, u8>, SliceIter<'a, u16>>,
|
inner: Units<SliceIter<'a, u8>, SliceIter<'a, u16>>,
|
||||||
|
@ -117,6 +117,29 @@ pub fn str_hash<H: Hasher>(s: WStr<'_>, state: &mut H) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn map_latin1_chars(s: WStr<'_>, mut map: impl FnMut(u8) -> u8) -> WString {
|
||||||
|
match s.units() {
|
||||||
|
Units::Bytes(us) => {
|
||||||
|
let us: Vec<u8> = us.iter().map(|c| map(*c)).collect();
|
||||||
|
WString::from_buf(us)
|
||||||
|
}
|
||||||
|
Units::Wide(us) => {
|
||||||
|
let us: Vec<u16> = us
|
||||||
|
.iter()
|
||||||
|
.map(|c| match u8::try_from(*c) {
|
||||||
|
Ok(c) => map(c).into(),
|
||||||
|
Err(_) => *c,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
WString::from_buf(us)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn str_to_ascii_lowercase(s: WStr<'_>) -> WString {
|
||||||
|
map_latin1_chars(s, |c| c.to_ascii_lowercase())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn str_is_latin1(s: WStr<'_>) -> bool {
|
pub fn str_is_latin1(s: WStr<'_>) -> bool {
|
||||||
match s.units() {
|
match s.units() {
|
||||||
Units::Bytes(_) => true,
|
Units::Bytes(_) => true,
|
||||||
|
|
Loading…
Reference in New Issue