2020-07-12 22:16:48 +00:00
|
|
|
use gc_arena::{Collect, Gc, MutationContext};
|
|
|
|
use std::ops::Deref;
|
|
|
|
|
2021-09-13 14:30:53 +00:00
|
|
|
use super::{BorrowWStr, WStr, WString};
|
|
|
|
|
2020-07-22 19:55:49 +00:00
|
|
|
#[derive(Clone, Copy, Collect)]
|
2020-07-12 22:16:48 +00:00
|
|
|
#[collect(no_drop)]
|
2020-07-12 22:43:12 +00:00
|
|
|
enum Source<'gc> {
|
2021-09-13 14:30:53 +00:00
|
|
|
// Store the string both in UTF8 and UCS2, to be able to have
|
|
|
|
// both `impl Deref<&str>` and O(1) UCS2 char access.
|
|
|
|
// TODO(moulins): remove the extra `String`
|
|
|
|
Owned(Gc<'gc, (String, WString)>),
|
2021-09-15 09:59:43 +00:00
|
|
|
// Should be an ASCII string, for zero-copy conversion into `WStr<'_>`.
|
2020-07-12 22:43:12 +00:00
|
|
|
Static(&'static str),
|
|
|
|
}
|
|
|
|
|
2021-09-13 14:30:53 +00:00
|
|
|
#[derive(Clone, Copy, Collect)]
|
2020-07-12 22:43:12 +00:00
|
|
|
#[collect(no_drop)]
|
2020-07-13 10:09:30 +00:00
|
|
|
pub struct AvmString<'gc> {
|
2020-07-12 22:43:12 +00:00
|
|
|
source: Source<'gc>,
|
|
|
|
}
|
2020-07-12 22:16:48 +00:00
|
|
|
|
2020-07-13 10:09:30 +00:00
|
|
|
impl<'gc> AvmString<'gc> {
|
2020-07-12 22:16:48 +00:00
|
|
|
pub fn new<S: Into<String>>(gc_context: MutationContext<'gc, '_>, string: S) -> Self {
|
2021-09-13 14:30:53 +00:00
|
|
|
let utf8 = string.into();
|
|
|
|
let buf = WString::from_utf8(&utf8);
|
|
|
|
Self {
|
|
|
|
source: Source::Owned(Gc::allocate(gc_context, (utf8, buf))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_ucs2(gc_context: MutationContext<'gc, '_>, string: WString) -> Self {
|
|
|
|
// TODO(moulins): this loose unpaired surrogates
|
|
|
|
let utf8 = string.to_string();
|
2020-07-12 22:43:12 +00:00
|
|
|
Self {
|
2021-09-13 14:30:53 +00:00
|
|
|
source: Source::Owned(Gc::allocate(gc_context, (utf8, string))),
|
2020-07-12 22:43:12 +00:00
|
|
|
}
|
2020-07-12 22:16:48 +00:00
|
|
|
}
|
|
|
|
|
2021-09-17 09:24:12 +00:00
|
|
|
pub fn concat(
|
|
|
|
gc_context: MutationContext<'gc, '_>,
|
|
|
|
left: AvmString<'gc>,
|
|
|
|
right: AvmString<'gc>,
|
|
|
|
) -> AvmString<'gc> {
|
|
|
|
if left.is_empty() {
|
|
|
|
right
|
|
|
|
} else if right.is_empty() {
|
|
|
|
left
|
|
|
|
} else {
|
|
|
|
let mut out = WString::from(left.borrow());
|
|
|
|
out.push_str(right.borrow());
|
|
|
|
Self::new_ucs2(gc_context, out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-15 09:59:43 +00:00
|
|
|
#[inline]
|
2020-07-12 22:16:48 +00:00
|
|
|
pub fn as_str(&self) -> &str {
|
|
|
|
self
|
|
|
|
}
|
2021-09-13 14:30:53 +00:00
|
|
|
|
|
|
|
pub fn as_ucs2(&self) -> WStr<'_> {
|
|
|
|
match &self.source {
|
|
|
|
Source::Owned(str) => str.1.borrow(),
|
|
|
|
// `str` is valid ASCII, per invariant.
|
|
|
|
Source::Static(str) => WStr::from_units(str.as_bytes()),
|
|
|
|
}
|
|
|
|
}
|
2021-09-15 09:59:43 +00:00
|
|
|
|
|
|
|
impl_str_methods! {
|
|
|
|
lifetime: '_;
|
|
|
|
self: &Self;
|
|
|
|
deref: self.as_ucs2();
|
2021-09-17 19:12:11 +00:00
|
|
|
pattern['a,]: 'a, &'a Self;
|
2021-09-15 09:59:43 +00:00
|
|
|
}
|
2020-07-12 22:16:48 +00:00
|
|
|
}
|
|
|
|
|
2020-07-13 10:09:30 +00:00
|
|
|
impl Default for AvmString<'_> {
|
2020-07-12 23:29:04 +00:00
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
source: Source::Static(""),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-13 10:09:30 +00:00
|
|
|
impl<'gc> From<&'static str> for AvmString<'gc> {
|
2020-07-12 22:43:12 +00:00
|
|
|
fn from(str: &'static str) -> Self {
|
2021-09-13 14:30:53 +00:00
|
|
|
// TODO(moulins): actually check that `str` is valid ASCII.
|
2020-07-12 22:43:12 +00:00
|
|
|
Self {
|
|
|
|
source: Source::Static(str),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-13 10:09:30 +00:00
|
|
|
impl Deref for AvmString<'_> {
|
2020-07-12 22:16:48 +00:00
|
|
|
type Target = str;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn deref(&self) -> &str {
|
2020-07-12 22:43:12 +00:00
|
|
|
match &self.source {
|
2021-09-13 14:30:53 +00:00
|
|
|
Source::Owned(str) => str.0.deref(),
|
2020-07-12 22:43:12 +00:00
|
|
|
Source::Static(str) => str,
|
|
|
|
}
|
2020-07-12 22:16:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-13 10:09:30 +00:00
|
|
|
impl AsRef<str> for AvmString<'_> {
|
2020-07-12 22:16:48 +00:00
|
|
|
#[inline]
|
|
|
|
fn as_ref(&self) -> &str {
|
2020-07-12 22:43:12 +00:00
|
|
|
match &self.source {
|
2021-09-13 14:30:53 +00:00
|
|
|
Source::Owned(str) => &str.0,
|
2020-07-12 22:43:12 +00:00
|
|
|
Source::Static(str) => str,
|
|
|
|
}
|
2020-07-12 22:16:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-13 14:30:53 +00:00
|
|
|
impl<'gc> BorrowWStr for AvmString<'gc> {
|
2020-07-12 23:29:04 +00:00
|
|
|
#[inline]
|
2021-09-13 14:30:53 +00:00
|
|
|
fn borrow(&self) -> WStr<'_> {
|
|
|
|
self.as_ucs2()
|
2020-07-14 02:09:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-12 22:16:48 +00:00
|
|
|
macro_rules! impl_eq {
|
|
|
|
($lhs:ty, $rhs: ty) => {
|
2021-02-12 13:03:17 +00:00
|
|
|
#[allow(unused_lifetimes, clippy::redundant_slicing)]
|
2020-07-12 22:16:48 +00:00
|
|
|
impl<'a, 'b> PartialEq<$rhs> for $lhs {
|
|
|
|
#[inline]
|
|
|
|
fn eq(&self, other: &$rhs) -> bool {
|
|
|
|
PartialEq::eq(&self[..], &other[..])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-12 13:03:17 +00:00
|
|
|
#[allow(unused_lifetimes, clippy::redundant_slicing)]
|
2020-07-12 22:16:48 +00:00
|
|
|
impl<'a, 'b> PartialEq<$lhs> for $rhs {
|
|
|
|
#[inline]
|
|
|
|
fn eq(&self, other: &$lhs) -> bool {
|
|
|
|
PartialEq::eq(&self[..], &other[..])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-07-13 10:09:30 +00:00
|
|
|
impl_eq! { AvmString<'_>, str }
|
|
|
|
impl_eq! { AvmString<'_>, &'a str }
|
|
|
|
impl_eq! { AvmString<'_>, String }
|