diff --git a/Jint/Extensions/Character.cs b/Jint/Extensions/Character.cs
index 4d65aa1b4..af05873c5 100644
--- a/Jint/Extensions/Character.cs
+++ b/Jint/Extensions/Character.cs
@@ -5,14 +5,13 @@ namespace Jint.Extensions;
internal static class Character
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsInRange(this char c, ushort min, ushort max) => (uint)(c - min) <= (uint)(max - min);
-
///
/// https://tc39.es/ecma262/#ASCII-word-characters
///
+ public const string AsciiWordCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsAsciiWordCharacter(this char c) => c == '_' || c.IsDecimalDigit() || c.IsInRange('a', 'z') || c.IsInRange('A', 'Z');
+ public static bool IsInRange(this char c, ushort min, ushort max) => (uint)(c - min) <= (uint)(max - min);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsOctalDigit(this char c) => c.IsInRange('0', '7');
diff --git a/Jint/Native/Global/GlobalObject.cs b/Jint/Native/Global/GlobalObject.cs
index d64b5c84c..8626086e2 100644
--- a/Jint/Native/Global/GlobalObject.cs
+++ b/Jint/Native/Global/GlobalObject.cs
@@ -274,8 +274,8 @@ private static JsValue IsFinite(JsValue thisObject, JsValue[] arguments)
private const string UriReservedString = ";/?:@&=+$,";
private const string UriUnescapedString = "-.!~*'()";
- private static readonly SearchValues UriUnescaped = SearchValues.Create(UriUnescapedString);
- private static readonly SearchValues UnescapedUriSet = SearchValues.Create(UriReservedString + UriUnescapedString + '#');
+ private static readonly SearchValues UriUnescaped = SearchValues.Create(Character.AsciiWordCharacters + UriUnescapedString);
+ private static readonly SearchValues UnescapedUriSet = SearchValues.Create(Character.AsciiWordCharacters + UriReservedString + UriUnescapedString + '#');
private static readonly SearchValues ReservedUriSet = SearchValues.Create(UriReservedString + '#');
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -300,7 +300,7 @@ private JsValue EncodeUriComponent(JsValue thisObject, JsValue[] arguments)
return Encode(uriString, UriUnescaped);
}
- private JsValue Encode(string uriString, SearchValues unescapedUriSet)
+ private JsValue Encode(string uriString, SearchValues allowedCharacters)
{
var strLen = uriString.Length;
var builder = new ValueStringBuilder(uriString.Length);
@@ -309,7 +309,7 @@ private JsValue Encode(string uriString, SearchValues unescapedUriSet)
for (var k = 0; k < strLen; k++)
{
var c = uriString[k];
- if (c.IsAsciiWordCharacter() || unescapedUriSet.Contains(c))
+ if (allowedCharacters.Contains(c))
{
builder.Append(c);
}
@@ -416,7 +416,7 @@ private JsValue Decode(string uriString, SearchValues? reservedSet)
_stringBuilder.Clear();
#if SUPPORTS_SPAN_PARSE
- Span octets = stackalloc byte[4];
+ Span octets = stackalloc byte[4];
#else
var octets = new byte[4];
#endif
@@ -576,6 +576,8 @@ private static bool IsDigit(char c, int radix, out int result)
return tmp < radix;
}
+ private static readonly SearchValues EscapeAllowList = SearchValues.Create(Character.AsciiWordCharacters + "@*+-./");
+
///
/// https://tc39.es/ecma262/#sec-escape-string
///
@@ -583,29 +585,27 @@ private JsValue Escape(JsValue thisObject, JsValue[] arguments)
{
var uriString = TypeConverter.ToString(arguments.At(0));
- var strLen = uriString.Length;
-
- _stringBuilder.EnsureCapacity(strLen);
- _stringBuilder.Clear();
+ var builder = new ValueStringBuilder(uriString.Length);
- for (var k = 0; k < strLen; k++)
+ foreach (var c in uriString)
{
- var c = uriString[k];
- if (c.IsAsciiWordCharacter() || c == '@' || c == '*' || c == '+' || c == '-' || c == '.' || c == '/')
+ if (EscapeAllowList.Contains(c))
{
- _stringBuilder.Append(c);
+ builder.Append(c);
}
else if (c < 256)
{
- _stringBuilder.Append('%').AppendFormat(CultureInfo.InvariantCulture, "{0:X2}", (int) c);
+ builder.Append('%');
+ builder.AppendHex((byte) c);
}
else
{
- _stringBuilder.Append("%u").AppendFormat(CultureInfo.InvariantCulture, "{0:X4}", (int) c);
+ builder.Append("%u");
+ builder.Append(((int) c).ToString("X4", CultureInfo.InvariantCulture));
}
}
- return _stringBuilder.ToString();
+ return builder.ToString();
}
///
diff --git a/Jint/Native/TypedArray/TypedArrayConstructor.Uint8Array.cs b/Jint/Native/TypedArray/TypedArrayConstructor.Uint8Array.cs
index 64e98a2be..892adc882 100644
--- a/Jint/Native/TypedArray/TypedArrayConstructor.Uint8Array.cs
+++ b/Jint/Native/TypedArray/TypedArrayConstructor.Uint8Array.cs
@@ -22,7 +22,12 @@ internal Uint8ArrayConstructor(
protected override void Initialize()
{
const PropertyFlag PropertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
- var properties = new PropertyDictionary(1, checkExistingKeys: false) { ["BYTES_PER_ELEMENT"] = new(new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.AllForbidden)), ["fromBase64"] = new(new ClrFunction(Engine, "fromBase64", FromBase64, 1, PropertyFlag.Configurable), PropertyFlags), ["fromHex"] = new(new ClrFunction(Engine, "fromHex", FromHex, 1, PropertyFlag.Configurable), PropertyFlags), };
+ var properties = new PropertyDictionary(3, checkExistingKeys: false)
+ {
+ ["BYTES_PER_ELEMENT"] = new(new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.AllForbidden)),
+ ["fromBase64"] = new(new ClrFunction(Engine, "fromBase64", FromBase64, 1, PropertyFlag.Configurable), PropertyFlags),
+ ["fromHex"] = new(new ClrFunction(Engine, "fromHex", FromHex, 1, PropertyFlag.Configurable), PropertyFlags),
+ };
SetProperties(properties);
}
@@ -92,6 +97,8 @@ internal static JsString GetAndValidateAlphabetOption(Engine engine, ObjectInsta
internal readonly record struct FromEncodingResult(byte[] Bytes, JavaScriptException? Error, int Read);
+ private static readonly SearchValues Base64Alphabet = SearchValues.Create("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
+
internal static FromEncodingResult FromBase64(Engine engine, string input, string alphabet, string lastChunkHandling, uint maxLength = uint.MaxValue)
{
if (maxLength == 0)
@@ -200,10 +207,7 @@ internal static FromEncodingResult FromBase64(Engine engine, string input, strin
}
}
- if (!currentChar.IsDecimalDigit()
- && !char.ToLowerInvariant(currentChar).IsInRange('a', 'z')
- && currentChar != '+'
- && currentChar != '/')
+ if (!Base64Alphabet.Contains(currentChar))
{
return new FromEncodingResult(bytes.ToArray(), ExceptionHelper.CreateSyntaxError(engine.Realm, "Invalid base64 character."), read);
}
diff --git a/Jint/Pooling/ValueStringBuilder.cs b/Jint/Pooling/ValueStringBuilder.cs
index 4252c3518..07763992d 100644
--- a/Jint/Pooling/ValueStringBuilder.cs
+++ b/Jint/Pooling/ValueStringBuilder.cs
@@ -192,11 +192,11 @@ public void Append(char c)
public void AppendHex(byte b)
{
const string Map = "0123456789ABCDEF";
- Span data = stackalloc char[]
- {
+ ReadOnlySpan data =
+ [
Map[b / 16],
- Map[b % 16],
- };
+ Map[b % 16]
+ ];
Append(data);
}