123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- using System.Text;
- using Jint.Extensions;
- using Jint.Native.ArrayBuffer;
- using Jint.Runtime;
- using Jint.Runtime.Descriptors;
- using Jint.Runtime.Interop;
- namespace Jint.Native.TypedArray;
- internal sealed class Uint8ArrayPrototype : Prototype
- {
- private readonly TypedArrayConstructor _constructor;
- public Uint8ArrayPrototype(
- Engine engine,
- IntrinsicTypedArrayPrototype objectPrototype,
- TypedArrayConstructor constructor)
- : base(engine, engine.Realm)
- {
- _prototype = objectPrototype;
- _constructor = constructor;
- }
- protected override void Initialize()
- {
- const PropertyFlag PropertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
- var properties = new PropertyDictionary(6, checkExistingKeys: false)
- {
- ["BYTES_PER_ELEMENT"] = new(JsNumber.PositiveOne, PropertyFlag.AllForbidden),
- ["constructor"] = new(_constructor, PropertyFlag.NonEnumerable),
- ["setFromBase64"] = new(new ClrFunction(Engine, "setFromBase64", SetFromBase64, 1, PropertyFlag.Configurable), PropertyFlags),
- ["setFromHex"] = new(new ClrFunction(Engine, "setFromHex", SetFromHex, 1, PropertyFlag.Configurable), PropertyFlags),
- ["toBase64"] = new(new ClrFunction(Engine, "toBase64", ToBase64, 0, PropertyFlag.Configurable), PropertyFlags),
- ["toHex"] = new(new ClrFunction(Engine, "toHex", ToHex, 0, PropertyFlag.Configurable), PropertyFlags),
- };
- SetProperties(properties);
- }
- private JsObject SetFromBase64(JsValue thisObject, JsCallArguments arguments)
- {
- var into = ValidateUint8Array(thisObject);
- var s = arguments.At(0);
- if (!s.IsString())
- {
- Throw.TypeError(_realm, "setFromBase64 must be called with a string");
- }
- var opts = Uint8ArrayConstructor.GetOptionsObject(_engine, arguments.At(1));
- var alphabet = Uint8ArrayConstructor.GetAndValidateAlphabetOption(_engine, opts);
- var lastChunkHandling = Uint8ArrayConstructor.GetAndValidateLastChunkHandling(_engine, opts);
- var taRecord = IntrinsicTypedArrayPrototype.MakeTypedArrayWithBufferWitnessRecord(into, ArrayBufferOrder.SeqCst);
- if (taRecord.IsTypedArrayOutOfBounds)
- {
- Throw.TypeError(_realm, "TypedArray is out of bounds");
- }
- var byteLength = taRecord.TypedArrayLength;
- var result = Uint8ArrayConstructor.FromBase64(_engine, s.ToString(), alphabet.ToString(), lastChunkHandling.ToString(), byteLength);
- SetUint8ArrayBytes(into, result.Bytes);
- if (result.Error is not null)
- {
- throw result.Error;
- }
- var resultObject = OrdinaryObjectCreate(_engine, _engine.Intrinsics.Object);
- resultObject.CreateDataPropertyOrThrow("read", result.Read);
- resultObject.CreateDataPropertyOrThrow("written", result.Bytes.Length);
- return resultObject;
- }
- private static void SetUint8ArrayBytes(JsTypedArray into, byte[] bytes)
- {
- var offset = into._byteOffset;
- var len = bytes.Length;
- var index = 0;
- while (index < len)
- {
- var b = bytes[index];
- var byteIndexInBuffer = index + offset;
- into._viewedArrayBuffer.SetValueInBuffer(byteIndexInBuffer, TypedArrayElementType.Uint8, b, isTypedArray: true, ArrayBufferOrder.Unordered);
- index++;
- }
- }
- private JsObject SetFromHex(JsValue thisObject, JsCallArguments arguments)
- {
- var into = ValidateUint8Array(thisObject);
- var s = arguments.At(0);
- if (!s.IsString())
- {
- Throw.TypeError(_realm, "setFromHex must be called with a string");
- }
- var taRecord = IntrinsicTypedArrayPrototype.MakeTypedArrayWithBufferWitnessRecord(into, ArrayBufferOrder.SeqCst);
- if (taRecord.IsTypedArrayOutOfBounds)
- {
- Throw.TypeError(_realm, "TypedArray is out of bounds");
- }
- var byteLength = taRecord.TypedArrayLength;
- var result = Uint8ArrayConstructor.FromHex(_engine, s.ToString(), byteLength);
- SetUint8ArrayBytes(into, result.Bytes);
- if (result.Error is not null)
- {
- throw result.Error;
- }
- var resultObject = OrdinaryObjectCreate(_engine, _engine.Intrinsics.Object);
- resultObject.CreateDataPropertyOrThrow("read", result.Read);
- resultObject.CreateDataPropertyOrThrow("written", result.Bytes.Length);
- return resultObject;
- }
- private JsValue ToBase64(JsValue thisObject, JsCallArguments arguments)
- {
- var o = ValidateUint8Array(thisObject);
- var opts = Uint8ArrayConstructor.GetOptionsObject(_engine, arguments.At(0));
- var alphabet = Uint8ArrayConstructor.GetAndValidateAlphabetOption(_engine, opts);
- var omitPadding = TypeConverter.ToBoolean(opts.Get("omitPadding"));
- #if NETCOREAPP
- var toEncode = GetUint8ArrayBytes(o);
- #else
- var toEncode = GetUint8ArrayBytes(o).ToArray();
- #endif
- string outAscii;
- if (alphabet == "base64")
- {
- outAscii = Convert.ToBase64String(toEncode);
- if (omitPadding)
- {
- outAscii = outAscii.TrimEnd('=');
- }
- }
- else
- {
- outAscii = WebEncoders.Base64UrlEncode(toEncode, omitPadding);
- }
- return outAscii;
- }
- private JsValue ToHex(JsValue thisObject, JsCallArguments arguments)
- {
- var o = ValidateUint8Array(thisObject);
- var toEncode = GetUint8ArrayBytes(o);
- using var outString = new ValueStringBuilder();
- foreach (var b in toEncode)
- {
- var b1 = (byte) (b >> 4);
- outString.Append((char) (b1 > 9 ? b1 - 10 + 'a' : b1 + '0'));
- var b2 = (byte) (b & 0x0F);
- outString.Append((char) (b2 > 9 ? b2 - 10 + 'a' : b2 + '0'));
- }
- return outString.ToString();
- }
- private ReadOnlySpan<byte> GetUint8ArrayBytes(JsTypedArray ta)
- {
- var buffer = ta._viewedArrayBuffer;
- var taRecord = IntrinsicTypedArrayPrototype.MakeTypedArrayWithBufferWitnessRecord(ta, ArrayBufferOrder.SeqCst);
- if (taRecord.IsTypedArrayOutOfBounds)
- {
- Throw.TypeError(_realm, "TypedArray is out of bounds");
- }
- return buffer._arrayBufferData!.AsSpan(0, (int) taRecord.TypedArrayLength);
- }
- private JsTypedArray ValidateUint8Array(JsValue ta)
- {
- if (ta is not JsTypedArray { _arrayElementType: TypedArrayElementType.Uint8 } typedArray)
- {
- Throw.TypeError(_engine.Realm, "Not a Uint8Array");
- return default;
- }
- return typedArray;
- }
- }
|