#![deny(missing_docs)]
use crate::error::throw_type_error;
use crate::jsapi::AssertSameCompartment;
use crate::jsapi::JS;
use crate::jsapi::{ForOfIterator, ForOfIterator_NonIterableBehavior};
use crate::jsapi::{Heap, JS_DefineElement, JS_GetLatin1StringCharsAndLength};
use crate::jsapi::{JSContext, JSObject, JSString, RootedObject, RootedValue};
use crate::jsapi::{JS_DeprecatedStringHasLatin1Chars, JS_NewStringCopyUTF8N, JSPROP_ENUMERATE};
use crate::jsapi::{JS_GetTwoByteStringCharsAndLength, NewArrayObject1};
use crate::jsval::{BooleanValue, DoubleValue, Int32Value, NullValue, UInt32Value, UndefinedValue};
use crate::jsval::{JSVal, ObjectOrNullValue, ObjectValue, StringValue, SymbolValue};
use crate::rooted;
use crate::rust::maybe_wrap_value;
use crate::rust::{maybe_wrap_object_or_null_value, maybe_wrap_object_value, ToString};
use crate::rust::{HandleValue, MutableHandleValue};
use crate::rust::{ToBoolean, ToInt32, ToInt64, ToNumber, ToUint16, ToUint32, ToUint64};
use libc;
use log::debug;
use mozjs_sys::jsgc::Rooted;
use num_traits::PrimInt;
use std::borrow::Cow;
use std::ffi::CStr;
use std::mem;
use std::ptr::NonNull;
use std::rc::Rc;
use std::{ptr, slice};
trait As<O>: Copy {
fn cast(self) -> O;
}
macro_rules! impl_as {
($I:ty, $O:ty) => {
impl As<$O> for $I {
fn cast(self) -> $O {
self as $O
}
}
};
}
impl_as!(f64, u8);
impl_as!(f64, u16);
impl_as!(f64, u32);
impl_as!(f64, u64);
impl_as!(f64, i8);
impl_as!(f64, i16);
impl_as!(f64, i32);
impl_as!(f64, i64);
impl_as!(u8, f64);
impl_as!(u16, f64);
impl_as!(u32, f64);
impl_as!(u64, f64);
impl_as!(i8, f64);
impl_as!(i16, f64);
impl_as!(i32, f64);
impl_as!(i64, f64);
impl_as!(i32, i8);
impl_as!(i32, u8);
impl_as!(i32, i16);
impl_as!(u16, u16);
impl_as!(i32, i32);
impl_as!(u32, u32);
impl_as!(i64, i64);
impl_as!(u64, u64);
pub trait Number {
const ZERO: Self;
const MIN: Self;
const MAX: Self;
}
macro_rules! impl_num {
($N:ty, $zero:expr, $min:expr, $max:expr) => {
impl Number for $N {
const ZERO: $N = $zero;
const MIN: $N = $min;
const MAX: $N = $max;
}
};
}
impl_num!(u8, 0, u8::MIN, u8::MAX);
impl_num!(u16, 0, u16::MIN, u16::MAX);
impl_num!(u32, 0, u32::MIN, u32::MAX);
impl_num!(u64, 0, 0, (1 << 53) - 1);
impl_num!(i8, 0, i8::MIN, i8::MAX);
impl_num!(i16, 0, i16::MIN, i16::MAX);
impl_num!(i32, 0, i32::MIN, i32::MAX);
impl_num!(i64, 0, -(1 << 53) + 1, (1 << 53) - 1);
impl_num!(f32, 0.0, f32::MIN, f32::MAX);
impl_num!(f64, 0.0, f64::MIN, f64::MAX);
pub trait ToJSValConvertible {
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue);
fn safe_to_jsval(&self, cx: &mut crate::context::JSContext, rval: MutableHandleValue) {
unsafe { self.to_jsval(cx.raw_cx(), rval) }
}
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum ConversionResult<T> {
Success(T),
Failure(Cow<'static, CStr>),
}
impl<T> ConversionResult<T> {
pub fn get_success_value(&self) -> Option<&T> {
match *self {
ConversionResult::Success(ref v) => Some(v),
_ => None,
}
}
}
pub trait FromJSValConvertible: Sized {
type Config;
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
option: Self::Config,
) -> Result<ConversionResult<Self>, ()>;
fn safe_from_jsval(
cx: &mut crate::context::JSContext,
val: HandleValue,
option: Self::Config,
) -> Result<ConversionResult<Self>, ()> {
unsafe { Self::from_jsval(cx.raw_cx(), val, option) }
}
}
pub trait FromJSValConvertibleRc: Sized {
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
) -> Result<ConversionResult<Rc<Self>>, ()>;
fn safe_from_jsval(
cx: &mut crate::context::JSContext,
val: HandleValue,
) -> Result<ConversionResult<Rc<Self>>, ()> {
unsafe { Self::from_jsval(cx.raw_cx(), val) }
}
}
impl<T: FromJSValConvertibleRc> FromJSValConvertible for Rc<T> {
type Config = ();
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
_option: (),
) -> Result<ConversionResult<Rc<T>>, ()> {
<T as FromJSValConvertibleRc>::from_jsval(cx, val)
}
fn safe_from_jsval(
cx: &mut crate::context::JSContext,
val: HandleValue,
_option: (),
) -> Result<ConversionResult<Rc<T>>, ()> {
<T as FromJSValConvertibleRc>::safe_from_jsval(cx, val)
}
}
#[derive(PartialEq, Eq, Clone)]
pub enum ConversionBehavior {
Default,
EnforceRange,
Clamp,
}
unsafe fn enforce_range<D>(cx: *mut JSContext, d: f64) -> Result<ConversionResult<D>, ()>
where
D: Number + As<f64>,
f64: As<D>,
{
if d.is_infinite() {
throw_type_error(cx, c"value out of range in an EnforceRange argument");
return Err(());
}
let rounded = d.signum() * d.abs().floor();
if D::MIN.cast() <= rounded && rounded <= D::MAX.cast() {
Ok(ConversionResult::Success(rounded.cast()))
} else {
throw_type_error(cx, c"value out of range in an EnforceRange argument");
Err(())
}
}
fn clamp_to<D>(d: f64) -> D
where
D: Number + PrimInt + As<f64>,
f64: As<D>,
{
if d.is_nan() {
return D::ZERO;
}
if d >= D::MAX.cast() {
return D::MAX;
}
if d <= D::MIN.cast() {
return D::MIN;
}
debug_assert!(d.is_finite());
let to_truncate = if d < 0.0 { d - 0.5 } else { d + 0.5 };
let mut truncated: D = to_truncate.cast();
if truncated.cast() == to_truncate {
truncated = truncated & !D::one();
}
truncated
}
impl ToJSValConvertible for () {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(UndefinedValue());
}
}
impl FromJSValConvertible for JSVal {
type Config = ();
unsafe fn from_jsval(
_cx: *mut JSContext,
value: HandleValue,
_option: (),
) -> Result<ConversionResult<JSVal>, ()> {
Ok(ConversionResult::Success(value.get()))
}
}
impl ToJSValConvertible for JSVal {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(*self);
maybe_wrap_value(cx, rval);
}
}
impl<'a> ToJSValConvertible for HandleValue<'a> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(self.get());
maybe_wrap_value(cx, rval);
}
}
impl ToJSValConvertible for Heap<JSVal> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(self.get());
maybe_wrap_value(cx, rval);
}
}
#[inline]
unsafe fn convert_int_from_jsval<T, M>(
cx: *mut JSContext,
value: HandleValue,
option: ConversionBehavior,
convert_fn: unsafe fn(*mut JSContext, HandleValue) -> Result<M, ()>,
) -> Result<ConversionResult<T>, ()>
where
T: Number + As<f64> + PrimInt,
M: Number + As<T>,
f64: As<T>,
{
match option {
ConversionBehavior::Default => Ok(ConversionResult::Success(convert_fn(cx, value)?.cast())),
ConversionBehavior::EnforceRange => enforce_range(cx, ToNumber(cx, value)?),
ConversionBehavior::Clamp => Ok(ConversionResult::Success(clamp_to(ToNumber(cx, value)?))),
}
}
impl ToJSValConvertible for bool {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(BooleanValue(*self));
}
}
impl FromJSValConvertible for bool {
type Config = ();
unsafe fn from_jsval(
_cx: *mut JSContext,
val: HandleValue,
_option: (),
) -> Result<ConversionResult<bool>, ()> {
Ok(ToBoolean(val)).map(ConversionResult::Success)
}
}
impl ToJSValConvertible for i8 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(Int32Value(*self as i32));
}
}
impl FromJSValConvertible for i8 {
type Config = ConversionBehavior;
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
option: ConversionBehavior,
) -> Result<ConversionResult<i8>, ()> {
convert_int_from_jsval(cx, val, option, ToInt32)
}
}
impl ToJSValConvertible for u8 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(Int32Value(*self as i32));
}
}
impl FromJSValConvertible for u8 {
type Config = ConversionBehavior;
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
option: ConversionBehavior,
) -> Result<ConversionResult<u8>, ()> {
convert_int_from_jsval(cx, val, option, ToInt32)
}
}
impl ToJSValConvertible for i16 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(Int32Value(*self as i32));
}
}
impl FromJSValConvertible for i16 {
type Config = ConversionBehavior;
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
option: ConversionBehavior,
) -> Result<ConversionResult<i16>, ()> {
convert_int_from_jsval(cx, val, option, ToInt32)
}
}
impl ToJSValConvertible for u16 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(Int32Value(*self as i32));
}
}
impl FromJSValConvertible for u16 {
type Config = ConversionBehavior;
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
option: ConversionBehavior,
) -> Result<ConversionResult<u16>, ()> {
convert_int_from_jsval(cx, val, option, ToUint16)
}
}
impl ToJSValConvertible for i32 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(Int32Value(*self));
}
}
impl FromJSValConvertible for i32 {
type Config = ConversionBehavior;
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
option: ConversionBehavior,
) -> Result<ConversionResult<i32>, ()> {
convert_int_from_jsval(cx, val, option, ToInt32)
}
}
impl ToJSValConvertible for u32 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(UInt32Value(*self));
}
}
impl FromJSValConvertible for u32 {
type Config = ConversionBehavior;
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
option: ConversionBehavior,
) -> Result<ConversionResult<u32>, ()> {
convert_int_from_jsval(cx, val, option, ToUint32)
}
}
impl ToJSValConvertible for i64 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(DoubleValue(*self as f64));
}
}
impl FromJSValConvertible for i64 {
type Config = ConversionBehavior;
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
option: ConversionBehavior,
) -> Result<ConversionResult<i64>, ()> {
convert_int_from_jsval(cx, val, option, ToInt64)
}
}
impl ToJSValConvertible for u64 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(DoubleValue(*self as f64));
}
}
impl FromJSValConvertible for u64 {
type Config = ConversionBehavior;
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
option: ConversionBehavior,
) -> Result<ConversionResult<u64>, ()> {
convert_int_from_jsval(cx, val, option, ToUint64)
}
}
impl ToJSValConvertible for f32 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(DoubleValue(*self as f64));
}
}
impl FromJSValConvertible for f32 {
type Config = ();
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
_option: (),
) -> Result<ConversionResult<f32>, ()> {
let result = ToNumber(cx, val);
result.map(|f| f as f32).map(ConversionResult::Success)
}
}
impl ToJSValConvertible for f64 {
#[inline]
unsafe fn to_jsval(&self, _cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(DoubleValue(*self))
}
}
impl FromJSValConvertible for f64 {
type Config = ();
unsafe fn from_jsval(
cx: *mut JSContext,
val: HandleValue,
_option: (),
) -> Result<ConversionResult<f64>, ()> {
ToNumber(cx, val).map(ConversionResult::Success)
}
}
pub unsafe fn latin1_to_string(cx: *mut JSContext, s: NonNull<JSString>) -> String {
assert!(JS_DeprecatedStringHasLatin1Chars(s.as_ptr()));
let mut length = 0;
let chars = unsafe {
let chars = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), s.as_ptr(), &mut length);
assert!(!chars.is_null());
slice::from_raw_parts(chars, length as usize)
};
let mut v = vec![0; chars.len() * 2];
let real_size = encoding_rs::mem::convert_latin1_to_utf8(chars, v.as_mut_slice());
v.truncate(real_size);
unsafe { String::from_utf8_unchecked(v) }
}
pub unsafe fn jsstr_to_string(cx: *mut JSContext, jsstr: NonNull<JSString>) -> String {
if JS_DeprecatedStringHasLatin1Chars(jsstr.as_ptr()) {
return latin1_to_string(cx, jsstr);
}
let mut length = 0;
let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), jsstr.as_ptr(), &mut length);
assert!(!chars.is_null());
let char_vec = slice::from_raw_parts(chars, length as usize);
String::from_utf16_lossy(char_vec)
}
impl ToJSValConvertible for str {
#[inline]
#[deny(unsafe_op_in_unsafe_fn)]
unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
let s = Utf8Chars::from(self);
let jsstr = unsafe { JS_NewStringCopyUTF8N(cx, &*s as *const _) };
if jsstr.is_null() {
panic!("JS String copy routine failed");
}
unsafe {
rval.set(StringValue(&*jsstr));
}
}
}
impl ToJSValConvertible for String {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
(**self).to_jsval(cx, rval);
}
}
impl FromJSValConvertible for String {
type Config = ();
unsafe fn from_jsval(
cx: *mut JSContext,
value: HandleValue,
_: (),
) -> Result<ConversionResult<String>, ()> {
let jsstr = ToString(cx, value);
let Some(jsstr) = NonNull::new(jsstr) else {
debug!("ToString failed");
return Err(());
};
Ok(jsstr_to_string(cx, jsstr)).map(ConversionResult::Success)
}
}
impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
match self {
&Some(ref value) => value.to_jsval(cx, rval),
&None => rval.set(NullValue()),
}
}
}
impl<T: FromJSValConvertible> FromJSValConvertible for Option<T> {
type Config = T::Config;
unsafe fn from_jsval(
cx: *mut JSContext,
value: HandleValue,
option: T::Config,
) -> Result<ConversionResult<Option<T>>, ()> {
if value.get().is_null_or_undefined() {
Ok(ConversionResult::Success(None))
} else {
Ok(match FromJSValConvertible::from_jsval(cx, value, option)? {
ConversionResult::Success(v) => ConversionResult::Success(Some(v)),
ConversionResult::Failure(v) => ConversionResult::Failure(v),
})
}
}
}
impl<T: ToJSValConvertible> ToJSValConvertible for &'_ T {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
(**self).to_jsval(cx, rval)
}
}
impl<T: ToJSValConvertible> ToJSValConvertible for Box<T> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
(**self).to_jsval(cx, rval)
}
}
impl<T: ToJSValConvertible> ToJSValConvertible for Rc<T> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
(**self).to_jsval(cx, rval)
}
}
impl<T: ToJSValConvertible> ToJSValConvertible for [T] {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
rooted!(in(cx) let js_array = NewArrayObject1(cx, self.len() as libc::size_t));
assert!(!js_array.handle().is_null());
rooted!(in(cx) let mut val = UndefinedValue());
for (index, obj) in self.iter().enumerate() {
obj.to_jsval(cx, val.handle_mut());
assert!(JS_DefineElement(
cx,
js_array.handle().into(),
index as u32,
val.handle().into(),
JSPROP_ENUMERATE as u32
));
}
rval.set(ObjectValue(js_array.handle().get()));
}
}
impl<T: ToJSValConvertible> ToJSValConvertible for Vec<T> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
<[_]>::to_jsval(self, cx, rval)
}
}
struct ForOfIteratorGuard<'a> {
root: &'a mut ForOfIterator,
}
impl<'a> ForOfIteratorGuard<'a> {
fn new(cx: *mut JSContext, root: &'a mut ForOfIterator) -> Self {
unsafe {
Rooted::add_to_root_stack(&raw mut root.iterator, cx);
}
ForOfIteratorGuard { root }
}
}
impl<'a> Drop for ForOfIteratorGuard<'a> {
fn drop(&mut self) {
unsafe {
self.root.iterator.remove_from_root_stack();
}
}
}
impl<C: Clone, T: FromJSValConvertible<Config = C>> FromJSValConvertible for Vec<T> {
type Config = C;
unsafe fn from_jsval(
cx: *mut JSContext,
value: HandleValue,
option: C,
) -> Result<ConversionResult<Vec<T>>, ()> {
if !value.is_object() {
return Ok(ConversionResult::Failure(c"Value is not an object".into()));
}
#[allow(unused_variables)]
let zero = mem::zeroed();
let mut iterator = ForOfIterator {
cx_: cx,
iterator: RootedObject::new_unrooted(ptr::null_mut()),
nextMethod: RootedValue::new_unrooted(JSVal { asBits_: 0 }),
index: ::std::u32::MAX, ..zero
};
let iterator = ForOfIteratorGuard::new(cx, &mut iterator);
let iterator: &mut ForOfIterator = &mut *iterator.root;
if !iterator.init(
value.into(),
ForOfIterator_NonIterableBehavior::AllowNonIterable,
) {
return Err(());
}
if iterator.iterator.data.is_null() {
return Ok(ConversionResult::Failure(c"Value is not iterable".into()));
}
let mut ret = vec![];
loop {
let mut done = false;
rooted!(in(cx) let mut val = UndefinedValue());
if !iterator.next(val.handle_mut().into(), &mut done) {
return Err(());
}
if done {
break;
}
ret.push(match T::from_jsval(cx, val.handle(), option.clone())? {
ConversionResult::Success(v) => v,
ConversionResult::Failure(e) => {
throw_type_error(cx, e.as_ref());
return Err(());
}
});
}
Ok(ret).map(ConversionResult::Success)
}
}
impl ToJSValConvertible for *mut JSObject {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(ObjectOrNullValue(*self));
maybe_wrap_object_or_null_value(cx, rval);
}
}
impl ToJSValConvertible for ptr::NonNull<JSObject> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(ObjectValue(self.as_ptr()));
maybe_wrap_object_value(cx, rval);
}
}
impl ToJSValConvertible for Heap<*mut JSObject> {
#[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(ObjectOrNullValue(self.get()));
maybe_wrap_object_or_null_value(cx, rval);
}
}
impl FromJSValConvertible for *mut JSObject {
type Config = ();
#[inline]
unsafe fn from_jsval(
cx: *mut JSContext,
value: HandleValue,
_option: (),
) -> Result<ConversionResult<*mut JSObject>, ()> {
if !value.is_object() {
throw_type_error(cx, c"value is not an object");
return Err(());
}
AssertSameCompartment(cx, value.to_object());
Ok(ConversionResult::Success(value.to_object()))
}
}
impl ToJSValConvertible for *mut JS::Symbol {
#[inline]
unsafe fn to_jsval(&self, _: *mut JSContext, mut rval: MutableHandleValue) {
rval.set(SymbolValue(&**self));
}
}
impl FromJSValConvertible for *mut JS::Symbol {
type Config = ();
#[inline]
unsafe fn from_jsval(
cx: *mut JSContext,
value: HandleValue,
_option: (),
) -> Result<ConversionResult<*mut JS::Symbol>, ()> {
if !value.is_symbol() {
throw_type_error(cx, c"value is not a symbol");
return Err(());
}
Ok(ConversionResult::Success(value.to_symbol()))
}
}
pub struct Utf8Chars<'a> {
lt_marker: std::marker::PhantomData<&'a ()>,
inner: crate::jsapi::UTF8Chars,
}
impl<'a> std::ops::Deref for Utf8Chars<'a> {
type Target = crate::jsapi::UTF8Chars;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<'a> From<&'a str> for Utf8Chars<'a> {
#[allow(unsafe_code)]
fn from(value: &'a str) -> Self {
use std::marker::PhantomData;
use crate::jsapi::mozilla::{Range, RangedPtr};
use crate::jsapi::UTF8Chars;
let range = value.as_bytes().as_ptr_range();
let range_start = range.start as *mut _;
let range_end = range.end as *mut _;
let start = RangedPtr {
_phantom_0: PhantomData,
mPtr: range_start,
#[cfg(feature = "debugmozjs")]
mRangeStart: range_start,
#[cfg(feature = "debugmozjs")]
mRangeEnd: range_end,
};
let end = RangedPtr {
_phantom_0: PhantomData,
mPtr: range_end,
#[cfg(feature = "debugmozjs")]
mRangeStart: range_start,
#[cfg(feature = "debugmozjs")]
mRangeEnd: range_end,
};
let base = Range {
_phantom_0: PhantomData,
mStart: start,
mEnd: end,
};
let inner = UTF8Chars { _base: base };
Self {
lt_marker: PhantomData,
inner,
}
}
}