| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| /// Specialized integers and floating point numbers, |
| /// with SIMD support and efficient lists. |
| library dart.typed_data.implementation; |
| |
| import 'dart:collection'; |
| import 'dart:_internal'; |
| import 'dart:_interceptors' show JSIndexable; |
| import 'dart:_js_helper' |
| show |
| Creates, |
| JavaScriptIndexingBehavior, |
| JSName, |
| Native, |
| Returns, |
| diagnoseIndexError, |
| diagnoseRangeError; |
| import 'dart:_foreign_helper' show JS; |
| import 'dart:math' as Math; |
| |
| import 'dart:typed_data'; |
| |
| @Native('ArrayBuffer') |
| class NativeByteBuffer implements ByteBuffer { |
| @JSName('byteLength') |
| external int get lengthInBytes; |
| |
| Type get runtimeType => ByteBuffer; |
| |
| Uint8List asUint8List([int offsetInBytes = 0, int? length]) { |
| return NativeUint8List.view(this, offsetInBytes, length); |
| } |
| |
| Int8List asInt8List([int offsetInBytes = 0, int? length]) { |
| return NativeInt8List.view(this, offsetInBytes, length); |
| } |
| |
| Uint8ClampedList asUint8ClampedList([int offsetInBytes = 0, int? length]) { |
| return NativeUint8ClampedList.view(this, offsetInBytes, length); |
| } |
| |
| Uint16List asUint16List([int offsetInBytes = 0, int? length]) { |
| return NativeUint16List.view(this, offsetInBytes, length); |
| } |
| |
| Int16List asInt16List([int offsetInBytes = 0, int? length]) { |
| return NativeInt16List.view(this, offsetInBytes, length); |
| } |
| |
| Uint32List asUint32List([int offsetInBytes = 0, int? length]) { |
| return NativeUint32List.view(this, offsetInBytes, length); |
| } |
| |
| Int32List asInt32List([int offsetInBytes = 0, int? length]) { |
| return NativeInt32List.view(this, offsetInBytes, length); |
| } |
| |
| Uint64List asUint64List([int offsetInBytes = 0, int? length]) { |
| throw UnsupportedError("Uint64List not supported by dart2js."); |
| } |
| |
| Int64List asInt64List([int offsetInBytes = 0, int? length]) { |
| throw UnsupportedError("Int64List not supported by dart2js."); |
| } |
| |
| Int32x4List asInt32x4List([int offsetInBytes = 0, int? length]) { |
| length ??= (lengthInBytes - offsetInBytes) ~/ Int32x4List.bytesPerElement; |
| var storage = this.asInt32List(offsetInBytes, length * 4); |
| return NativeInt32x4List._externalStorage(storage); |
| } |
| |
| Float32List asFloat32List([int offsetInBytes = 0, int? length]) { |
| return NativeFloat32List.view(this, offsetInBytes, length); |
| } |
| |
| Float64List asFloat64List([int offsetInBytes = 0, int? length]) { |
| return NativeFloat64List.view(this, offsetInBytes, length); |
| } |
| |
| Float32x4List asFloat32x4List([int offsetInBytes = 0, int? length]) { |
| length ??= (lengthInBytes - offsetInBytes) ~/ Float32x4List.bytesPerElement; |
| var storage = this.asFloat32List(offsetInBytes, length * 4); |
| return NativeFloat32x4List._externalStorage(storage); |
| } |
| |
| Float64x2List asFloat64x2List([int offsetInBytes = 0, int? length]) { |
| length ??= (lengthInBytes - offsetInBytes) ~/ Float64x2List.bytesPerElement; |
| var storage = this.asFloat64List(offsetInBytes, length * 2); |
| return NativeFloat64x2List._externalStorage(storage); |
| } |
| |
| ByteData asByteData([int offsetInBytes = 0, int? length]) { |
| return NativeByteData.view(this, offsetInBytes, length); |
| } |
| } |
| |
| /// A fixed-length list of Float32x4 numbers that is viewable as a |
| /// [TypedData]. For long lists, this implementation will be considerably more |
| /// space- and time-efficient than the default [List] implementation. |
| class NativeFloat32x4List extends Object |
| with ListMixin<Float32x4>, FixedLengthListMixin<Float32x4> |
| implements Float32x4List { |
| final Float32List _storage; |
| |
| /// Creates a [Float32x4List] of the specified length (in elements), |
| /// all of whose elements are initially zero. |
| NativeFloat32x4List(int length) : _storage = NativeFloat32List(length * 4); |
| |
| NativeFloat32x4List._externalStorage(this._storage); |
| |
| NativeFloat32x4List._slowFromList(List<Float32x4> list) |
| : _storage = NativeFloat32List(list.length * 4) { |
| for (int i = 0; i < list.length; i++) { |
| var e = list[i]; |
| _storage[(i * 4) + 0] = e.x; |
| _storage[(i * 4) + 1] = e.y; |
| _storage[(i * 4) + 2] = e.z; |
| _storage[(i * 4) + 3] = e.w; |
| } |
| } |
| |
| Type get runtimeType => Float32x4List; |
| |
| /// Creates a [Float32x4List] with the same size as the [elements] list |
| /// and copies over the elements. |
| factory NativeFloat32x4List.fromList(List<Float32x4> list) { |
| if (list is NativeFloat32x4List) { |
| return NativeFloat32x4List._externalStorage( |
| NativeFloat32List.fromList(list._storage)); |
| } else { |
| return NativeFloat32x4List._slowFromList(list); |
| } |
| } |
| |
| ByteBuffer get buffer => _storage.buffer; |
| |
| int get lengthInBytes => _storage.lengthInBytes; |
| |
| int get offsetInBytes => _storage.offsetInBytes; |
| |
| int get elementSizeInBytes => Float32x4List.bytesPerElement; |
| |
| int get length => _storage.length ~/ 4; |
| |
| Float32x4 operator [](int index) { |
| _checkValidIndex(index, this, this.length); |
| double _x = _storage[(index * 4) + 0]; |
| double _y = _storage[(index * 4) + 1]; |
| double _z = _storage[(index * 4) + 2]; |
| double _w = _storage[(index * 4) + 3]; |
| return NativeFloat32x4._truncated(_x, _y, _z, _w); |
| } |
| |
| void operator []=(int index, Float32x4 value) { |
| _checkValidIndex(index, this, this.length); |
| _storage[(index * 4) + 0] = value.x; |
| _storage[(index * 4) + 1] = value.y; |
| _storage[(index * 4) + 2] = value.z; |
| _storage[(index * 4) + 3] = value.w; |
| } |
| |
| Float32x4List sublist(int start, [int? end]) { |
| var stop = _checkValidRange(start, end, this.length); |
| return NativeFloat32x4List._externalStorage( |
| _storage.sublist(start * 4, stop * 4)); |
| } |
| } |
| |
| /// A fixed-length list of Int32x4 numbers that is viewable as a |
| /// [TypedData]. For long lists, this implementation will be considerably more |
| /// space- and time-efficient than the default [List] implementation. |
| class NativeInt32x4List extends Object |
| with ListMixin<Int32x4>, FixedLengthListMixin<Int32x4> |
| implements Int32x4List { |
| final Int32List _storage; |
| |
| /// Creates a [Int32x4List] of the specified length (in elements), |
| /// all of whose elements are initially zero. |
| NativeInt32x4List(int length) : _storage = NativeInt32List(length * 4); |
| |
| NativeInt32x4List._externalStorage(Int32List storage) : _storage = storage; |
| |
| NativeInt32x4List._slowFromList(List<Int32x4> list) |
| : _storage = NativeInt32List(list.length * 4) { |
| for (int i = 0; i < list.length; i++) { |
| var e = list[i]; |
| _storage[(i * 4) + 0] = e.x; |
| _storage[(i * 4) + 1] = e.y; |
| _storage[(i * 4) + 2] = e.z; |
| _storage[(i * 4) + 3] = e.w; |
| } |
| } |
| |
| Type get runtimeType => Int32x4List; |
| |
| /// Creates a [Int32x4List] with the same size as the [elements] list |
| /// and copies over the elements. |
| factory NativeInt32x4List.fromList(List<Int32x4> list) { |
| if (list is NativeInt32x4List) { |
| return NativeInt32x4List._externalStorage( |
| NativeInt32List.fromList(list._storage)); |
| } else { |
| return NativeInt32x4List._slowFromList(list); |
| } |
| } |
| |
| ByteBuffer get buffer => _storage.buffer; |
| |
| int get lengthInBytes => _storage.lengthInBytes; |
| |
| int get offsetInBytes => _storage.offsetInBytes; |
| |
| int get elementSizeInBytes => Int32x4List.bytesPerElement; |
| |
| int get length => _storage.length ~/ 4; |
| |
| Int32x4 operator [](int index) { |
| _checkValidIndex(index, this, this.length); |
| int _x = _storage[(index * 4) + 0]; |
| int _y = _storage[(index * 4) + 1]; |
| int _z = _storage[(index * 4) + 2]; |
| int _w = _storage[(index * 4) + 3]; |
| return NativeInt32x4._truncated(_x, _y, _z, _w); |
| } |
| |
| void operator []=(int index, Int32x4 value) { |
| _checkValidIndex(index, this, this.length); |
| _storage[(index * 4) + 0] = value.x; |
| _storage[(index * 4) + 1] = value.y; |
| _storage[(index * 4) + 2] = value.z; |
| _storage[(index * 4) + 3] = value.w; |
| } |
| |
| Int32x4List sublist(int start, [int? end]) { |
| var stop = _checkValidRange(start, end, this.length); |
| return NativeInt32x4List._externalStorage( |
| _storage.sublist(start * 4, stop * 4)); |
| } |
| } |
| |
| /// A fixed-length list of Float64x2 numbers that is viewable as a |
| /// [TypedData]. For long lists, this implementation will be considerably more |
| /// space- and time-efficient than the default [List] implementation. |
| class NativeFloat64x2List extends Object |
| with ListMixin<Float64x2>, FixedLengthListMixin<Float64x2> |
| implements Float64x2List { |
| final Float64List _storage; |
| |
| /// Creates a [Float64x2List] of the specified length (in elements), |
| /// all of whose elements are initially zero. |
| NativeFloat64x2List(int length) : _storage = NativeFloat64List(length * 2); |
| |
| NativeFloat64x2List._externalStorage(this._storage); |
| |
| NativeFloat64x2List._slowFromList(List<Float64x2> list) |
| : _storage = NativeFloat64List(list.length * 2) { |
| for (int i = 0; i < list.length; i++) { |
| var e = list[i]; |
| _storage[(i * 2) + 0] = e.x; |
| _storage[(i * 2) + 1] = e.y; |
| } |
| } |
| |
| /// Creates a [Float64x2List] with the same size as the [elements] list |
| /// and copies over the elements. |
| factory NativeFloat64x2List.fromList(List<Float64x2> list) { |
| if (list is NativeFloat64x2List) { |
| return NativeFloat64x2List._externalStorage( |
| NativeFloat64List.fromList(list._storage)); |
| } else { |
| return NativeFloat64x2List._slowFromList(list); |
| } |
| } |
| |
| Type get runtimeType => Float64x2List; |
| |
| ByteBuffer get buffer => _storage.buffer; |
| |
| int get lengthInBytes => _storage.lengthInBytes; |
| |
| int get offsetInBytes => _storage.offsetInBytes; |
| |
| int get elementSizeInBytes => Float64x2List.bytesPerElement; |
| |
| int get length => _storage.length ~/ 2; |
| |
| Float64x2 operator [](int index) { |
| _checkValidIndex(index, this, this.length); |
| double _x = _storage[(index * 2) + 0]; |
| double _y = _storage[(index * 2) + 1]; |
| return Float64x2(_x, _y); |
| } |
| |
| void operator []=(int index, Float64x2 value) { |
| _checkValidIndex(index, this, this.length); |
| _storage[(index * 2) + 0] = value.x; |
| _storage[(index * 2) + 1] = value.y; |
| } |
| |
| Float64x2List sublist(int start, [int? end]) { |
| var stop = _checkValidRange(start, end, this.length); |
| return NativeFloat64x2List._externalStorage( |
| _storage.sublist(start * 2, stop * 2)); |
| } |
| } |
| |
| @Native('ArrayBufferView') |
| class NativeTypedData implements TypedData { |
| /// Returns the byte buffer associated with this object. |
| @Creates('NativeByteBuffer') |
| @Returns('NativeByteBuffer') |
| external ByteBuffer get buffer; |
| |
| /// Returns the length of this view, in bytes. |
| @JSName('byteLength') |
| external int get lengthInBytes; |
| |
| /// Returns the offset in bytes into the underlying byte buffer of this view. |
| @JSName('byteOffset') |
| external int get offsetInBytes; |
| |
| /// Returns the number of bytes in the representation of each element in this |
| /// list. |
| @JSName('BYTES_PER_ELEMENT') |
| external int get elementSizeInBytes; |
| |
| void _invalidPosition(int position, int length, String name) { |
| if (position is! int) { |
| throw ArgumentError.value(position, name, 'Invalid list position'); |
| } else { |
| throw RangeError.range(position, 0, length, name); |
| } |
| } |
| |
| void _checkPosition(int position, int length, String name) { |
| if (JS<bool>('!', '(# >>> 0) !== #', position, position) || |
| JS<int>('!', '#', position) > length) { |
| // 'int' guaranteed by above test. |
| _invalidPosition(position, length, name); |
| } |
| } |
| } |
| |
| // Validates the unnamed constructor length argument. Checking is necessary |
| // because passing unvalidated values to the native constructors can cause |
| // conversions or create views. |
| int _checkLength(length) { |
| if (length is! int) throw ArgumentError('Invalid length $length'); |
| return length; |
| } |
| |
| // Validates `.view` constructor arguments. Checking is necessary because |
| // passing unvalidated values to the native constructors can cause conversions |
| // (e.g. String arguments) or create typed data objects that are not actually |
| // views of the input. |
| void _checkViewArguments(buffer, offsetInBytes, length) { |
| if (buffer is! NativeByteBuffer) { |
| throw ArgumentError('Invalid view buffer'); |
| } |
| if (offsetInBytes is! int) { |
| throw ArgumentError('Invalid view offsetInBytes $offsetInBytes'); |
| } |
| if (length is! int?) { |
| throw ArgumentError('Invalid view length $length'); |
| } |
| } |
| |
| // Ensures that [list] is a JavaScript Array or a typed array. If necessary, |
| // returns a copy of the list. |
| List _ensureNativeList(List list) { |
| if (list is JSIndexable) return list; |
| List result = List.filled(list.length, null); |
| for (int i = 0; i < list.length; i++) { |
| result[i] = list[i]; |
| } |
| return result; |
| } |
| |
| @Native('DataView') |
| class NativeByteData extends NativeTypedData implements ByteData { |
| /// Creates a [ByteData] of the specified length (in elements), all of |
| /// whose elements are initially zero. |
| factory NativeByteData(int length) => _create1(_checkLength(length)); |
| |
| /// Creates an [ByteData] _view_ of the specified region in the specified |
| /// byte buffer. Changes in the [ByteData] will be visible in the byte |
| /// buffer and vice versa. If the [offsetInBytes] index of the region is not |
| /// specified, it defaults to zero (the first byte in the byte buffer). |
| /// If the length is not specified, it defaults to null, which indicates |
| /// that the view extends to the end of the byte buffer. |
| /// |
| /// The [offsetInBytes] and [length] must be non-negative, and |
| /// [offsetInBytes] + ([length] * elementSizeInBytes) must be less than or |
| /// equal to the length of [buffer]. |
| factory NativeByteData.view( |
| ByteBuffer buffer, int offsetInBytes, int? length) { |
| _checkViewArguments(buffer, offsetInBytes, length); |
| return length == null |
| ? _create2(buffer, offsetInBytes) |
| : _create3(buffer, offsetInBytes, length); |
| } |
| |
| Type get runtimeType => ByteData; |
| |
| int get elementSizeInBytes => 1; |
| |
| /// Returns the floating point number represented by the four bytes at |
| /// the specified [byteOffset] in this object, in IEEE 754 |
| /// single-precision binary floating-point format (binary32). |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 4` must be less than or equal to the length of this object. |
| double getFloat32(int byteOffset, [Endian endian = Endian.big]) => |
| _getFloat32(byteOffset, Endian.little == endian); |
| |
| @JSName('getFloat32') |
| @Returns('double') |
| double _getFloat32(int byteOffset, [bool? littleEndian]) native; |
| |
| /// Returns the floating point number represented by the eight bytes at |
| /// the specified [byteOffset] in this object, in IEEE 754 |
| /// double-precision binary floating-point format (binary64). |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 8` must be less than or equal to the length of this object. |
| double getFloat64(int byteOffset, [Endian endian = Endian.big]) => |
| _getFloat64(byteOffset, Endian.little == endian); |
| |
| @JSName('getFloat64') |
| @Returns('double') |
| double _getFloat64(int byteOffset, [bool? littleEndian]) native; |
| |
| /// Returns the (possibly negative) integer represented by the two bytes at |
| /// the specified [byteOffset] in this object, in two's complement binary |
| /// form. |
| /// The return value will be between 2<sup>15</sup> and 2<sup>15</sup> - 1, |
| /// inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 2` must be less than or equal to the length of this object. |
| int getInt16(int byteOffset, [Endian endian = Endian.big]) => |
| _getInt16(byteOffset, Endian.little == endian); |
| |
| @JSName('getInt16') |
| @Returns('int') |
| int _getInt16(int byteOffset, [bool? littleEndian]) native; |
| |
| /// Returns the (possibly negative) integer represented by the four bytes at |
| /// the specified [byteOffset] in this object, in two's complement binary |
| /// form. |
| /// The return value will be between 2<sup>31</sup> and 2<sup>31</sup> - 1, |
| /// inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 4` must be less than or equal to the length of this object. |
| int getInt32(int byteOffset, [Endian endian = Endian.big]) => |
| _getInt32(byteOffset, Endian.little == endian); |
| |
| @JSName('getInt32') |
| @Returns('int') |
| int _getInt32(int byteOffset, [bool? littleEndian]) native; |
| |
| /// Returns the (possibly negative) integer represented by the eight bytes at |
| /// the specified [byteOffset] in this object, in two's complement binary |
| /// form. |
| /// The return value will be between 2<sup>63</sup> and 2<sup>63</sup> - 1, |
| /// inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 8` must be less than or equal to the length of this object. |
| int getInt64(int byteOffset, [Endian endian = Endian.big]) { |
| throw UnsupportedError('Int64 accessor not supported by dart2js.'); |
| } |
| |
| /// Returns the (possibly negative) integer represented by the byte at the |
| /// specified [byteOffset] in this object, in two's complement binary |
| /// representation. The return value will be between -128 and 127, inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// less than the length of this object. |
| int getInt8(int byteOffset) native; |
| |
| /// Returns the positive integer represented by the two bytes starting |
| /// at the specified [byteOffset] in this object, in unsigned binary |
| /// form. |
| /// The return value will be between 0 and 2<sup>16</sup> - 1, inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 2` must be less than or equal to the length of this object. |
| int getUint16(int byteOffset, [Endian endian = Endian.big]) => |
| _getUint16(byteOffset, Endian.little == endian); |
| |
| @JSName('getUint16') |
| @Returns('int') |
| int _getUint16(int byteOffset, [bool? littleEndian]) native; |
| |
| /// Returns the positive integer represented by the four bytes starting |
| /// at the specified [byteOffset] in this object, in unsigned binary |
| /// form. |
| /// The return value will be between 0 and 2<sup>32</sup> - 1, inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 4` must be less than or equal to the length of this object. |
| int getUint32(int byteOffset, [Endian endian = Endian.big]) => |
| _getUint32(byteOffset, Endian.little == endian); |
| |
| @JSName('getUint32') |
| @Returns('int') |
| int _getUint32(int byteOffset, [bool? littleEndian]) native; |
| |
| /// Returns the positive integer represented by the eight bytes starting |
| /// at the specified [byteOffset] in this object, in unsigned binary |
| /// form. |
| /// The return value will be between 0 and 2<sup>64</sup> - 1, inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 8` must be less than or equal to the length of this object. |
| int getUint64(int byteOffset, [Endian endian = Endian.big]) { |
| throw UnsupportedError('Uint64 accessor not supported by dart2js.'); |
| } |
| |
| /// Returns the positive integer represented by the byte at the specified |
| /// [byteOffset] in this object, in unsigned binary form. The |
| /// return value will be between 0 and 255, inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// less than the length of this object. |
| int getUint8(int byteOffset) native; |
| |
| /// Sets the four bytes starting at the specified [byteOffset] in this |
| /// object to the IEEE 754 single-precision binary floating-point |
| /// (binary32) representation of the specified [value]. |
| /// |
| /// **Note that this method can lose precision.** The input [value] is |
| /// a 64-bit floating point value, which will be converted to 32-bit |
| /// floating point value by IEEE 754 rounding rules before it is stored. |
| /// If [value] cannot be represented exactly as a binary32, it will be |
| /// converted to the nearest binary32 value. If two binary32 values are |
| /// equally close, the one whose least significant bit is zero will be used. |
| /// Note that finite (but large) values can be converted to infinity, and |
| /// small non-zero values can be converted to zero. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 4` must be less than or equal to the length of this object. |
| void setFloat32(int byteOffset, num value, [Endian endian = Endian.big]) => |
| _setFloat32(byteOffset, value, Endian.little == endian); |
| |
| @JSName('setFloat32') |
| void _setFloat32(int byteOffset, num value, [bool? littleEndian]) native; |
| |
| /// Sets the eight bytes starting at the specified [byteOffset] in this |
| /// object to the IEEE 754 double-precision binary floating-point |
| /// (binary64) representation of the specified [value]. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 8` must be less than or equal to the length of this object. |
| void setFloat64(int byteOffset, num value, [Endian endian = Endian.big]) => |
| _setFloat64(byteOffset, value, Endian.little == endian); |
| |
| @JSName('setFloat64') |
| void _setFloat64(int byteOffset, num value, [bool? littleEndian]) native; |
| |
| /// Sets the two bytes starting at the specified [byteOffset] in this |
| /// object to the two's complement binary representation of the specified |
| /// [value], which must fit in two bytes. In other words, [value] must lie |
| /// between 2<sup>15</sup> and 2<sup>15</sup> - 1, inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 2` must be less than or equal to the length of this object. |
| void setInt16(int byteOffset, int value, [Endian endian = Endian.big]) => |
| _setInt16(byteOffset, value, Endian.little == endian); |
| |
| @JSName('setInt16') |
| void _setInt16(int byteOffset, int value, [bool? littleEndian]) native; |
| |
| /// Sets the four bytes starting at the specified [byteOffset] in this |
| /// object to the two's complement binary representation of the specified |
| /// [value], which must fit in four bytes. In other words, [value] must lie |
| /// between 2<sup>31</sup> and 2<sup>31</sup> - 1, inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 4` must be less than or equal to the length of this object. |
| void setInt32(int byteOffset, int value, [Endian endian = Endian.big]) => |
| _setInt32(byteOffset, value, Endian.little == endian); |
| |
| @JSName('setInt32') |
| void _setInt32(int byteOffset, int value, [bool? littleEndian]) native; |
| |
| /// Sets the eight bytes starting at the specified [byteOffset] in this |
| /// object to the two's complement binary representation of the specified |
| /// [value], which must fit in eight bytes. In other words, [value] must lie |
| /// between 2<sup>63</sup> and 2<sup>63</sup> - 1, inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 8` must be less than or equal to the length of this object. |
| void setInt64(int byteOffset, int value, [Endian endian = Endian.big]) { |
| throw UnsupportedError('Int64 accessor not supported by dart2js.'); |
| } |
| |
| /// Sets the byte at the specified [byteOffset] in this object to the |
| /// two's complement binary representation of the specified [value], which |
| /// must fit in a single byte. In other words, [value] must be between |
| /// -128 and 127, inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// less than the length of this object. |
| void setInt8(int byteOffset, int value) native; |
| |
| /// Sets the two bytes starting at the specified [byteOffset] in this object |
| /// to the unsigned binary representation of the specified [value], |
| /// which must fit in two bytes. in other words, [value] must be between |
| /// 0 and 2<sup>16</sup> - 1, inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 2` must be less than or equal to the length of this object. |
| void setUint16(int byteOffset, int value, [Endian endian = Endian.big]) => |
| _setUint16(byteOffset, value, Endian.little == endian); |
| |
| @JSName('setUint16') |
| void _setUint16(int byteOffset, int value, [bool? littleEndian]) native; |
| |
| /// Sets the four bytes starting at the specified [byteOffset] in this object |
| /// to the unsigned binary representation of the specified [value], |
| /// which must fit in four bytes. in other words, [value] must be between |
| /// 0 and 2<sup>32</sup> - 1, inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 4` must be less than or equal to the length of this object. |
| void setUint32(int byteOffset, int value, [Endian endian = Endian.big]) => |
| _setUint32(byteOffset, value, Endian.little == endian); |
| |
| @JSName('setUint32') |
| void _setUint32(int byteOffset, int value, [bool? littleEndian]) native; |
| |
| /// Sets the eight bytes starting at the specified [byteOffset] in this object |
| /// to the unsigned binary representation of the specified [value], |
| /// which must fit in eight bytes. in other words, [value] must be between |
| /// 0 and 2<sup>64</sup> - 1, inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// `byteOffset + 8` must be less than or equal to the length of this object. |
| void setUint64(int byteOffset, int value, [Endian endian = Endian.big]) { |
| throw UnsupportedError('Uint64 accessor not supported by dart2js.'); |
| } |
| |
| /// Sets the byte at the specified [byteOffset] in this object to the |
| /// unsigned binary representation of the specified [value], which must fit |
| /// in a single byte. in other words, [value] must be between 0 and 255, |
| /// inclusive. |
| /// |
| /// The [byteOffset] must be non-negative, and |
| /// less than the length of this object. |
| void setUint8(int byteOffset, int value) native; |
| |
| static NativeByteData _create1(arg) => |
| JS('NativeByteData', 'new DataView(new ArrayBuffer(#))', arg); |
| |
| static NativeByteData _create2(arg1, arg2) => |
| JS('NativeByteData', 'new DataView(#, #)', arg1, arg2); |
| |
| static NativeByteData _create3(arg1, arg2, arg3) => |
| JS('NativeByteData', 'new DataView(#, #, #)', arg1, arg2, arg3); |
| } |
| |
| abstract class NativeTypedArray<E> extends NativeTypedData |
| implements JavaScriptIndexingBehavior<E> { |
| int get length; |
| |
| void _setRangeFast( |
| int start, int end, NativeTypedArray source, int skipCount) { |
| int targetLength = this.length; |
| _checkPosition(start, targetLength, "start"); |
| _checkPosition(end, targetLength, "end"); |
| if (start > end) throw RangeError.range(start, 0, end); |
| int count = end - start; |
| |
| if (skipCount < 0) throw ArgumentError(skipCount); |
| |
| int sourceLength = source.length; |
| if (sourceLength - skipCount < count) { |
| throw StateError('Not enough elements'); |
| } |
| |
| if (skipCount != 0 || sourceLength != count) { |
| // Create a view of the exact subrange that is copied from the source. |
| source = JS('', '#.subarray(#, #)', source, skipCount, skipCount + count); |
| } |
| JS('void', '#.set(#, #)', this, source, start); |
| } |
| } |
| |
| abstract class NativeTypedArrayOfDouble extends NativeTypedArray<double> |
| with ListMixin<double>, FixedLengthListMixin<double> { |
| int get length => JS<int>('!', '#.length', this); |
| |
| double operator [](int index) { |
| _checkValidIndex(index, this, this.length); |
| return JS<double>('!', '#[#]', this, index); |
| } |
| |
| void operator []=(int index, num value) { |
| _checkValidIndex(index, this, this.length); |
| JS('void', '#[#] = #', this, index, value); |
| } |
| |
| void setRange(int start, int end, Iterable<double> iterable, |
| [int skipCount = 0]) { |
| if (iterable is NativeTypedArrayOfDouble) { |
| _setRangeFast(start, end, iterable, skipCount); |
| return; |
| } |
| super.setRange(start, end, iterable, skipCount); |
| } |
| } |
| |
| abstract class NativeTypedArrayOfInt extends NativeTypedArray<int> |
| with ListMixin<int>, FixedLengthListMixin<int> |
| implements List<int> { |
| int get length => JS<int>('!', '#.length', this); |
| |
| // operator[]() is not here since different versions have different return |
| // types |
| |
| void operator []=(int index, int value) { |
| _checkValidIndex(index, this, this.length); |
| JS('void', '#[#] = #', this, index, value); |
| } |
| |
| void setRange(int start, int end, Iterable<int> iterable, |
| [int skipCount = 0]) { |
| if (iterable is NativeTypedArrayOfInt) { |
| _setRangeFast(start, end, iterable, skipCount); |
| return; |
| } |
| super.setRange(start, end, iterable, skipCount); |
| } |
| } |
| |
| @Native('Float32Array') |
| class NativeFloat32List extends NativeTypedArrayOfDouble |
| implements Float32List { |
| factory NativeFloat32List(int length) => _create1(_checkLength(length)); |
| |
| factory NativeFloat32List.fromList(List<double> elements) => |
| _create1(_ensureNativeList(elements)); |
| |
| factory NativeFloat32List.view( |
| ByteBuffer buffer, int offsetInBytes, int? length) { |
| _checkViewArguments(buffer, offsetInBytes, length); |
| length ??= |
| (buffer.lengthInBytes - offsetInBytes) ~/ Float32List.bytesPerElement; |
| return _create3(buffer, offsetInBytes, length); |
| } |
| |
| Type get runtimeType => Float32List; |
| |
| Float32List sublist(int start, [int? end]) { |
| var stop = _checkValidRange(start, end, this.length); |
| var source = |
| JS<NativeFloat32List>('!', '#.subarray(#, #)', this, start, stop); |
| return _create1(source); |
| } |
| |
| static NativeFloat32List _create1(arg) => |
| JS<NativeFloat32List>('!', 'new Float32Array(#)', arg); |
| |
| static NativeFloat32List _create3(arg1, arg2, arg3) => |
| JS<NativeFloat32List>('!', 'new Float32Array(#, #, #)', arg1, arg2, arg3); |
| } |
| |
| @Native('Float64Array') |
| class NativeFloat64List extends NativeTypedArrayOfDouble |
| implements Float64List { |
| factory NativeFloat64List(int length) => _create1(_checkLength(length)); |
| |
| factory NativeFloat64List.fromList(List<double> elements) => |
| _create1(_ensureNativeList(elements)); |
| |
| factory NativeFloat64List.view( |
| ByteBuffer buffer, int offsetInBytes, int? length) { |
| _checkViewArguments(buffer, offsetInBytes, length); |
| length ??= |
| (buffer.lengthInBytes - offsetInBytes) ~/ Float64List.bytesPerElement; |
| return _create3(buffer, offsetInBytes, length); |
| } |
| |
| Type get runtimeType => Float64List; |
| |
| Float64List sublist(int start, [int? end]) { |
| var stop = _checkValidRange(start, end, this.length); |
| var source = JS('NativeFloat64List', '#.subarray(#, #)', this, start, stop); |
| return _create1(source); |
| } |
| |
| static NativeFloat64List _create1(arg) => |
| JS('NativeFloat64List', 'new Float64Array(#)', arg); |
| |
| static NativeFloat64List _create3(arg1, arg2, arg3) => |
| JS('NativeFloat64List', 'new Float64Array(#, #, #)', arg1, arg2, arg3); |
| } |
| |
| @Native('Int16Array') |
| class NativeInt16List extends NativeTypedArrayOfInt implements Int16List { |
| factory NativeInt16List(int length) => _create1(_checkLength(length)); |
| |
| factory NativeInt16List.fromList(List<int> elements) => |
| _create1(_ensureNativeList(elements)); |
| |
| factory NativeInt16List.view( |
| NativeByteBuffer buffer, int offsetInBytes, int? length) { |
| _checkViewArguments(buffer, offsetInBytes, length); |
| length ??= |
| (buffer.lengthInBytes - offsetInBytes) ~/ Int16List.bytesPerElement; |
| return _create3(buffer, offsetInBytes, length); |
| } |
| |
| Type get runtimeType => Int16List; |
| |
| int operator [](int index) { |
| _checkValidIndex(index, this, this.length); |
| return JS<int>('!', '#[#]', this, index); |
| } |
| |
| Int16List sublist(int start, [int? end]) { |
| var stop = _checkValidRange(start, end, this.length); |
| var source = JS('NativeInt16List', '#.subarray(#, #)', this, start, stop); |
| return _create1(source); |
| } |
| |
| static NativeInt16List _create1(arg) => |
| JS('NativeInt16List', 'new Int16Array(#)', arg); |
| |
| static NativeInt16List _create3(arg1, arg2, arg3) => |
| JS('NativeInt16List', 'new Int16Array(#, #, #)', arg1, arg2, arg3); |
| } |
| |
| @Native('Int32Array') |
| class NativeInt32List extends NativeTypedArrayOfInt implements Int32List { |
| factory NativeInt32List(int length) => _create1(_checkLength(length)); |
| |
| factory NativeInt32List.fromList(List<int> elements) => |
| _create1(_ensureNativeList(elements)); |
| |
| factory NativeInt32List.view( |
| ByteBuffer buffer, int offsetInBytes, int? length) { |
| _checkViewArguments(buffer, offsetInBytes, length); |
| length ??= |
| (buffer.lengthInBytes - offsetInBytes) ~/ Int32List.bytesPerElement; |
| return _create3(buffer, offsetInBytes, length); |
| } |
| |
| Type get runtimeType => Int32List; |
| |
| int operator [](int index) { |
| _checkValidIndex(index, this, this.length); |
| return JS<int>('!', '#[#]', this, index); |
| } |
| |
| Int32List sublist(int start, [int? end]) { |
| var stop = _checkValidRange(start, end, this.length); |
| var source = |
| JS<NativeInt32List>('!', '#.subarray(#, #)', this, start, stop); |
| return _create1(source); |
| } |
| |
| static NativeInt32List _create1(arg) => |
| JS<NativeInt32List>('!', 'new Int32Array(#)', arg); |
| |
| static NativeInt32List _create3(arg1, arg2, arg3) => |
| JS<NativeInt32List>('!', 'new Int32Array(#, #, #)', arg1, arg2, arg3); |
| } |
| |
| @Native('Int8Array') |
| class NativeInt8List extends NativeTypedArrayOfInt implements Int8List { |
| factory NativeInt8List(int length) => _create1(_checkLength(length)); |
| |
| factory NativeInt8List.fromList(List<int> elements) => |
| _create1(_ensureNativeList(elements)); |
| |
| factory NativeInt8List.view( |
| ByteBuffer buffer, int offsetInBytes, int? length) { |
| _checkViewArguments(buffer, offsetInBytes, length); |
| return length == null |
| ? _create2(buffer, offsetInBytes) |
| : _create3(buffer, offsetInBytes, length); |
| } |
| |
| Type get runtimeType => Int8List; |
| |
| int operator [](int index) { |
| _checkValidIndex(index, this, this.length); |
| return JS<int>('!', '#[#]', this, index); |
| } |
| |
| Int8List sublist(int start, [int? end]) { |
| var stop = _checkValidRange(start, end, this.length); |
| var source = JS<NativeInt8List>('!', '#.subarray(#, #)', this, start, stop); |
| return _create1(source); |
| } |
| |
| static NativeInt8List _create1(arg) => |
| JS<NativeInt8List>('!', 'new Int8Array(#)', arg); |
| |
| static NativeInt8List _create2(arg1, arg2) => |
| JS<NativeInt8List>('!', 'new Int8Array(#, #)', arg1, arg2); |
| |
| static NativeInt8List _create3(arg1, arg2, arg3) => |
| JS<NativeInt8List>('!', 'new Int8Array(#, #, #)', arg1, arg2, arg3); |
| } |
| |
| @Native('Uint16Array') |
| class NativeUint16List extends NativeTypedArrayOfInt implements Uint16List { |
| factory NativeUint16List(int length) => _create1(_checkLength(length)); |
| |
| factory NativeUint16List.fromList(List<int> list) => |
| _create1(_ensureNativeList(list)); |
| |
| factory NativeUint16List.view( |
| ByteBuffer buffer, int offsetInBytes, int? length) { |
| _checkViewArguments(buffer, offsetInBytes, length); |
| length ??= |
| (buffer.lengthInBytes - offsetInBytes) ~/ Uint16List.bytesPerElement; |
| return _create3(buffer, offsetInBytes, length); |
| } |
| |
| Type get runtimeType => Uint16List; |
| |
| int operator [](int index) { |
| _checkValidIndex(index, this, this.length); |
| return JS<int>('!', '#[#]', this, index); |
| } |
| |
| Uint16List sublist(int start, [int? end]) { |
| var stop = _checkValidRange(start, end, this.length); |
| var source = |
| JS<NativeUint16List>('!', '#.subarray(#, #)', this, start, stop); |
| return _create1(source); |
| } |
| |
| static NativeUint16List _create1(arg) => |
| JS<NativeUint16List>('!', 'new Uint16Array(#)', arg); |
| |
| static NativeUint16List _create3(arg1, arg2, arg3) => |
| JS<NativeUint16List>('!', 'new Uint16Array(#, #, #)', arg1, arg2, arg3); |
| } |
| |
| @Native('Uint32Array') |
| class NativeUint32List extends NativeTypedArrayOfInt implements Uint32List { |
| factory NativeUint32List(int length) => _create1(_checkLength(length)); |
| |
| factory NativeUint32List.fromList(List<int> elements) => |
| _create1(_ensureNativeList(elements)); |
| |
| factory NativeUint32List.view( |
| ByteBuffer buffer, int offsetInBytes, int? length) { |
| _checkViewArguments(buffer, offsetInBytes, length); |
| length ??= |
| (buffer.lengthInBytes - offsetInBytes) ~/ Uint32List.bytesPerElement; |
| return _create3(buffer, offsetInBytes, length); |
| } |
| |
| Type get runtimeType => Uint32List; |
| |
| int operator [](int index) { |
| _checkValidIndex(index, this, this.length); |
| return JS<int>('!', '#[#]', this, index); |
| } |
| |
| Uint32List sublist(int start, [int? end]) { |
| var stop = _checkValidRange(start, end, this.length); |
| var source = |
| JS<NativeUint32List>('!', '#.subarray(#, #)', this, start, stop); |
| return _create1(source); |
| } |
| |
| static NativeUint32List _create1(arg) => |
| JS<NativeUint32List>('!', 'new Uint32Array(#)', arg); |
| |
| static NativeUint32List _create3(arg1, arg2, arg3) => |
| JS<NativeUint32List>('!', 'new Uint32Array(#, #, #)', arg1, arg2, arg3); |
| } |
| |
| @Native('Uint8ClampedArray,CanvasPixelArray') |
| class NativeUint8ClampedList extends NativeTypedArrayOfInt |
| implements Uint8ClampedList { |
| factory NativeUint8ClampedList(int length) => _create1(_checkLength(length)); |
| |
| factory NativeUint8ClampedList.fromList(List<int> elements) => |
| _create1(_ensureNativeList(elements)); |
| |
| factory NativeUint8ClampedList.view( |
| ByteBuffer buffer, int offsetInBytes, int? length) { |
| _checkViewArguments(buffer, offsetInBytes, length); |
| return length == null |
| ? _create2(buffer, offsetInBytes) |
| : _create3(buffer, offsetInBytes, length); |
| } |
| |
| Type get runtimeType => Uint8ClampedList; |
| |
| int get length => JS<int>('!', '#.length', this); |
| |
| int operator [](int index) { |
| _checkValidIndex(index, this, this.length); |
| return JS<int>('!', '#[#]', this, index); |
| } |
| |
| Uint8ClampedList sublist(int start, [int? end]) { |
| var stop = _checkValidRange(start, end, this.length); |
| var source = |
| JS<NativeUint8ClampedList>('!', '#.subarray(#, #)', this, start, stop); |
| return _create1(source); |
| } |
| |
| static NativeUint8ClampedList _create1(arg) => |
| JS<NativeUint8ClampedList>('!', 'new Uint8ClampedArray(#)', arg); |
| |
| static NativeUint8ClampedList _create2(arg1, arg2) => |
| JS<NativeUint8ClampedList>( |
| '!', 'new Uint8ClampedArray(#, #)', arg1, arg2); |
| |
| static NativeUint8ClampedList _create3(arg1, arg2, arg3) => JS( |
| 'NativeUint8ClampedList', |
| 'new Uint8ClampedArray(#, #, #)', |
| arg1, |
| arg2, |
| arg3); |
| } |
| |
| // On some browsers Uint8ClampedArray is a subtype of Uint8Array. Marking |
| // Uint8List as !nonleaf ensures that the native dispatch correctly handles |
| // the potential for Uint8ClampedArray to 'accidentally' pick up the |
| // dispatch record for Uint8List. |
| @Native('Uint8Array,!nonleaf') |
| class NativeUint8List extends NativeTypedArrayOfInt implements Uint8List { |
| factory NativeUint8List(int length) => _create1(_checkLength(length)); |
| |
| factory NativeUint8List.fromList(List<int> elements) => |
| _create1(_ensureNativeList(elements)); |
| |
| factory NativeUint8List.view( |
| ByteBuffer buffer, int offsetInBytes, int? length) { |
| _checkViewArguments(buffer, offsetInBytes, length); |
| return length == null |
| ? _create2(buffer, offsetInBytes) |
| : _create3(buffer, offsetInBytes, length); |
| } |
| |
| Type get runtimeType => Uint8List; |
| |
| int get length => JS<int>('!', '#.length', this); |
| |
| int operator [](int index) { |
| _checkValidIndex(index, this, this.length); |
| return JS<int>('!', '#[#]', this, index); |
| } |
| |
| Uint8List sublist(int start, [int? end]) { |
| var stop = _checkValidRange(start, end, this.length); |
| var source = |
| JS<NativeUint8List>('!', '#.subarray(#, #)', this, start, stop); |
| return _create1(source); |
| } |
| |
| static NativeUint8List _create1(arg) => |
| JS<NativeUint8List>('!', 'new Uint8Array(#)', arg); |
| |
| static NativeUint8List _create2(arg1, arg2) => |
| JS<NativeUint8List>('!', 'new Uint8Array(#, #)', arg1, arg2); |
| |
| static NativeUint8List _create3(arg1, arg2, arg3) => |
| JS<NativeUint8List>('!', 'new Uint8Array(#, #, #)', arg1, arg2, arg3); |
| } |
| |
| /// Implementation of Dart Float32x4 immutable value type and operations. |
| /// Float32x4 stores 4 32-bit floating point values in "lanes". |
| /// The lanes are "x", "y", "z", and "w" respectively. |
| class NativeFloat32x4 implements Float32x4 { |
| final double x; |
| final double y; |
| final double z; |
| final double w; |
| |
| static final NativeFloat32List _list = NativeFloat32List(4); |
| static final Uint32List _uint32view = _list.buffer.asUint32List(); |
| |
| static _truncate(x) { |
| _list[0] = x; |
| return _list[0]; |
| } |
| |
| NativeFloat32x4(double x, double y, double z, double w) |
| : this.x = _truncate(x), |
| this.y = _truncate(y), |
| this.z = _truncate(z), |
| this.w = _truncate(w) { |
| // We would prefer to check for `double` but in dart2js we can't see the |
| // difference anyway. |
| if (x is! num) throw ArgumentError(x); |
| if (y is! num) throw ArgumentError(y); |
| if (z is! num) throw ArgumentError(z); |
| if (w is! num) throw ArgumentError(w); |
| } |
| |
| NativeFloat32x4.splat(double v) : this(v, v, v, v); |
| NativeFloat32x4.zero() : this._truncated(0.0, 0.0, 0.0, 0.0); |
| |
| /// Returns a bit-wise copy of [i] as a Float32x4. |
| factory NativeFloat32x4.fromInt32x4Bits(Int32x4 i) { |
| _uint32view[0] = i.x; |
| _uint32view[1] = i.y; |
| _uint32view[2] = i.z; |
| _uint32view[3] = i.w; |
| return NativeFloat32x4._truncated(_list[0], _list[1], _list[2], _list[3]); |
| } |
| |
| NativeFloat32x4.fromFloat64x2(Float64x2 v) |
| : this._truncated(_truncate(v.x), _truncate(v.y), 0.0, 0.0); |
| |
| /// Creates a new NativeFloat32x4. |
| /// |
| /// Does not verify if the given arguments are non-null. |
| NativeFloat32x4._doubles(double x, double y, double z, double w) |
| : this.x = _truncate(x), |
| this.y = _truncate(y), |
| this.z = _truncate(z), |
| this.w = _truncate(w); |
| |
| /// Creates a new NativeFloat32x4. |
| /// |
| /// The constructor does not truncate the arguments. They must already be in |
| /// the correct range. It does not verify the type of the given arguments, |
| /// either. |
| NativeFloat32x4._truncated(this.x, this.y, this.z, this.w); |
| |
| String toString() { |
| return '[$x, $y, $z, $w]'; |
| } |
| |
| /// Addition operator. |
| Float32x4 operator +(Float32x4 other) { |
| double _x = x + other.x; |
| double _y = y + other.y; |
| double _z = z + other.z; |
| double _w = w + other.w; |
| return NativeFloat32x4._doubles(_x, _y, _z, _w); |
| } |
| |
| /// Negate operator. |
| Float32x4 operator -() { |
| return NativeFloat32x4._truncated(-x, -y, -z, -w); |
| } |
| |
| /// Subtraction operator. |
| Float32x4 operator -(Float32x4 other) { |
| double _x = x - other.x; |
| double _y = y - other.y; |
| double _z = z - other.z; |
| double _w = w - other.w; |
| return NativeFloat32x4._doubles(_x, _y, _z, _w); |
| } |
| |
| /// Multiplication operator. |
| Float32x4 operator *(Float32x4 other) { |
| double _x = x * other.x; |
| double _y = y * other.y; |
| double _z = z * other.z; |
| double _w = w * other.w; |
| return NativeFloat32x4._doubles(_x, _y, _z, _w); |
| } |
| |
| /// Division operator. |
| Float32x4 operator /(Float32x4 other) { |
| double _x = x / other.x; |
| double _y = y / other.y; |
| double _z = z / other.z; |
| double _w = w / other.w; |
| return NativeFloat32x4._doubles(_x, _y, _z, _w); |
| } |
| |
| /// Relational less than. |
| Int32x4 lessThan(Float32x4 other) { |
| bool _cx = x < other.x; |
| bool _cy = y < other.y; |
| bool _cz = z < other.z; |
| bool _cw = w < other.w; |
| return NativeInt32x4._truncated( |
| _cx ? -1 : 0, _cy ? -1 : 0, _cz ? -1 : 0, _cw ? -1 : 0); |
| } |
| |
| /// Relational less than or equal. |
| Int32x4 lessThanOrEqual(Float32x4 other) { |
| bool _cx = x <= other.x; |
| bool _cy = y <= other.y; |
| bool _cz = z <= other.z; |
| bool _cw = w <= other.w; |
| return NativeInt32x4._truncated( |
| _cx ? -1 : 0, _cy ? -1 : 0, _cz ? -1 : 0, _cw ? -1 : 0); |
| } |
| |
| /// Relational greater than. |
| Int32x4 greaterThan(Float32x4 other) { |
| bool _cx = x > other.x; |
| bool _cy = y > other.y; |
| bool _cz = z > other.z; |
| bool _cw = w > other.w; |
| return NativeInt32x4._truncated( |
| _cx ? -1 : 0, _cy ? -1 : 0, _cz ? -1 : 0, _cw ? -1 : 0); |
| } |
| |
| /// Relational greater than or equal. |
| Int32x4 greaterThanOrEqual(Float32x4 other) { |
| bool _cx = x >= other.x; |
| bool _cy = y >= other.y; |
| bool _cz = z >= other.z; |
| bool _cw = w >= other.w; |
| return NativeInt32x4._truncated( |
| _cx ? -1 : 0, _cy ? -1 : 0, _cz ? -1 : 0, _cw ? -1 : 0); |
| } |
| |
| /// Relational equal. |
| Int32x4 equal(Float32x4 other) { |
| bool _cx = x == other.x; |
| bool _cy = y == other.y; |
| bool _cz = z == other.z; |
| bool _cw = w == other.w; |
| return NativeInt32x4._truncated( |
| _cx ? -1 : 0, _cy ? -1 : 0, _cz ? -1 : 0, _cw ? -1 : 0); |
| } |
| |
| /// Relational not-equal. |
| Int32x4 notEqual(Float32x4 other) { |
| bool _cx = x != other.x; |
| bool _cy = y != other.y; |
| bool _cz = z != other.z; |
| bool _cw = w != other.w; |
| return NativeInt32x4._truncated( |
| _cx ? -1 : 0, _cy ? -1 : 0, _cz ? -1 : 0, _cw ? -1 : 0); |
| } |
| |
| /// Returns a copy of [this] each lane being scaled by [s]. |
| Float32x4 scale(double s) { |
| double _x = s * x; |
| double _y = s * y; |
| double _z = s * z; |
| double _w = s * w; |
| return NativeFloat32x4._doubles(_x, _y, _z, _w); |
| } |
| |
| /// Returns the absolute value of this [Float32x4]. |
| Float32x4 abs() { |
| double _x = x.abs(); |
| double _y = y.abs(); |
| double _z = z.abs(); |
| double _w = w.abs(); |
| return NativeFloat32x4._truncated(_x, _y, _z, _w); |
| } |
| |
| /// Clamps [this] to be in the range [lowerLimit]-[upperLimit]. |
| Float32x4 clamp(Float32x4 lowerLimit, Float32x4 upperLimit) { |
| double _lx = lowerLimit.x; |
| double _ly = lowerLimit.y; |
| double _lz = lowerLimit.z; |
| double _lw = lowerLimit.w; |
| double _ux = upperLimit.x; |
| double _uy = upperLimit.y; |
| double _uz = upperLimit.z; |
| double _uw = upperLimit.w; |
| double _x = x; |
| double _y = y; |
| double _z = z; |
| double _w = w; |
| // MAX(MIN(self, upper), lower). |
| _x = _x > _ux ? _ux : _x; |
| _y = _y > _uy ? _uy : _y; |
| _z = _z > _uz ? _uz : _z; |
| _w = _w > _uw ? _uw : _w; |
| _x = _x < _lx ? _lx : _x; |
| _y = _y < _ly ? _ly : _y; |
| _z = _z < _lz ? _lz : _z; |
| _w = _w < _lw ? _lw : _w; |
| return NativeFloat32x4._truncated(_x, _y, _z, _w); |
| } |
| |
| /// Extract the sign bit from each lane return them in the first 4 bits. |
| int get signMask { |
| var view = _uint32view; |
| var mx, my, mz, mw; |
| _list[0] = x; |
| _list[1] = y; |
| _list[2] = z; |
| _list[3] = w; |
| // This is correct because dart2js uses the unsigned right shift. |
| mx = (view[0] & 0x80000000) >> 31; |
| my = (view[1] & 0x80000000) >> 30; |
| mz = (view[2] & 0x80000000) >> 29; |
| mw = (view[3] & 0x80000000) >> 28; |
| return mx | my | mz | mw; |
| } |
| |
| /// Shuffle the lane values. [mask] must be one of the 256 shuffle constants. |
| Float32x4 shuffle(int mask) { |
| if ((mask < 0) || (mask > 255)) { |
| throw RangeError.range(mask, 0, 255, "mask"); |
| } |
| _list[0] = x; |
| _list[1] = y; |
| _list[2] = z; |
| _list[3] = w; |
| |
| double _x = _list[mask & 0x3]; |
| double _y = _list[(mask >> 2) & 0x3]; |
| double _z = _list[(mask >> 4) & 0x3]; |
| double _w = _list[(mask >> 6) & 0x3]; |
| return NativeFloat32x4._truncated(_x, _y, _z, _w); |
| } |
| |
| /// Shuffle the lane values in [this] and [other]. The returned |
| /// Float32x4 will have XY lanes from [this] and ZW lanes from [other]. |
| /// Uses the same [mask] as [shuffle]. |
| Float32x4 shuffleMix(Float32x4 other, int mask) { |
| if ((mask < 0) || (mask > 255)) { |
| throw RangeError.range(mask, 0, 255, "mask"); |
| } |
| _list[0] = x; |
| _list[1] = y; |
| _list[2] = z; |
| _list[3] = w; |
| double _x = _list[mask & 0x3]; |
| double _y = _list[(mask >> 2) & 0x3]; |
| |
| _list[0] = other.x; |
| _list[1] = other.y; |
| _list[2] = other.z; |
| _list[3] = other.w; |
| double _z = _list[(mask >> 4) & 0x3]; |
| double _w = _list[(mask >> 6) & 0x3]; |
| return NativeFloat32x4._truncated(_x, _y, _z, _w); |
| } |
| |
| /// Copy [this] and replace the [x] lane. |
| Float32x4 withX(double newX) { |
| ArgumentError.checkNotNull(newX); |
| return NativeFloat32x4._truncated(_truncate(newX), y, z, w); |
| } |
| |
| /// Copy [this] and replace the [y] lane. |
| Float32x4 withY(double newY) { |
| ArgumentError.checkNotNull(newY); |
| return NativeFloat32x4._truncated(x, _truncate(newY), z, w); |
| } |
| |
| /// Copy [this] and replace the [z] lane. |
| Float32x4 withZ(double newZ) { |
| ArgumentError.checkNotNull(newZ); |
| return NativeFloat32x4._truncated(x, y, _truncate(newZ), w); |
| } |
| |
| /// Copy [this] and replace the [w] lane. |
| Float32x4 withW(double newW) { |
| ArgumentError.checkNotNull(newW); |
| return NativeFloat32x4._truncated(x, y, z, _truncate(newW)); |
| } |
| |
| /// Returns the lane-wise minimum value in [this] or [other]. |
| Float32x4 min(Float32x4 other) { |
| double _x = x < other.x ? x : other.x; |
| double _y = y < other.y ? y : other.y; |
| double _z = z < other.z ? z : other.z; |
| double _w = w < other.w ? w : other.w; |
| return NativeFloat32x4._truncated(_x, _y, _z, _w); |
| } |
| |
| /// Returns the lane-wise maximum value in [this] or [other]. |
| Float32x4 max(Float32x4 other) { |
| double _x = x > other.x ? x : other.x; |
| double _y = y > other.y ? y : other.y; |
| double _z = z > other.z ? z : other.z; |
| double _w = w > other.w ? w : other.w; |
| return NativeFloat32x4._truncated(_x, _y, _z, _w); |
| } |
| |
| /// Returns the square root of [this]. |
| Float32x4 sqrt() { |
| double _x = Math.sqrt(x); |
| double _y = Math.sqrt(y); |
| double _z = Math.sqrt(z); |
| double _w = Math.sqrt(w); |
| return NativeFloat32x4._doubles(_x, _y, _z, _w); |
| } |
| |
| /// Returns the reciprocal of [this]. |
| Float32x4 reciprocal() { |
| double _x = 1.0 / x; |
| double _y = 1.0 / y; |
| double _z = 1.0 / z; |
| double _w = 1.0 / w; |
| return NativeFloat32x4._doubles(_x, _y, _z, _w); |
| } |
| |
| /// Returns the square root of the reciprocal of [this]. |
| Float32x4 reciprocalSqrt() { |
| double _x = Math.sqrt(1.0 / x); |
| double _y = Math.sqrt(1.0 / y); |
| double _z = Math.sqrt(1.0 / z); |
| double _w = Math.sqrt(1.0 / w); |
| return NativeFloat32x4._doubles(_x, _y, _z, _w); |
| } |
| } |
| |
| /// Interface of Dart Int32x4 and operations. |
| /// Int32x4 stores 4 32-bit bit-masks in "lanes". |
| /// The lanes are "x", "y", "z", and "w" respectively. |
| class NativeInt32x4 implements Int32x4 { |
| final int x; |
| final int y; |
| final int z; |
| final int w; |
| |
| static final _list = NativeInt32List(4); |
| |
| static _truncate(x) { |
| _list[0] = x; |
| return _list[0]; |
| } |
| |
| NativeInt32x4(int x, int y, int z, int w) |
| : this.x = _truncate(x), |
| this.y = _truncate(y), |
| this.z = _truncate(z), |
| this.w = _truncate(w) { |
| if (x != this.x && x is! int) throw ArgumentError(x); |
| if (y != this.y && y is! int) throw ArgumentError(y); |
| if (z != this.z && z is! int) throw ArgumentError(z); |
| if (w != this.w && w is! int) throw ArgumentError(w); |
| } |
| |
| NativeInt32x4.bool(bool x, bool y, bool z, bool w) |
| : this.x = x ? -1 : 0, |
| this.y = y ? -1 : 0, |
| this.z = z ? -1 : 0, |
| this.w = w ? -1 : 0; |
| |
| /// Returns a bit-wise copy of [f] as a Int32x4. |
| factory NativeInt32x4.fromFloat32x4Bits(Float32x4 f) { |
| NativeFloat32List floatList = NativeFloat32x4._list; |
| floatList[0] = f.x; |
| floatList[1] = f.y; |
| floatList[2] = f.z; |
| floatList[3] = f.w; |
| Int32List view = floatList.buffer.asInt32List(); |
| return NativeInt32x4._truncated(view[0], view[1], view[2], view[3]); |
| } |
| |
| NativeInt32x4._truncated(this.x, this.y, this.z, this.w); |
| |
| String toString() => '[$x, $y, $z, $w]'; |
| |
| /// The bit-wise or operator. |
| Int32x4 operator |(Int32x4 other) { |
| // Dart2js uses unsigned results for bit-operations. |
| // We use "JS" to fall back to the signed versions. |
| return NativeInt32x4._truncated( |
| JS("int", "# | #", x, other.x), |
| JS("int", "# | #", y, other.y), |
| JS("int", "# | #", z, other.z), |
| JS("int", "# | #", w, other.w)); |
| } |
| |
| /// The bit-wise and operator. |
| Int32x4 operator &(Int32x4 other) { |
| // Dart2js uses unsigned results for bit-operations. |
| // We use "JS" to fall back to the signed versions. |
| return NativeInt32x4._truncated( |
| JS("int", "# & #", x, other.x), |
| JS("int", "# & #", y, other.y), |
| JS("int", "# & #", z, other.z), |
| JS("int", "# & #", w, other.w)); |
| } |
| |
| /// The bit-wise xor operator. |
| Int32x4 operator ^(Int32x4 other) { |
| // Dart2js uses unsigned results for bit-operations. |
| // We use "JS" to fall back to the signed versions. |
| return NativeInt32x4._truncated( |
| JS("int", "# ^ #", x, other.x), |
| JS("int", "# ^ #", y, other.y), |
| JS("int", "# ^ #", z, other.z), |
| JS("int", "# ^ #", w, other.w)); |
| } |
| |
| Int32x4 operator +(Int32x4 other) { |
| // Avoid going through the typed array by "| 0" the result. |
| return NativeInt32x4._truncated( |
| JS("int", "(# + #) | 0", x, other.x), |
| JS("int", "(# + #) | 0", y, other.y), |
| JS("int", "(# + #) | 0", z, other.z), |
| JS("int", "(# + #) | 0", w, other.w)); |
| } |
| |
| Int32x4 operator -(Int32x4 other) { |
| // Avoid going through the typed array by "| 0" the result. |
| return NativeInt32x4._truncated( |
| JS("int", "(# - #) | 0", x, other.x), |
| JS("int", "(# - #) | 0", y, other.y), |
| JS("int", "(# - #) | 0", z, other.z), |
| JS("int", "(# - #) | 0", w, other.w)); |
| } |
| |
| Int32x4 operator -() { |
| // Avoid going through the typed array by "| 0" the result. |
| return NativeInt32x4._truncated( |
| JS("int", "(-#) | 0", x), |
| JS("int", "(-#) | 0", y), |
| JS("int", "(-#) | 0", z), |
| JS("int", "(-#) | 0", w)); |
| } |
| |
| /// Extract the top bit from each lane return them in the first 4 bits. |
| int get signMask { |
| int mx = (x & 0x80000000) >> 31; |
| int my = (y & 0x80000000) >> 31; |
| int mz = (z & 0x80000000) >> 31; |
| int mw = (w & 0x80000000) >> 31; |
| return mx | my << 1 | mz << 2 | mw << 3; |
| } |
| |
| /// Shuffle the lane values. [mask] must be one of the 256 shuffle constants. |
| Int32x4 shuffle(int mask) { |
| if ((mask < 0) || (mask > 255)) { |
| throw RangeError.range(mask, 0, 255, "mask"); |
| } |
| _list[0] = x; |
| _list[1] = y; |
| _list[2] = z; |
| _list[3] = w; |
| int _x = _list[mask & 0x3]; |
| int _y = _list[(mask >> 2) & 0x3]; |
| int _z = _list[(mask >> 4) & 0x3]; |
| int _w = _list[(mask >> 6) & 0x3]; |
| return NativeInt32x4._truncated(_x, _y, _z, _w); |
| } |
| |
| /// Shuffle the lane values in [this] and [other]. The returned |
| /// Int32x4 will have XY lanes from [this] and ZW lanes from [other]. |
| /// Uses the same [mask] as [shuffle]. |
| Int32x4 shuffleMix(Int32x4 other, int mask) { |
| if ((mask < 0) || (mask > 255)) { |
| throw RangeError.range(mask, 0, 255, "mask"); |
| } |
| _list[0] = x; |
| _list[1] = y; |
| _list[2] = z; |
| _list[3] = w; |
| int _x = _list[mask & 0x3]; |
| int _y = _list[(mask >> 2) & 0x3]; |
| |
| _list[0] = other.x; |
| _list[1] = other.y; |
| _list[2] = other.z; |
| _list[3] = other.w; |
| int _z = _list[(mask >> 4) & 0x3]; |
| int _w = _list[(mask >> 6) & 0x3]; |
| return NativeInt32x4._truncated(_x, _y, _z, _w); |
| } |
| |
| /// Returns a new [Int32x4] copied from [this] with a new x value. |
| Int32x4 withX(int x) { |
| ArgumentError.checkNotNull(x); |
| int _x = _truncate(x); |
| return NativeInt32x4._truncated(_x, y, z, w); |
| } |
| |
| /// Returns a new [Int32x4] copied from [this] with a new y value. |
| Int32x4 withY(int y) { |
| ArgumentError.checkNotNull(y); |
| int _y = _truncate(y); |
| return NativeInt32x4._truncated(x, _y, z, w); |
| } |
| |
| /// Returns a new [Int32x4] copied from [this] with a new z value. |
| Int32x4 withZ(int z) { |
| ArgumentError.checkNotNull(z); |
| int _z = _truncate(z); |
| return NativeInt32x4._truncated(x, y, _z, w); |
| } |
| |
| /// Returns a new [Int32x4] copied from [this] with a new w value. |
| Int32x4 withW(int w) { |
| ArgumentError.checkNotNull(w); |
| int _w = _truncate(w); |
| return NativeInt32x4._truncated(x, y, z, _w); |
| } |
| |
| /// Extracted x value. Returns `false` for 0, `true` for any other value. |
| bool get flagX => x != 0; |
| |
| /// Extracted y value. Returns `false` for 0, `true` for any other value. |
| bool get flagY => y != 0; |
| |
| /// Extracted z value. Returns `false` for 0, `true` for any other value. |
| bool get flagZ => z != 0; |
| |
| /// Extracted w value. Returns `false` for 0, `true` for any other value. |
| bool get flagW => w != 0; |
| |
| /// Returns a new [Int32x4] copied from [this] with a new x value. |
| Int32x4 withFlagX(bool flagX) { |
| int _x = flagX ? -1 : 0; |
| return NativeInt32x4._truncated(_x, y, z, w); |
| } |
| |
| /// Returns a new [Int32x4] copied from [this] with a new y value. |
| Int32x4 withFlagY(bool flagY) { |
| int _y = flagY ? -1 : 0; |
| return NativeInt32x4._truncated(x, _y, z, w); |
| } |
| |
| /// Returns a new [Int32x4] copied from [this] with a new z value. |
| Int32x4 withFlagZ(bool flagZ) { |
| int _z = flagZ ? -1 : 0; |
| return NativeInt32x4._truncated(x, y, _z, w); |
| } |
| |
| /// Returns a new [Int32x4] copied from [this] with a new w value. |
| Int32x4 withFlagW(bool flagW) { |
| int _w = flagW ? -1 : 0; |
| return NativeInt32x4._truncated(x, y, z, _w); |
| } |
| |
| /// Merge [trueValue] and [falseValue] based on [this]' bit mask: |
| /// Select bit from [trueValue] when bit in [this] is on. |
| /// Select bit from [falseValue] when bit in [this] is off. |
| Float32x4 select(Float32x4 trueValue, Float32x4 falseValue) { |
| var floatList = NativeFloat32x4._list; |
| var intView = NativeFloat32x4._uint32view; |
| |
| floatList[0] = trueValue.x; |
| floatList[1] = trueValue.y; |
| floatList[2] = trueValue.z; |
| floatList[3] = trueValue.w; |
| int stx = intView[0]; |
| int sty = intView[1]; |
| int stz = intView[2]; |
| int stw = intView[3]; |
| |
| floatList[0] = falseValue.x; |
| floatList[1] = falseValue.y; |
| floatList[2] = falseValue.z; |
| floatList[3] = falseValue.w; |
| int sfx = intView[0]; |
| int sfy = intView[1]; |
| int sfz = intView[2]; |
| int sfw = intView[3]; |
| int _x = (x & stx) | (~x & sfx); |
| int _y = (y & sty) | (~y & sfy); |
| int _z = (z & stz) | (~z & sfz); |
| int _w = (w & stw) | (~w & sfw); |
| intView[0] = _x; |
| intView[1] = _y; |
| intView[2] = _z; |
| intView[3] = _w; |
| return NativeFloat32x4._truncated( |
| floatList[0], floatList[1], floatList[2], floatList[3]); |
| } |
| } |
| |
| class NativeFloat64x2 implements Float64x2 { |
| final double x; |
| final double y; |
| |
| static NativeFloat64List _list = NativeFloat64List(2); |
| static Uint32List _uint32View = _list.buffer.asUint32List(); |
| |
| NativeFloat64x2(this.x, this.y) { |
| if (x is! num) throw ArgumentError(x); |
| if (y is! num) throw ArgumentError(y); |
| } |
| |
| NativeFloat64x2.splat(double v) : this(v, v); |
| |
| NativeFloat64x2.zero() : this.splat(0.0); |
| |
| NativeFloat64x2.fromFloat32x4(Float32x4 v) : this(v.x, v.y); |
| |
| /// Arguments [x] and [y] must be doubles. |
| NativeFloat64x2._doubles(this.x, this.y); |
| |
| String toString() => '[$x, $y]'; |
| |
| /// Addition operator. |
| Float64x2 operator +(Float64x2 other) { |
| return NativeFloat64x2._doubles(x + other.x, y + other.y); |
| } |
| |
| /// Negate operator. |
| Float64x2 operator -() { |
| return NativeFloat64x2._doubles(-x, -y); |
| } |
| |
| /// Subtraction operator. |
| Float64x2 operator -(Float64x2 other) { |
| return NativeFloat64x2._doubles(x - other.x, y - other.y); |
| } |
| |
| /// Multiplication operator. |
| Float64x2 operator *(Float64x2 other) { |
| return NativeFloat64x2._doubles(x * other.x, y * other.y); |
| } |
| |
| /// Division operator. |
| Float64x2 operator /(Float64x2 other) { |
| return NativeFloat64x2._doubles(x / other.x, y / other.y); |
| } |
| |
| /// Returns a copy of [this] each lane being scaled by [s]. |
| Float64x2 scale(double s) { |
| return NativeFloat64x2._doubles(x * s, y * s); |
| } |
| |
| /// Returns the absolute value of this [Float64x2]. |
| Float64x2 abs() { |
| return NativeFloat64x2._doubles(x.abs(), y.abs()); |
| } |
| |
| /// Clamps [this] to be in the range [lowerLimit]-[upperLimit]. |
| Float64x2 clamp(Float64x2 lowerLimit, Float64x2 upperLimit) { |
| double _lx = lowerLimit.x; |
| double _ly = lowerLimit.y; |
| double _ux = upperLimit.x; |
| double _uy = upperLimit.y; |
| double _x = x; |
| double _y = y; |
| // MAX(MIN(self, upper), lower). |
| _x = _x > _ux ? _ux : _x; |
| _y = _y > _uy ? _uy : _y; |
| _x = _x < _lx ? _lx : _x; |
| _y = _y < _ly ? _ly : _y; |
| return NativeFloat64x2._doubles(_x, _y); |
| } |
| |
| /// Extract the sign bits from each lane return them in the first 2 bits. |
| int get signMask { |
| var view = _uint32View; |
| _list[0] = x; |
| _list[1] = y; |
| var mx = (view[1] & 0x80000000) >> 31; |
| var my = (view[3] & 0x80000000) >> 31; |
| return mx | my << 1; |
| } |
| |
| /// Returns a new [Float64x2] copied from [this] with a new x value. |
| Float64x2 withX(double x) { |
| if (x is! num) throw ArgumentError(x); |
| return NativeFloat64x2._doubles(x, y); |
| } |
| |
| /// Returns a new [Float64x2] copied from [this] with a new y value. |
| Float64x2 withY(double y) { |
| if (y is! num) throw ArgumentError(y); |
| return NativeFloat64x2._doubles(x, y); |
| } |
| |
| /// Returns the lane-wise minimum value in [this] or [other]. |
| Float64x2 min(Float64x2 other) { |
| return NativeFloat64x2._doubles( |
| x < other.x ? x : other.x, y < other.y ? y : other.y); |
| } |
| |
| /// Returns the lane-wise maximum value in [this] or [other]. |
| Float64x2 max(Float64x2 other) { |
| return NativeFloat64x2._doubles( |
| x > other.x ? x : other.x, y > other.y ? y : other.y); |
| } |
| |
| /// Returns the lane-wise square root of [this]. |
| Float64x2 sqrt() { |
| return NativeFloat64x2._doubles(Math.sqrt(x), Math.sqrt(y)); |
| } |
| } |
| |
| /// Checks that the value is a Uint32. If not, it's not valid as an array |
| /// index or offset. Also ensures that the value is non-negative. |
| bool _isInvalidArrayIndex(int index) { |
| return (JS<bool>('!', '(# >>> 0 !== #)', index, index)); |
| } |
| |
| /// Checks that [index] is a valid index into [list] which has length [length]. |
| /// |
| /// That is, [index] is an integer in the range `0..length - 1`. |
| void _checkValidIndex(int index, List list, int length) { |
| if (_isInvalidArrayIndex(index) || JS<int>('!', '#', index) >= length) { |
| throw diagnoseIndexError(list, index); |
| } |
| } |
| |
| /// Checks that [start] and [end] form a range of a list of length [length]. |
| /// |
| /// That is: `start` and `end` are integers with `0 <= start <= end <= length`. |
| /// If `end` is `null` in which case it is considered to be `length` |
| /// |
| /// Returns the actual value of `end`, which is `length` if `end` is `null`, and |
| /// the original value of `end` otherwise. |
| int _checkValidRange(int start, int? end, int length) { |
| if (_isInvalidArrayIndex(start) || // Ensures start is non-negative int. |
| ((end == null) |
| ? start > length |
| : (_isInvalidArrayIndex(end) || start > end || end > length))) { |
| throw diagnoseRangeError(start, end, length); |
| } |
| if (end == null) return length; |
| return end; |
| } |