diff --git a/.gitignore b/.gitignore index ff18e04..e4de85d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ node_modules/ .psci_modules/ yarn-error.log yarn.lock +generated-docs/ diff --git a/bower.json b/bower.json index ad45920..f05f8d1 100644 --- a/bower.json +++ b/bower.json @@ -16,10 +16,17 @@ "purescript-arraybuffer-types": "^2.0.0", "purescript-maybe": "^4.0.0", "purescript-effect": "^2.0.0", - "purescript-uint": "^5.0.0" + "purescript-nullable": "^4.1.0", + "purescript-typelevel": "^4.0.0", + "purescript-parseint": "^1.1.0", + "purescript-uint": "^5.1.0", + "purescript-sized-vectors": "^3.1.0", + "purescript-float32": "^0.0.1" }, "devDependencies": { "purescript-debug": "^4.0.0", - "purescript-quickcheck": "^5.0.0" + "purescript-quickcheck": "^5.0.0", + "purescript-partial": "^2.0.0", + "purescript-quickcheck-combinators": "^0.1.0" } } diff --git a/src/Data/ArrayBuffer/ArrayBuffer.js b/src/Data/ArrayBuffer/ArrayBuffer.js index 695ed48..656d576 100644 --- a/src/Data/ArrayBuffer/ArrayBuffer.js +++ b/src/Data/ArrayBuffer/ArrayBuffer.js @@ -2,26 +2,14 @@ // module Data.ArrayBuffer.ArrayBuffer -exports.create = function(s) { - return function () { +exports.emptyImpl = function empty (s) { return new ArrayBuffer(s); - }; }; -exports.byteLength = function(a) { - return a.byteLength; +exports.byteLength = function byteLength (a) { + return a.byteLength; }; -exports.sliceImpl = function(s, e, a) { - return function () { - return a.slice(s, e); - }; -}; - -exports.fromArray = function(s) { - return (new Uint8Array(s)).buffer; -}; - -exports.fromIntArray = function(s) { - return (new Uint8Array(s)).buffer; +exports.sliceImpl = function sliceImpl (a, ms, me) { + return me === null ? (ms === null ? a.slice() : a.slice(ms)) : a.slice(ms,me); }; diff --git a/src/Data/ArrayBuffer/ArrayBuffer.purs b/src/Data/ArrayBuffer/ArrayBuffer.purs index 539a766..4bd11a0 100644 --- a/src/Data/ArrayBuffer/ArrayBuffer.purs +++ b/src/Data/ArrayBuffer/ArrayBuffer.purs @@ -1,29 +1,37 @@ -module Data.ArrayBuffer.ArrayBuffer ( create - , byteLength - , slice - , fromArray - , fromIntArray - ) where +-- | This module represents the functional bindings to JavaScript's `ArrayBuffer` +-- | objects. See [MDN's spec](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) for details. + +module Data.ArrayBuffer.ArrayBuffer + ( empty + , byteLength + , slice + ) where import Data.ArrayBuffer.Types (ArrayBuffer, ByteOffset, ByteLength) import Data.Function.Uncurried (Fn3, runFn3) +import Data.Maybe (Maybe(..)) +import Data.Nullable (Nullable, notNull, null) +import Data.Tuple (Tuple(..)) import Effect (Effect) +import Effect.Uncurried (EffectFn1, runEffectFn1) + + +foreign import emptyImpl :: EffectFn1 ByteLength ArrayBuffer -- | Create an `ArrayBuffer` with the given capacity. -foreign import create :: ByteLength -> Effect ArrayBuffer +empty :: ByteLength -> Effect ArrayBuffer +empty l = runEffectFn1 emptyImpl l -- | Represents the length of an `ArrayBuffer` in bytes. foreign import byteLength :: ArrayBuffer -> ByteLength -foreign import sliceImpl :: Fn3 ByteOffset ByteOffset ArrayBuffer (Effect ArrayBuffer) +foreign import sliceImpl :: Fn3 ArrayBuffer (Nullable ByteOffset) (Nullable ByteOffset) ArrayBuffer -- | Returns a new `ArrayBuffer` whose contents are a copy of this ArrayBuffer's bytes from begin, inclusive, up to end, exclusive. -slice :: ByteOffset -> ByteOffset -> ArrayBuffer -> Effect ArrayBuffer -slice = runFn3 sliceImpl - --- | Convert an array into an `ArrayBuffer` representation. -foreign import fromArray :: Array Number -> ArrayBuffer - --- | Convert an array into an `ArrayBuffer` representation. -foreign import fromIntArray :: Array Int -> ArrayBuffer \ No newline at end of file +slice :: ArrayBuffer -> Maybe (Tuple ByteOffset (Maybe ByteOffset)) -> ArrayBuffer +slice a mz = case mz of + Nothing -> runFn3 sliceImpl a null null + Just (Tuple s me) -> case me of + Nothing -> runFn3 sliceImpl a (notNull s) null + Just e -> runFn3 sliceImpl a (notNull s) (notNull e) diff --git a/src/Data/ArrayBuffer/ArrayBuffer/Gen.purs b/src/Data/ArrayBuffer/ArrayBuffer/Gen.purs new file mode 100644 index 0000000..dbe909a --- /dev/null +++ b/src/Data/ArrayBuffer/ArrayBuffer/Gen.purs @@ -0,0 +1,13 @@ +module Data.ArrayBuffer.ArrayBuffer.Gen where + +import Control.Monad.Gen.Class (class MonadGen) +import Data.ArrayBuffer.Typed (buffer) +import Data.ArrayBuffer.Typed.Gen (genTypedArray, genUint8) +import Data.ArrayBuffer.Types (ArrayBuffer, Uint8Array) +import Prelude ((<$>)) + + +genArrayBuffer :: forall m + . MonadGen m + => m ArrayBuffer +genArrayBuffer = buffer <$> (genTypedArray genUint8 :: m Uint8Array) diff --git a/src/Data/ArrayBuffer/DataView.js b/src/Data/ArrayBuffer/DataView.js index c1587c3..44ad1b9 100644 --- a/src/Data/ArrayBuffer/DataView.js +++ b/src/Data/ArrayBuffer/DataView.js @@ -3,43 +3,41 @@ // module Data.ArrayBuffer.DataView -exports.whole = function(b) { - return new DataView(b); -} - -exports.sliceImpl = function(just, nothing, s, l, b) { - return ((s + l)>>>0) <= b.byteLength ? just(new DataView(b, s, l)) : nothing; -} - -exports.buffer = function(v) { - return v.buffer; -} - -exports.byteOffset = function(v) { - return v.byteOffset; -} - -exports.byteLength = function(v) { - return v.byteLength; -} - -exports.getterImpl = function(just, nothing, s, l, e, v, o) { - return function() { - return ((o + l)>>>0) <= v.byteLength? just(v[s].call(v,o,e)) : nothing; - }; -} - -exports.setter = function(s) { - return function(e) { - return function(v) { - var f = v[s]; - return function(n) { - return function(o) { - return function() { - f.call(v,o,n,e); - }; - }; - }; - }; - }; -} +exports.whole = function whole (b) { + return new DataView(b); +}; + +exports.remainderImpl = function remainderImpl (b,i) { + return new DataView(b,i); +}; + +exports.partImpl = function partImpl (b,i,j) { + return new DataView(b,i,j); +}; + +exports.buffer = function buffer (v) { + return v.buffer; +}; + +exports.byteOffset = function byteOffset (v) { + return v.byteOffset; +}; + +exports.byteLength = function byteLength (v) { + return v.byteLength; +}; + +exports.getterImpl = function getterImpl (data, v, o) { + return ((o + data.bytesPerValue) >>> 0) <= v.byteLength + ? data.just (v[data.functionName].call(v,o,data.littleEndian)) + : data.nothing; +}; + +exports.setterImpl = function setterImpl (data, v, o, n) { + if (((o + data.bytesPerValue) >>> 0) <= v.byteLength) { + v[data.functionName].call(v,o,n,data.littleEndian); + return true; + } else { + return false; + } +}; diff --git a/src/Data/ArrayBuffer/DataView.purs b/src/Data/ArrayBuffer/DataView.purs index 31d99b9..cffe5ec 100644 --- a/src/Data/ArrayBuffer/DataView.purs +++ b/src/Data/ArrayBuffer/DataView.purs @@ -1,61 +1,82 @@ -module Data.ArrayBuffer.DataView( whole - , slice - , buffer - , byteOffset - , byteLength - , Getter() - , getInt8 - , getInt16be - , getInt32be - , getUint8 - , getUint16be - , getUint32be - , getFloat32be - , getFloat64be - , getInt16le - , getInt32le - , getUint16le - , getUint32le - , getFloat32le - , getFloat64le - , Setter() - , setInt8 - , setInt16be - , setInt32be - , setUint8 - , setUint16be - , setUint32be - , setFloat32be - , setFloat64be - , setInt16le - , setInt32le - , setUint16le - , setUint32le - , setFloat32le - , setFloat64le - ) where - -import Prelude -import Data.ArrayBuffer.Types (ByteOffset, DataView, ByteLength, ArrayBuffer) -import Data.Function.Uncurried (Fn5, Fn7, runFn5, runFn7) +-- | This module represents the functional bindings to JavaScript's `DataView` +-- | objects. See [MDN's spec](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView) for details. + +module Data.ArrayBuffer.DataView + ( AProxy (..) + , Endian (..) + , buffer + , byteLength + , byteOffset + , class DataView + , class ShowArrayViewType + , get + , getBE + , getFloat32be + , getFloat32le + , getFloat64be + , getFloat64le + , getInt16be + , getInt16le + , getInt32be + , getInt32le + , getInt8 + , getLE + , getUint16be + , getUint16le + , getUint32be + , getUint32le + , getUint8 + , part + , remainder + , set + , setBE + , setFloat32be + , setFloat32le + , setFloat64be + , setFloat64le + , setInt16be + , setInt16le + , setInt32be + , setInt32le + , setInt8 + , setLE + , setUint16be + , setUint16le + , setUint32be + , setUint32le + , setUint8 + , whole + ) where + +import Prelude (class Eq, (==), (<>)) + +import Data.ArrayBuffer.Types (ArrayBuffer, ByteLength, ByteOffset, DataView, Float32, Float64, Int16, Int32, Int8, Uint16, Uint32, Uint8, Uint8Clamped, kind ArrayViewType) +import Data.ArrayBuffer.ValueMapping (class BinaryValue, class BytesPerValue) import Data.Maybe (Maybe(..)) -import Effect (Effect) +import Data.Typelevel.Num (toInt', class Nat) import Data.UInt (UInt) +import Data.Float32 (Float32) as F +import Data.Symbol (SProxy (..), class IsSymbol, reflectSymbol) +import Effect (Effect) +import Effect.Uncurried (EffectFn2, EffectFn3, EffectFn4, runEffectFn2, runEffectFn3, runEffectFn4) +import Type.Proxy (Proxy(..)) --- | Type for all fetching functions. -type Getter r = DataView -> ByteOffset -> Effect (Maybe r) --- | Type for all storing functions. -type Setter r = DataView -> r -> ByteOffset -> Effect Unit -- | View mapping the whole `ArrayBuffer`. foreign import whole :: ArrayBuffer -> DataView -foreign import sliceImpl :: Fn5 (DataView -> Maybe DataView) (Maybe DataView) ByteOffset ByteLength ArrayBuffer (Maybe DataView) +foreign import remainderImpl :: EffectFn2 ArrayBuffer ByteOffset DataView + +-- | View mapping the rest of an `ArrayBuffer` after an index. +remainder :: ArrayBuffer -> ByteOffset -> Effect DataView +remainder a o = runEffectFn2 remainderImpl a o + +foreign import partImpl :: EffectFn3 ArrayBuffer ByteOffset ByteLength DataView -- | View mapping a region of the `ArrayBuffer`. -slice :: ByteOffset -> ByteLength -> ArrayBuffer -> (Maybe DataView) -slice = runFn5 sliceImpl Just Nothing +part :: ArrayBuffer -> ByteOffset -> ByteLength -> Effect DataView +part a o l = runEffectFn3 partImpl a o l -- | `ArrayBuffer` being mapped by the view. foreign import buffer :: DataView -> ArrayBuffer @@ -67,113 +88,256 @@ foreign import byteOffset :: DataView -> ByteOffset foreign import byteLength :: DataView -> ByteLength -type Endianness = Boolean - -foreign import getterImpl :: forall r. Fn7 (r -> Maybe r) (Maybe r) String ByteLength Endianness DataView ByteOffset (Effect (Maybe r)) - -getter :: forall r. String -> ByteLength -> Endianness -> DataView -> ByteOffset -> Effect (Maybe r) -getter = runFn7 getterImpl Just Nothing - - -foreign import setter :: forall r. String -> Endianness -> DataView -> r -> ByteOffset -> Effect Unit - +data AProxy (a :: ArrayViewType) = AProxy + +data Endian = LE | BE + +instance eqEndian :: Eq Endian where + eq LE LE = true + eq BE BE = true + eq _ _ = false + +class BinaryValue a t <= DataView (a :: ArrayViewType) t | a -> t + +instance dataViewUint8Clamped :: DataView Uint8Clamped UInt +instance dataViewUint32 :: DataView Uint32 UInt +instance dataViewUint16 :: DataView Uint16 UInt +instance dataViewUint8 :: DataView Uint8 UInt +instance dataViewInt32 :: DataView Int32 Int +instance dataViewInt16 :: DataView Int16 Int +instance dataViewInt8 :: DataView Int8 Int +instance dataViewFloat32 :: DataView Float32 F.Float32 +instance dataViewFloat64 :: DataView Float64 Number + + +class ShowArrayViewType (a :: ArrayViewType) (name :: Symbol) | a -> name +instance showArrayViewTypeUint8Clamped :: ShowArrayViewType Uint8Clamped "Uint8Clamped" +instance showArrayViewTypeViewUint32 :: ShowArrayViewType Uint32 "Uint32" +instance showArrayViewTypeViewUint16 :: ShowArrayViewType Uint16 "Uint16" +instance showArrayViewTypeViewUint8 :: ShowArrayViewType Uint8 "Uint8" +instance showArrayViewTypeViewInt32 :: ShowArrayViewType Int32 "Int32" +instance showArrayViewTypeViewInt16 :: ShowArrayViewType Int16 "Int16" +instance showArrayViewTypeViewInt8 :: ShowArrayViewType Int8 "Int8" +instance showArrayViewTypeViewFloat32 :: ShowArrayViewType Float32 "Float32" +instance showArrayViewTypeViewFloat64 :: ShowArrayViewType Float64 "Float64" + +foreign import getterImpl :: forall t + . EffectFn3 { just :: t -> Maybe t + , nothing :: Maybe t + , functionName :: String + , littleEndian :: Boolean + , bytesPerValue :: ByteLength + } DataView ByteOffset (Maybe t) + +getter :: forall t. + { functionName :: String + , bytesPerValue :: ByteLength + , littleEndian :: Boolean + } + -> DataView -> ByteOffset -> Effect (Maybe t) +getter data' d o = + runEffectFn3 getterImpl + { just: Just + , nothing: Nothing + , functionName: data'.functionName + , littleEndian: data'.littleEndian + , bytesPerValue: data'.bytesPerValue + } d o + + +get :: forall a name t b + . DataView a t + => BytesPerValue a b + => ShowArrayViewType a name + => IsSymbol name + => Nat b + => Endian -> AProxy a -> DataView -> ByteOffset -> Effect (Maybe t) +get endian prx = + let le = endian == LE + pnm = "get" <> reflectSymbol (SProxy :: SProxy name) + bpv = toInt' (Proxy :: Proxy b) + in getter { functionName: pnm + , bytesPerValue: bpv + , littleEndian: le + } + +getBE :: forall a name t b + . DataView a t + => BytesPerValue a b + => ShowArrayViewType a name + => IsSymbol name + => Nat b + => AProxy a -> DataView -> ByteOffset -> Effect (Maybe t) +getBE = get BE + +getLE :: forall a name t b + . DataView a t + => BytesPerValue a b + => ShowArrayViewType a name + => IsSymbol name + => Nat b + => AProxy a -> DataView -> ByteOffset -> Effect (Maybe t) +getLE = get LE + +foreign import setterImpl :: forall t + . EffectFn4 { functionName :: String + , littleEndian :: Boolean + , bytesPerValue :: ByteLength + } DataView ByteOffset t Boolean + +setter :: forall t. + { functionName :: String + , bytesPerValue :: ByteLength + , littleEndian :: Boolean + } -> DataView -> ByteOffset -> t -> Effect Boolean +setter d o t = runEffectFn4 setterImpl d o t + +set :: forall a name t b + . DataView a t + => BytesPerValue a b + => ShowArrayViewType a name + => IsSymbol name + => Nat b + => Endian -> AProxy a -> DataView -> ByteOffset -> t -> Effect Boolean +set endian prx = + let le = endian == LE + pnm = "set" <> reflectSymbol (SProxy :: SProxy name) + bpv = toInt' (Proxy :: Proxy b) + in setter { functionName: pnm + , bytesPerValue: bpv + , littleEndian: le + } -- | Fetch int8 value at a certain index in a `DataView`. -getInt8 :: Getter Int -getInt8 = getter "getInt8" 1 false +getInt8 :: DataView -> ByteOffset -> Effect (Maybe Int) +getInt8 = getLE (AProxy :: AProxy Int8) --- | Fetch int16 value at a certain index in a `DataView`. -getInt16be :: Getter Int -getInt16be = getter "getInt16" 2 false +-- | Fetch big-endian int16 value at a certain index in a `DataView`. +getInt16be :: DataView -> ByteOffset -> Effect (Maybe Int) +getInt16be = getBE (AProxy :: AProxy Int16) -getInt16le :: Getter Int -getInt16le = getter "getInt16" 2 true +-- | Fetch little-endian int16 value at a certain index in a `DataView`. +getInt16le :: DataView -> ByteOffset -> Effect (Maybe Int) +getInt16le = getLE (AProxy :: AProxy Int16) --- | Fetch int32 value at a certain index in a `DataView`. -getInt32be :: Getter Int -getInt32be = getter "getInt32" 4 false +-- | Fetch big-endian int32 value at a certain index in a `DataView`. +getInt32be :: DataView -> ByteOffset -> Effect (Maybe Int) +getInt32be = getBE (AProxy :: AProxy Int32) -getInt32le :: Getter Int -getInt32le = getter "getInt32" 4 true +-- | Fetch little-endian int32 value at a certain index in a `DataView`. +getInt32le :: DataView -> ByteOffset -> Effect (Maybe Int) +getInt32le = getLE (AProxy :: AProxy Int32) -- | Fetch uint8 value at a certain index in a `DataView`. -getUint8 :: Getter UInt -getUint8 = getter "getUint8" 1 false - --- | Fetch uint16 value at a certain index in a `DataView`. -getUint16be :: Getter UInt -getUint16be = getter "getUint16" 2 false - -getUint16le :: Getter UInt -getUint16le = getter "getUint16" 2 true - --- | Fetch uint32 value at a certain index in a `DataView`. -getUint32be :: Getter UInt -getUint32be = getter "getUint32" 4 false - -getUint32le :: Getter UInt -getUint32le = getter "getUint32" 4 true - --- | Fetch float32 value at a certain index in a `DataView`. -getFloat32be :: Getter Number -getFloat32be = getter "getFloat32" 4 false - -getFloat32le :: Getter Number -getFloat32le = getter "getFloat32" 4 true - --- | Fetch float64 value at a certain index in a `DataView`. -getFloat64be :: Getter Number -getFloat64be = getter "getFloat64" 8 false - -getFloat64le :: Getter Number -getFloat64le = getter "getFloat64" 8 true +getUint8 :: DataView -> ByteOffset -> Effect (Maybe UInt) +getUint8 = getLE (AProxy :: AProxy Uint8) + +-- | Fetch big-endian uint16 value at a certain index in a `DataView`. +getUint16be :: DataView -> ByteOffset -> Effect (Maybe UInt) +getUint16be = getBE (AProxy :: AProxy Uint16) + +-- | Fetch little-endian uint16 value at a certain index in a `DataView`. +getUint16le :: DataView -> ByteOffset -> Effect (Maybe UInt) +getUint16le = getLE (AProxy :: AProxy Uint16) + +-- | Fetch big-endian uint32 value at a certain index in a `DataView`. +getUint32be :: DataView -> ByteOffset -> Effect (Maybe UInt) +getUint32be = getBE (AProxy :: AProxy Uint32) + +-- | Fetch little-endian uint32 value at a certain index in a `DataView`. +getUint32le :: DataView -> ByteOffset -> Effect (Maybe UInt) +getUint32le = getLE (AProxy :: AProxy Uint32) + +-- | Fetch big-endian float32 value at a certain index in a `DataView`. +getFloat32be :: DataView -> ByteOffset -> Effect (Maybe F.Float32) +getFloat32be = getBE (AProxy :: AProxy Float32) + +-- | Fetch little-endian float32 value at a certain index in a `DataView`. +getFloat32le :: DataView -> ByteOffset -> Effect (Maybe F.Float32) +getFloat32le = getLE (AProxy :: AProxy Float32) + +-- | Fetch big-endian float64 value at a certain index in a `DataView`. +getFloat64be :: DataView -> ByteOffset -> Effect (Maybe Number) +getFloat64be = getBE (AProxy :: AProxy Float64) + +-- | Fetch little-endian float64 value at a certain index in a `DataView`. +getFloat64le :: DataView -> ByteOffset -> Effect (Maybe Number) +getFloat64le = getLE (AProxy :: AProxy Float64) + + +-- | Store big-endian value at a certain index in a `DataView`. +setBE :: forall a name t b + . DataView a t + => BytesPerValue a b + => ShowArrayViewType a name + => IsSymbol name + => Nat b + => AProxy a -> DataView -> ByteOffset -> t -> Effect Boolean +setBE = set BE + +-- | Store little-endian value at a certain index in a `DataView`. +setLE :: forall a name t b + . DataView a t + => BytesPerValue a b + => ShowArrayViewType a name + => IsSymbol name + => Nat b + => AProxy a -> DataView -> ByteOffset -> t -> Effect Boolean +setLE = set LE -- | Store int8 value at a certain index in a `DataView`. -setInt8 :: Setter Int -setInt8 = setter "setInt8" false +setInt8 :: DataView -> ByteOffset -> Int -> Effect Boolean +setInt8 = setLE (AProxy :: AProxy Int8) --- | Store int16 value at a certain index in a `DataView`. -setInt16be :: Setter Int -setInt16be = setter "setInt16" false +-- | Store big-endian int16 value at a certain index in a `DataView`. +setInt16be :: DataView -> ByteOffset -> Int -> Effect Boolean +setInt16be = setBE (AProxy :: AProxy Int16) -setInt16le :: Setter Int -setInt16le = setter "setInt16" true +-- | Store little-endian int16 value at a certain index in a `DataView`. +setInt16le :: DataView -> ByteOffset -> Int -> Effect Boolean +setInt16le = setLE (AProxy :: AProxy Int16) --- | Store int32 value at a certain index in a `DataView`. -setInt32be :: Setter Int -setInt32be = setter "setInt32" false +-- | Store big-endian int32 value at a certain index in a `DataView`. +setInt32be :: DataView -> ByteOffset -> Int -> Effect Boolean +setInt32be = setBE (AProxy :: AProxy Int32) -setInt32le :: Setter Int -setInt32le = setter "setInt32" true +-- | Store little-endian int32 value at a certain index in a `DataView`. +setInt32le :: DataView -> ByteOffset -> Int -> Effect Boolean +setInt32le = setLE (AProxy :: AProxy Int32) -- | Store uint8 value at a certain index in a `DataView`. -setUint8 :: Setter UInt -setUint8 = setter "setUint8" false +setUint8 :: DataView -> ByteOffset -> UInt -> Effect Boolean +setUint8 = setLE (AProxy :: AProxy Uint8) + --- | Store uint16 value at a certain index in a `DataView`. -setUint16be :: Setter UInt -setUint16be = setter "setUint16" false +-- | Store big-endian uint16 value at a certain index in a `DataView`. +setUint16be :: DataView -> ByteOffset -> UInt -> Effect Boolean +setUint16be = setBE (AProxy :: AProxy Uint16) -setUint16le :: Setter UInt -setUint16le = setter "setUint16" true +-- | Store little-endian uint16 value at a certain index in a `DataView`. +setUint16le :: DataView -> ByteOffset -> UInt -> Effect Boolean +setUint16le = setLE (AProxy :: AProxy Uint16) --- | Store uint32 value at a certain index in a `DataView`. -setUint32be :: Setter UInt -setUint32be = setter "setUint32" false +-- | Store big-endian uint32 value at a certain index in a `DataView`. +setUint32be :: DataView -> ByteOffset -> UInt -> Effect Boolean +setUint32be = setBE (AProxy :: AProxy Uint32) -setUint32le :: Setter UInt -setUint32le = setter "setUint32" true +-- | Store little-endian uint32 value at a certain index in a `DataView`. +setUint32le :: DataView -> ByteOffset -> UInt -> Effect Boolean +setUint32le = setLE (AProxy :: AProxy Uint32) --- | Store float32 value at a certain index in a `DataView`. -setFloat32be :: Setter Number -setFloat32be = setter "setFloat32" false +-- | Store big-endian float32 value at a certain index in a `DataView`. +setFloat32be :: DataView -> ByteOffset -> F.Float32 -> Effect Boolean +setFloat32be = setBE (AProxy :: AProxy Float32) -setFloat32le :: Setter Number -setFloat32le = setter "setFloat32" true +-- | Store little-endian float32 value at a certain index in a `DataView`. +setFloat32le :: DataView -> ByteOffset -> F.Float32 -> Effect Boolean +setFloat32le = setLE (AProxy :: AProxy Float32) --- | Store float64 value at a certain index in a `DataView`. -setFloat64be :: Setter Number -setFloat64be = setter "setFloat64" false +-- | Store big-endian float64 value at a certain index in a `DataView`. +setFloat64be :: DataView -> ByteOffset -> Number -> Effect Boolean +setFloat64be = setBE (AProxy :: AProxy Float64) -setFloat64le :: Setter Number -setFloat64le = setter "setFloat64" true +-- | Store little-endian float64 value at a certain index in a `DataView`. +setFloat64le :: DataView -> ByteOffset -> Number -> Effect Boolean +setFloat64le = setLE (AProxy :: AProxy Float64) diff --git a/src/Data/ArrayBuffer/DataView/Gen.purs b/src/Data/ArrayBuffer/DataView/Gen.purs new file mode 100644 index 0000000..3423d5f --- /dev/null +++ b/src/Data/ArrayBuffer/DataView/Gen.purs @@ -0,0 +1,51 @@ +module Data.ArrayBuffer.DataView.Gen where + +import Prelude ((<$>), bind, ($), (<=), (-), pure) + +import Control.Monad.Gen (suchThat) +import Control.Monad.Gen.Class (class MonadGen, chooseInt) +import Control.Monad.Rec.Class (class MonadRec) +import Data.ArrayBuffer.ArrayBuffer.Gen (genArrayBuffer) +import Data.ArrayBuffer.DataView (whole, byteLength, class DataView) +import Data.ArrayBuffer.Types (DataView, ByteOffset, kind ArrayViewType) +import Data.ArrayBuffer.ValueMapping (class BytesPerValue) +import Data.Maybe (Maybe(Just)) +import Data.Typelevel.Num (class Nat, toInt') +import Data.Unfoldable (replicateA) +import Data.Vec (Vec) +import Data.Vec (fromArray) as Vec +import Partial.Unsafe (unsafePartial) +import Type.Proxy (Proxy(..)) + + +genDataView :: forall m + . MonadGen m + => m DataView +genDataView = whole <$> genArrayBuffer + + + +-- | For generating some set of offsets residing inside the generated array, with some computable value +data WithOffsetAndValue n (a :: ArrayViewType) t = + WithOffsetAndValue (Vec n ByteOffset) t DataView + +genWithOffsetAndValue :: forall m n a b t + . MonadGen m + => MonadRec m + => Nat n + => BytesPerValue a b + => DataView a t + => Nat b + => m DataView -- ^ Assumes generated length is at least the minimum length of one value + -> m t + -> m (WithOffsetAndValue n a t) +genWithOffsetAndValue gen genT = do + let n = toInt' (Proxy :: Proxy n) + b = toInt' (Proxy :: Proxy b) + xs <- gen `suchThat` \xs -> b <= byteLength xs + let l = byteLength xs + mos <- replicateA n (chooseInt 0 (l - b)) + let os = unsafePartial $ case Vec.fromArray mos of + Just q -> q + t <- genT + pure (WithOffsetAndValue os t xs) diff --git a/src/Data/ArrayBuffer/Typed.js b/src/Data/ArrayBuffer/Typed.js index 5bda73f..fb56808 100644 --- a/src/Data/ArrayBuffer/Typed.js +++ b/src/Data/ArrayBuffer/Typed.js @@ -1,75 +1,185 @@ "use strict"; -// module Data.ArrayBuffer.Typed -exports.asInt8Array = function(v) { - return new Int8Array(v.buffer, v.byteOffset, v.byteLength); -} +exports.polyFill = function polyFill () { + var typedArrayTypes = + [ Int8Array, Uint8Array, Uint8ClampedArray, Int16Array + , Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array + ]; -exports.asInt16Array = function(v) { - return new Int16Array(v.buffer, v.byteOffset, v.byteLength >>> 1); -} + for (var k in typedArrayTypes) { + for (var v in Array.prototype) { + if (Array.prototype.hasOwnProperty(v) && !typedArrayTypes[k].prototype.hasOwnProperty(v)) + typedArrayTypes[k].prototype[v] = Array.prototype[v]; + } + } +}; -exports.asInt32Array = function(v) { - return new Int32Array(v.buffer, v.byteOffset, v.byteLength >>> 2); -} -exports.asUint8Array = function(v) { - return new Uint8Array(v.buffer, v.byteOffset, v.byteLength); -} +// module Data.ArrayBuffer.Typed -exports.asUint16Array = function(v) { - return new Uint16Array(v.buffer, v.byteOffset, v.byteLength >>> 1); -} +exports.buffer = function buffer (v) { + return v.buffer; +}; -exports.asUint32Array = function(v) { - return new Uint32Array(v.buffer, v.byteOffset, v.byteLength >>> 2); -} +exports.byteOffset = function byteOffset (v) { + return v.byteOffset; +}; -exports.asUint8ClampedArray = function(v) { - return new Uint8ClampedArray(v.buffer, v.byteOffset, v.byteLength); -} +exports.byteLength = function byteLength (v) { + return v.byteLength; +}; -exports.asFloat32Array = function(v) { - return new Float32Array(v.buffer, v.byteOffset, v.byteLength >>> 2); -} +exports.lengthImpl = function lemgthImpl (v) { + return v.length; +}; -exports.asFloat64Array = function(v) { - return new Float64Array(v.buffer, v.byteOffset, v.byteLength >>> 3); -} -exports.dataView = function(a) { - return new DataView(a.buffer); -} +// Typed Arrays -exports.setImpl = function(ra, off, a) { - return function() { - a.set(ra, off); + +function newArray (f) { + return function newArray_ (a,mb,mc) { + if (mb === null) + return new f(a); + var l = a.byteLength; + var eb = f.BYTES_PER_ELEMENT; + var off = Math.min(l, mb>>>0); + if (mc === null) + return new f(a,off); + var len = Math.min((l - off) / eb, mc); + return new f(a,off,len); }; } +exports.newUint8ClampedArray = newArray(Uint8ClampedArray); +exports.newUint32Array = newArray(Uint32Array); +exports.newUint16Array = newArray(Uint16Array); +exports.newUint8Array = newArray(Uint8Array); +exports.newInt32Array = newArray(Int32Array); +exports.newInt16Array = newArray(Int16Array); +exports.newInt8Array = newArray(Int8Array); +exports.newFloat32Array = newArray(Float32Array); +exports.newFloat64Array = newArray(Float64Array); + + +// ------ + +exports.everyImpl = function everyImpl (a,p) { + return a.every(p); +}; +exports.someImpl = function someImpl (a,p) { + return a.some(p); +}; + + +exports.fillImpl = function fillImpl (a,x,ms,me) { + return me === null ? (ms === null ? a.fill(x) : a.fill(x,ms)) : a.fill(x,ms,me); +}; + + +exports.mapImpl = function mapImpl (a,f) { + return a.map(f); +}; + +exports.forEachImpl = function forEachImpl (a,f) { + a.forEach(f); +}; + +exports.filterImpl = function filterImpl (a,p) { + return a.filter(p); +}; + +exports.includesImpl = function includesImpl (a,x,mo) { + return mo === null ? a.includes(x) : a.includes(x,mo); +}; + +exports.reduceImpl = function reduceImpl (a,f,i) { + return a.reduce(f,i); +}; +exports.reduce1Impl = function reduce1Impl (a,f) { + return a.reduce(f); +}; +exports.reduceRightImpl = function reduceRightImpl (a,f,i) { + return a.reduceRight(f,i); +}; +exports.reduceRight1Impl = function reduceRight1Impl (a,f) { + return a.reduceRight(f); +}; + +exports.findImpl = function findImpl (a,f) { + var x = a.find(f); + return (x === undefined) ? null : x; +}; +exports.findIndexImpl = function findIndexImpl (a,f) { + var x = a.findIndex(f); + return (x === -1) ? null : x; +}; +exports.indexOfImpl = function indexOfImpl (a,x,mo) { + var r = mo === null ? a.indexOf(x) : a.indexOf(x,mo); + return r === -1 ? null : r; +}; +exports.lastIndexOfImpl = function lastIndexOfImpl (a,x,mo) { + var r = mo === null ? a.lastIndexOf(x) : a.lastIndexOf(x,mo); + return r === -1 ? null : r; +}; + + + +exports.copyWithinImpl = function copyWithinImpl (a,t,s,me) { + if (me === null) { + a.copyWithin(t,s); + } else { + a.copyWithin(t,s,me); + } +}; + + +exports.reverseImpl = function reverseImpl (a) { + a.reverse(); +}; + + +exports.setImpl = function setImpl (a, off, b) { + a.set(b,off); +}; + + +exports.sliceImpl = function sliceImpl (a,ms,me) { + return me === null ? (ms === null ? a.slice() : a.slice(ms)) : a.slice(ms,me); +}; + + +exports.sortImpl = function sortImpl (a) { + a.sort(); +}; + + +exports.subArrayImpl = function subArrayImpl (a,ms,me) { + return me === null ? (ms === null ? a.subarray() : a.subarray(ms)) : a.subarray(ms,me); +}; + + +exports.toString = function toString (a) { + return a.toString(); +}; + +exports.joinImpl = function joinImpl (a,s) { + return a.join(s); +}; + exports.unsafeAtImpl = function(a, i) { - return function() { - return a[i]; - }; + return a[i]; } exports.hasIndexImpl = function(a, i) { - return i in a; -} - -exports.toArray = function(a) { - var l = a.length; - var ret = new Array(l); - for (var i = 0; i < l; i++) - ret[i] = a[i]; - return ret; + return i in a; } -exports.toIntArray = function(a) { - var l = a.length; - var ret = new Array(l); - for (var i = 0; i < l; i++) - ret[i] = a[i] | 0; - return ret; +exports.toArrayImpl = function(a) { + var l = a.length; + var ret = new Array(l); + for (var i = 0; i < l; i++) + ret[i] = a[i]; + return ret; } diff --git a/src/Data/ArrayBuffer/Typed.purs b/src/Data/ArrayBuffer/Typed.purs index fbfbdc5..c7e04f3 100644 --- a/src/Data/ArrayBuffer/Typed.purs +++ b/src/Data/ArrayBuffer/Typed.purs @@ -1,87 +1,431 @@ -module Data.ArrayBuffer.Typed( asInt8Array - , asInt16Array - , asInt32Array - , asUint8Array - , asUint16Array - , asUint32Array - , asUint8ClampedArray - , asFloat32Array - , asFloat64Array - , dataView - , set - , unsafeAt - , hasIndex - , at - , toArray - , toIntArray - ) where - -import Prelude +-- | This module represents the functional bindings to JavaScript's `TypedArray` and other +-- | objects. See [MDN's spec](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) for details. + +module Data.ArrayBuffer.Typed + ( polyFill + , Offset, Length, Range + , buffer, byteOffset, byteLength, length + , class TypedArray + , create, whole, remainder, part, empty, fromArray + , fill, set, setTyped, copyWithin + , map, traverse, traverse_, filter + , mapWithIndex, traverseWithIndex, traverseWithIndex_, filterWithIndex + , sort, reverse + , elem + , all, any + , allWithIndex, anyWithIndex + , unsafeAt, hasIndex, at, (!) + , foldlM, foldl1M, foldl, foldl1, foldrM, foldr1M, foldr, foldr1 + , find, findIndex, indexOf, lastIndexOf + , slice, subArray + , toString, toString', toArray + ) where + +import Prelude (Unit, (>=), (&&), (<<<), (<=), pure, (-), flip, (*), (*>)) + +import Data.Array (length) as A +import Data.ArrayBuffer.Types (ArrayView, kind ArrayViewType, ArrayBuffer, ByteOffset, ByteLength, Float64Array, Float32Array, Uint8ClampedArray, Uint32Array, Uint16Array, Uint8Array, Int32Array, Int16Array, Int8Array, Float64, Float32, Uint8Clamped, Uint32, Uint16, Uint8, Int32, Int16, Int8) +import Data.ArrayBuffer.ValueMapping (class BinaryValue, class BytesPerValue) +import Data.Function.Uncurried (Fn2, Fn3, mkFn2, runFn2, runFn3) +import Data.Maybe (Maybe(..), fromMaybe) +import Data.Nullable (Nullable, notNull, null, toMaybe, toNullable) +import Data.Tuple (Tuple(..)) +import Data.Typelevel.Num (class Nat, toInt') +import Data.UInt (UInt) +import Data.Float32 (Float32) as F import Effect (Effect) -import Data.ArrayBuffer.Types (ArrayView, ByteOffset, DataView, Float64Array, Float32Array, Uint8ClampedArray, Uint32Array, Uint16Array, Uint8Array, Int32Array, Int16Array, Int8Array) -import Data.Function.Uncurried (Fn2, Fn3, runFn2, runFn3) -import Data.Maybe (Maybe(..)) +import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn3, EffectFn4, mkEffectFn2, mkEffectFn3, runEffectFn1, runEffectFn2, runEffectFn3, runEffectFn4) +import Effect.Unsafe (unsafePerformEffect) +import Partial.Unsafe (unsafePartial) +import Type.Proxy (Proxy(..)) + + +-- | Lightweight polyfill for ie - see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray#Methods_Polyfill +foreign import polyFill :: Effect Unit + +-- | `ArrayBuffer` being mapped by the typed array. +foreign import buffer :: forall a. ArrayView a -> ArrayBuffer + +-- | Represents the offset of this view from the start of its `ArrayBuffer`. +foreign import byteOffset :: forall a. ArrayView a -> ByteOffset + +-- | Represents the length of this typed array, in bytes. +foreign import byteLength :: forall a. ArrayView a -> ByteLength + +foreign import lengthImpl :: forall a. ArrayView a -> Length + +length :: forall a. ArrayView a -> Length +length = lengthImpl + + +-- object creator implementations for each typed array + +foreign import newUint8ClampedArray :: forall a. Fn3 a (Nullable ByteOffset) (Nullable ByteLength) Uint8ClampedArray +foreign import newUint32Array :: forall a. Fn3 a (Nullable ByteOffset) (Nullable ByteLength) Uint32Array +foreign import newUint16Array :: forall a. Fn3 a (Nullable ByteOffset) (Nullable ByteLength) Uint16Array +foreign import newUint8Array :: forall a. Fn3 a (Nullable ByteOffset) (Nullable ByteLength) Uint8Array +foreign import newInt32Array :: forall a. Fn3 a (Nullable ByteOffset) (Nullable ByteLength) Int32Array +foreign import newInt16Array :: forall a. Fn3 a (Nullable ByteOffset) (Nullable ByteLength) Int16Array +foreign import newInt8Array :: forall a. Fn3 a (Nullable ByteOffset) (Nullable ByteLength) Int8Array +foreign import newFloat32Array :: forall a. Fn3 a (Nullable ByteOffset) (Nullable ByteLength) Float32Array +foreign import newFloat64Array :: forall a. Fn3 a (Nullable ByteOffset) (Nullable ByteLength) Float64Array + + +-- ---- + +foreign import everyImpl :: forall a b. Fn2 (ArrayView a) (Fn2 b Offset Boolean) Boolean +foreign import someImpl :: forall a b. Fn2 (ArrayView a) (Fn2 b Offset Boolean) Boolean + +foreign import fillImpl :: forall a b. EffectFn4 (ArrayView a) b (Nullable Offset) (Nullable Offset) Unit + +foreign import mapImpl :: forall a b. EffectFn2 (ArrayView a) (EffectFn2 b Offset b) (ArrayView a) +foreign import forEachImpl :: forall a b. EffectFn2 (ArrayView a) (EffectFn2 b Offset Unit) Unit +foreign import filterImpl :: forall a b. Fn2 (ArrayView a) (Fn2 b Offset Boolean) (ArrayView a) +foreign import includesImpl :: forall a b. Fn3 (ArrayView a) b (Nullable Offset) Boolean +foreign import reduceImpl :: forall a b c. EffectFn3 (ArrayView a) (EffectFn3 c b Offset c) c c +foreign import reduce1Impl :: forall a b. EffectFn2 (ArrayView a) (EffectFn3 b b Offset b) b +foreign import reduceRightImpl :: forall a b c. EffectFn3 (ArrayView a) (EffectFn3 c b Offset c) c c +foreign import reduceRight1Impl :: forall a b. EffectFn2 (ArrayView a) (EffectFn3 b b Offset b) b +foreign import findImpl :: forall a b. Fn2 (ArrayView a) (Fn2 b Offset Boolean) (Nullable b) +foreign import findIndexImpl :: forall a b. Fn2 (ArrayView a) (Fn2 b Offset Boolean) (Nullable Offset) +foreign import indexOfImpl :: forall a b. Fn3 (ArrayView a) b (Nullable Offset) (Nullable Offset) +foreign import lastIndexOfImpl :: forall a b. Fn3 (ArrayView a) b (Nullable Offset) (Nullable Offset) + + +-- | Value-oriented array offset +type Offset = Int +-- | Value-oriented array length +type Length = Int + +-- | Represents a range of indices, where if omitted, it represents the whole span. +-- | If only the second argument is omitted, then it represents the remainder of the span after the first index. +type Range = Maybe (Tuple Offset (Maybe Offset)) + --- | Create typed int8 array viewing the buffer mapped by the `DataView` -foreign import asInt8Array :: DataView -> Int8Array +-- TODO use purescript-quotient +-- | Typeclass that associates a measured user-level type with a typed array. +-- | +-- | #### Creation +-- | +-- | - `whole`, `remainder`, and `part` are methods for building a typed array accessible interface +-- | on top of an existing `ArrayBuffer` - Note, `part` and `remainder` may behave unintuitively - +-- | when the operation is isomorphic to `whole`, the new TypedArray uses the same buffer as the input, +-- | but not when the portion is a sub-array of the original buffer, a new one is made with +-- | `Data.ArrayBuffer.ArrayBuffer.slice`. +-- | - `empty` and `fromArray` are methods for creating pure typed arrays +-- | +-- | #### Modification +-- | +-- | - `fill`, `set`, and `setTyped` are methods for assigning values from external sources +-- | - `map` and `traverse` allow you to create a new array from the existing values in another +-- | - `copyWithin` allows you to set values to the array that exist in other parts of the array +-- | - `filter` creates a new array without the values that don't pass a predicate +-- | - `reverse` modifies an existing array in-place, with all values reversed +-- | - `sort` modifies an existing array in-place, with all values sorted +-- | +-- | #### Access +-- | +-- | - `elem`, `all`, and `any` are functions for testing the contents of an array +-- | - `unsafeAt`, `hasIndex`, and `at` are used to get values from an array, with an offset +-- | - `foldr`, `foldrM`, `foldr1`, `foldr1M`, `foldl`, `foldlM`, `foldl1`, `foldl1M` all can reduce an array +-- | - `find` and `findIndex` are searching functions via a predicate +-- | - `indexOf` and `lastIndexOf` are searching functions via equality +-- | - `slice` returns a new typed array on the same array buffer content as the input +-- | - `subArray` returns a new typed array with a separate array buffer +-- | - `toString` prints to a CSV, `toString'` allows you to supply the delimiter +-- | - `toArray` returns an array of numeric values +class BinaryValue a t <= TypedArray (a :: ArrayViewType) (t :: Type) | a -> t where + create :: forall x. Fn3 x (Nullable ByteOffset) (Nullable ByteLength) (ArrayView a) --- | Create typed int16 array viewing the buffer mapped by the `DataView` -foreign import asInt16Array :: DataView -> Int16Array +instance typedArrayUint8Clamped :: TypedArray Uint8Clamped UInt where + create = newUint8ClampedArray +instance typedArrayUint32 :: TypedArray Uint32 UInt where + create = newUint32Array +instance typedArrayUint16 :: TypedArray Uint16 UInt where + create = newUint16Array +instance typedArrayUint8 :: TypedArray Uint8 UInt where + create = newUint8Array +instance typedArrayInt32 :: TypedArray Int32 Int where + create = newInt32Array +instance typedArrayInt16 :: TypedArray Int16 Int where + create = newInt16Array +instance typedArrayInt8 :: TypedArray Int8 Int where + create = newInt8Array +instance typedArrayFloat32 :: TypedArray Float32 F.Float32 where + create = newFloat32Array +instance typedArrayFloat64 :: TypedArray Float64 Number where + create = newFloat64Array --- | Create typed int32 array viewing the buffer mapped by the `DataView` -foreign import asInt32Array :: DataView -> Int32Array +-- | View mapping the whole `ArrayBuffer`. +whole :: forall a t. TypedArray a t => ArrayBuffer -> ArrayView a +whole a = runFn3 create a null null --- | Create typed uint8 array viewing the buffer mapped by the `DataView` -foreign import asUint8Array :: DataView -> Uint8Array +-- | View mapping the rest of an `ArrayBuffer` after an index. +remainder :: forall a b t. TypedArray a t => Nat b => BytesPerValue a b => ArrayBuffer -> Offset -> ArrayView a +remainder a x = remainder' a o + where o = x * toInt' (Proxy :: Proxy b) --- | Create typed uint16 array viewing the buffer mapped by the `DataView` -foreign import asUint16Array :: DataView -> Uint16Array +remainder' :: forall a t. TypedArray a t => ArrayBuffer -> ByteOffset -> ArrayView a +remainder' a x = runFn3 create a (notNull x) null --- | Create typed uint32 array viewing the buffer mapped by the `DataView` -foreign import asUint32Array :: DataView -> Uint32Array +-- | View mapping a region of the `ArrayBuffer`. +part :: forall a b t. TypedArray a t => Nat b => BytesPerValue a b => ArrayBuffer -> Offset -> Length -> ArrayView a +part a x y = part' a o y + where o = x * toInt' (Proxy :: Proxy b) --- | Create typed uint8 clamped array viewing the buffer mapped by the `DataView` -foreign import asUint8ClampedArray :: DataView -> Uint8ClampedArray +part' :: forall a t. TypedArray a t => ArrayBuffer -> ByteOffset -> Length -> ArrayView a +part' a x y = runFn3 create a (notNull x) (notNull y) --- | Create typed float32 array viewing the buffer mapped by the `DataView` -foreign import asFloat32Array :: DataView -> Float32Array +-- | Creates an empty typed array, where each value is assigned 0 +empty :: forall a t. TypedArray a t => Length -> ArrayView a +empty n = runFn3 create n null null --- | Create typed float64 array viewing the buffer mapped by the `DataView` -foreign import asFloat64Array :: DataView -> Float64Array +-- | Creates a typed array from an input array of values, to be binary serialized +fromArray :: forall a t. TypedArray a t => Array t -> ArrayView a +fromArray a = runFn3 create a null null --- | Interpret typed array as a `DataView`. -foreign import dataView :: forall a. ArrayView a -> DataView +-- | Fill the array with a value +fill :: forall a t. TypedArray a t => ArrayView a -> t -> Range -> Effect Unit +fill a x mz = case mz of + Nothing -> runEffectFn4 fillImpl a x null null + Just (Tuple s me) -> runEffectFn4 fillImpl a x (notNull s) (toNullable me) -foreign import setImpl :: forall a. Fn3 (ArrayView a) ByteOffset (ArrayView a) (Effect Unit) +-- | Stores multiple values into the typed array +set :: forall a t. TypedArray a t => ArrayView a -> Maybe Offset -> Array t -> Effect Boolean +set = setInternal A.length --- | Stores multiple values in the last typed array, reading input values from ther first typed array. -set :: forall a. ArrayView a -> ByteOffset -> ArrayView a -> Effect Unit -set = runFn3 setImpl +ap1 :: forall a b c. (a -> c) -> (a -> b -> c) +ap1 f = \x _ -> f x -foreign import unsafeAtImpl :: forall a. Fn2 (ArrayView a) Int (Effect Number) + +-- | Maps a new value over the typed array, creating a new buffer and +-- | typed array as well. +map :: forall a t. TypedArray a t => (t -> t) -> ArrayView a -> ArrayView a +map = mapWithIndex' <<< ap1 + +-- | Apply a function to each element in an array, supplying a +-- | generated zero-based index integer along with the element, +-- | creating a typed array with the new elements +mapWithIndex :: forall a t. TypedArray a t => (Offset -> t -> t) -> ArrayView a -> ArrayView a +mapWithIndex = mapWithIndex' <<< flip + +mapWithIndex' :: forall a t. TypedArray a t => (t -> Offset -> t) -> ArrayView a -> ArrayView a +mapWithIndex' f a = unsafePerformEffect (runEffectFn2 mapImpl a (mkEffectFn2 (\x o -> pure (f x o)))) + +-- | Traverses over each value, returning a new one +traverse :: forall a t. TypedArray a t => (t -> Effect t) -> ArrayView a -> Effect (ArrayView a) +traverse = traverseWithIndex' <<< ap1 + +-- | Traverses over each value, returning a new one +traverseWithIndex :: forall a t. TypedArray a t => (Offset -> t -> Effect t) -> ArrayView a -> Effect (ArrayView a) +traverseWithIndex = traverseWithIndex' <<< flip + +traverseWithIndex' :: forall a t. TypedArray a t => (t -> Offset -> Effect t) -> ArrayView a -> Effect (ArrayView a) +traverseWithIndex' f a = runEffectFn2 mapImpl a (mkEffectFn2 f) + +-- | Traverses over each value +traverse_ :: forall a t. TypedArray a t => (t -> Effect Unit) -> ArrayView a -> Effect Unit +traverse_ = traverseWithIndex_' <<< ap1 + +-- | Traverses over each value +traverseWithIndex_ :: forall a t. TypedArray a t => (Offset -> t -> Effect Unit) -> ArrayView a -> Effect Unit +traverseWithIndex_ = traverseWithIndex_' <<< flip + +traverseWithIndex_' :: forall a t. TypedArray a t => (t -> Offset -> Effect Unit) -> ArrayView a -> Effect Unit +traverseWithIndex_' f a = runEffectFn2 forEachImpl a (mkEffectFn2 f) + +-- | Test a predicate to pass on all values +all :: forall a t. TypedArray a t => (t -> Boolean) -> ArrayView a -> Boolean +all = every <<< ap1 + +allWithIndex :: forall a t. TypedArray a t => (Offset -> t -> Boolean) -> ArrayView a -> Boolean +allWithIndex = every <<< flip + +every :: forall a t. TypedArray a t => (t -> Offset -> Boolean) -> ArrayView a -> Boolean +every p a = runFn2 everyImpl a (mkFn2 p) + +-- | Test a predicate to pass on any value +any :: forall a t. TypedArray a t => (t -> Boolean) -> ArrayView a -> Boolean +any = some <<< ap1 + +anyWithIndex :: forall a t. TypedArray a t => (Offset -> t -> Boolean) -> ArrayView a -> Boolean +anyWithIndex = some <<< flip + +some :: forall a t. TypedArray a t => (t -> Offset -> Boolean) -> ArrayView a -> Boolean +some p a = runFn2 someImpl a (mkFn2 p) + +-- | Returns a new typed array with all values that pass the predicate +filter :: forall a t. TypedArray a t => (t -> Boolean) -> ArrayView a -> ArrayView a +filter = filterWithIndex' <<< ap1 + +filterWithIndex :: forall a t. TypedArray a t => (Offset -> t -> Boolean) -> ArrayView a -> ArrayView a +filterWithIndex = filterWithIndex' <<< flip + +filterWithIndex' :: forall a t. TypedArray a t => (t -> Offset -> Boolean) -> ArrayView a -> ArrayView a +filterWithIndex' p a = runFn2 filterImpl a (mkFn2 p) + +-- | Tests if a value is an element of the typed array +elem :: forall a t. TypedArray a t => t -> Maybe Offset -> ArrayView a -> Boolean +elem x mo a = runFn3 includesImpl a x (toNullable mo) -- | Fetch element at index. -unsafeAt :: forall a. ArrayView a -> Int -> Effect Number -unsafeAt = runFn2 unsafeAtImpl +unsafeAt :: forall a t. TypedArray a t => Partial => ArrayView a -> Offset -> t +unsafeAt a o = runFn2 unsafeAtImpl a o + +-- | Folding from the left +foldlM :: forall a t b. TypedArray a t => (b -> t -> Offset -> Effect b) -> b -> ArrayView a -> Effect b +foldlM f i a = runEffectFn3 reduceImpl a (mkEffectFn3 f) i + +-- | Assumes the typed array is non-empty +foldl1M :: forall a t. TypedArray a t => (t -> t -> Offset -> Effect t) -> ArrayView a -> Effect t +foldl1M f a = runEffectFn2 reduce1Impl a (mkEffectFn3 f) -foreign import hasIndexImpl :: forall a. Fn2 (ArrayView a) Int Boolean +-- | Folding from the right +foldrM :: forall a t b. TypedArray a t => (t -> b -> Offset -> Effect b) -> b -> ArrayView a -> Effect b +foldrM f i a = runEffectFn3 reduceRightImpl a (mkEffectFn3 (\acc x o -> f x acc o)) i + +-- | Assumes the typed array is non-empty +foldr1M :: forall a t. TypedArray a t => (t -> t -> Offset -> Effect t) -> ArrayView a -> Effect t +foldr1M f a = runEffectFn2 reduceRight1Impl a (mkEffectFn3 (\acc x o -> f x acc o)) + +-- | Returns the first value satisfying the predicate +find :: forall a t. TypedArray a t => (t -> Boolean) -> ArrayView a -> Maybe t +find = findWithIndex' <<< ap1 + +findWithIndex :: forall a t. TypedArray a t => (Offset -> t -> Boolean) -> ArrayView a -> Maybe t +findWithIndex = findWithIndex' <<< flip + +findWithIndex' :: forall a t. TypedArray a t => (t -> Offset -> Boolean) -> ArrayView a -> Maybe t +findWithIndex' f a = toMaybe (runFn2 findImpl a (mkFn2 f)) + +-- | Returns the first index of the value satisfying the predicate +findIndex :: forall a t. TypedArray a t => (t -> Offset -> Boolean) -> ArrayView a -> Maybe Offset +findIndex f a = toMaybe (runFn2 findIndexImpl a (mkFn2 f)) + +-- | Returns the first index of the element, if it exists, from the left +indexOf :: forall a t. TypedArray a t => t -> Maybe Offset -> ArrayView a -> Maybe Offset +indexOf x mo a = toMaybe (runFn3 indexOfImpl a x (toNullable mo)) + +-- | Returns the first index of the element, if it exists, from the right +lastIndexOf :: forall a t. TypedArray a t => t -> Maybe Offset -> ArrayView a -> Maybe Offset +lastIndexOf x mo a = toMaybe (runFn3 lastIndexOfImpl a x (toNullable mo)) + +foldl :: forall a b t. TypedArray a t => (b -> t -> b) -> b -> ArrayView a -> b +foldl f = foldlWithIndex' (\a x _ -> f a x) + +foldlWithIndex :: forall a b t. TypedArray a t => (Offset -> b -> t -> b) -> b -> ArrayView a -> b +foldlWithIndex f = foldlWithIndex' (\a x o -> f o a x) + +foldlWithIndex' :: forall a b t. TypedArray a t => (b -> t -> Offset -> b) -> b -> ArrayView a -> b +foldlWithIndex' f i = unsafePerformEffect <<< foldlM (\a x o -> pure (f a x o)) i + +foldr :: forall a b t. TypedArray a t => (t -> b -> b) -> b -> ArrayView a -> b +foldr f = foldrWithIndex' (\a x _ -> f a x) + +foldrWithIndex :: forall a b t. TypedArray a t => (Offset -> t -> b -> b) -> b -> ArrayView a -> b +foldrWithIndex f = foldrWithIndex' (\a x o -> f o a x) + +foldrWithIndex' :: forall a b t. TypedArray a t => (t -> b -> Offset -> b) -> b -> ArrayView a -> b +foldrWithIndex' f i = unsafePerformEffect <<< foldrM (\x a o -> pure (f x a o)) i + +foldl1 :: forall a t. TypedArray a t => (t -> t -> t) -> ArrayView a -> t +foldl1 f = foldl1WithIndex (\_ a x -> f a x) + +foldl1WithIndex :: forall a t. TypedArray a t => (Offset -> t -> t -> t) -> ArrayView a -> t +foldl1WithIndex f = unsafePerformEffect <<< foldl1M (\acc x o -> pure (f o acc x)) + +foldr1 :: forall a t. TypedArray a t => (t -> t -> t) -> ArrayView a -> t +foldr1 f = foldr1WithIndex (\_ a x -> f a x) + +foldr1WithIndex :: forall a t. TypedArray a t => (Offset -> t -> t -> t) -> ArrayView a -> t +foldr1WithIndex f = unsafePerformEffect <<< foldr1M (\x a o -> pure (f o x a)) + +foreign import copyWithinImpl :: forall a. EffectFn4 (ArrayView a) Offset Offset (Nullable Offset) Unit + +-- | Internally copy values - see [MDN's spec](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/copyWithin) for details. +copyWithin :: forall a. ArrayView a -> Offset -> Offset -> Maybe Offset -> Effect Unit +copyWithin a t s me = runEffectFn4 copyWithinImpl a t s (toNullable me) + +foreign import reverseImpl :: forall a. EffectFn1 (ArrayView a) Unit + +-- | Reverses a typed array in-place. +reverse :: forall a. ArrayView a -> Effect Unit +reverse a = runEffectFn1 reverseImpl a + +foreign import setImpl :: forall a b. EffectFn3 (ArrayView a) Offset b Unit + +setInternal :: forall a b. (b -> Length) -> ArrayView a -> Maybe Offset -> b -> Effect Boolean +setInternal lenfn a mo b = + let o = fromMaybe 0 mo + in if o >= 0 && lenfn b <= length a - o + then runEffectFn3 setImpl a o b *> pure true + else pure false + + +-- | Stores multiple values in the typed array, reading input values from the second typed array. +setTyped :: forall a. ArrayView a -> Maybe Offset -> ArrayView a -> Effect Boolean +setTyped = setInternal length + + +-- | Copy the entire contents of the typed array into a new buffer. +foreign import sliceImpl :: forall a. Fn3 (ArrayView a) (Nullable Offset) (Nullable Offset) (ArrayView a) + +-- | Copy part of the contents of a typed array into a new buffer, between some start and end indices. +slice :: forall a. ArrayView a -> Range -> ArrayView a +slice a mz = case mz of + Nothing -> runFn3 sliceImpl a null null + Just (Tuple s me) -> runFn3 sliceImpl a (notNull s) (toNullable me) + +foreign import sortImpl :: forall a. EffectFn1 (ArrayView a) Unit + +-- | Sorts the values in-place +sort :: forall a. ArrayView a -> Effect Unit +sort a = runEffectFn1 sortImpl a + + +foreign import subArrayImpl :: forall a. Fn3 (ArrayView a) (Nullable Offset) (Nullable Offset) (ArrayView a) + +-- | Returns a new typed array view of the same buffer, beginning at the index and ending at the second. +-- | +-- | **Note**: there is really peculiar behavior with `subArray` - if the first offset argument is omitted, or +-- | is `0`, and likewise if the second argument is the length of the array, then the "sub-array" is actually a +-- | mutable replica of the original array - the sub-array reference reflects mutations to the original array. +-- | However, when the sub-array is is actually a smaller contiguous portion of the array, then it behaves +-- | purely, because JavaScript interally calls `Data.ArrayBuffer.ArrayBuffer.slice`. +subArray :: forall a. ArrayView a -> Range -> ArrayView a +subArray a mz = case mz of + Nothing -> runFn3 subArrayImpl a null null + Just (Tuple s me) -> runFn3 subArrayImpl a (notNull s) (toNullable me) + +-- | Prints array to a comma-separated string - see [MDN's spec](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/toString) for details. +foreign import toString :: forall a. ArrayView a -> String + +foreign import joinImpl :: forall a. Fn2 (ArrayView a) String String + +-- | Prints array to a delimiter-separated string - see [MDN's spec](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/join) for details. +toString' :: forall a. ArrayView a -> String -> String +toString' a s = runFn2 joinImpl a s + + +foreign import unsafeAtImpl :: forall a b. Fn2 (ArrayView a) Offset b + +foreign import hasIndexImpl :: forall a. Fn2 (ArrayView a) Offset Boolean -- | Determine if a certain index is valid. -hasIndex :: forall a. ArrayView a -> Int -> Boolean -hasIndex = runFn2 hasIndexImpl +hasIndex :: forall a. ArrayView a -> Offset -> Boolean +hasIndex a o = runFn2 hasIndexImpl a o -- | Fetch element at index. -at :: forall a. ArrayView a -> Int -> Effect (Maybe Number) -at a n = do - if a `hasIndex` n - then do - element <- unsafeAt a n - pure $ Just element - else - pure Nothing +at :: forall a t. TypedArray a t => ArrayView a -> Offset -> Maybe t +at a n = if a `hasIndex` n + then Just (unsafePartial (unsafeAt a n)) + else Nothing + +infixl 3 at as ! --- | Turn typed array into an array. -foreign import toArray :: forall a. ArrayView a -> Array Number --- | Turn typed array into integer array. -foreign import toIntArray :: forall a. ArrayView a -> Array Int +foreign import toArrayImpl :: forall a b. ArrayView a -> Array b + +-- | Turn typed array into an array. +toArray :: forall a t. TypedArray a t => ArrayView a -> Array t +toArray = toArrayImpl diff --git a/src/Data/ArrayBuffer/Typed/Gen.purs b/src/Data/ArrayBuffer/Typed/Gen.purs new file mode 100644 index 0000000..ffe3dd8 --- /dev/null +++ b/src/Data/ArrayBuffer/Typed/Gen.purs @@ -0,0 +1,76 @@ +-- | Functions for generating typed arrays and values. + +module Data.ArrayBuffer.Typed.Gen where + +import Prelude ((<$>), bind, (/), (-), negate, ($), bottom, pure, top) + +import Control.Monad.Gen.Class (class MonadGen, sized, chooseInt, chooseFloat) +import Data.ArrayBuffer.Typed (class TypedArray) +import Data.ArrayBuffer.Typed as TA +import Data.ArrayBuffer.Types (ArrayView) +import Data.Generic.Rep (class Generic) +import Data.Maybe (Maybe(..)) +import Data.Typelevel.Num (class Nat, toInt') +import Data.UInt (UInt) +import Data.UInt (fromInt) as UInt +import Data.Float32 (Float32, fromNumber) as F +import Data.UInt.Gen (genUInt) as UInt +import Data.Unfoldable (replicateA) +import Data.Vec (Vec) +import Data.Vec (fromArray) as Vec +import Partial.Unsafe (unsafePartial) +import Type.Proxy (Proxy(..)) + + +genTypedArray :: forall m a t + . MonadGen m + => TypedArray a t + => m t + -> m (ArrayView a) +genTypedArray gen = sized \s -> do + n <- chooseInt 0 s + a <- replicateA n gen + pure (TA.fromArray a) + +genUint8 :: forall m. MonadGen m => m UInt +genUint8 = UInt.fromInt <$> chooseInt 0 255 + +genInt8 :: forall m. MonadGen m => m Int +genInt8 = chooseInt (-128) 127 + +genUint16 :: forall m. MonadGen m => m UInt +genUint16 = UInt.fromInt <$> chooseInt 0 65535 + +genInt16 :: forall m. MonadGen m => m Int +genInt16 = chooseInt (-32768) 32767 + +genUint32 :: forall m. MonadGen m => m UInt +genUint32 = UInt.genUInt bottom top + +genInt32 :: forall m. MonadGen m => m Int +genInt32 = chooseInt bottom top + +genFloat32 :: forall m. MonadGen m => m F.Float32 +genFloat32 = F.fromNumber <$> chooseFloat (-3.40282347e+38) 3.40282347e+38 + +genFloat64 :: forall m. MonadGen m => m Number +genFloat64 = chooseFloat ((-1.7976931348623157e+308)/div) (1.7976931348623157e+308/div) + where div = 4.0 + +-- | For generating some set of offsets residing inside the generated array +data WithOffset n a = WithOffset (Vec n TA.Offset) (ArrayView a) +derive instance genericWithOffset :: Generic (ArrayView a) a' => Generic (WithOffset n a) _ + +genWithOffset :: forall m n a + . MonadGen m + => Nat n + => m (ArrayView a) + -> m (WithOffset n a) +genWithOffset gen = do + let n = toInt' (Proxy :: Proxy n) + xs <- gen + let l = TA.length xs + mos <- replicateA n (chooseInt 0 (l - 1)) + let os = unsafePartial $ case Vec.fromArray mos of + Just q -> q + pure (WithOffset os xs) diff --git a/src/Data/ArrayBuffer/ValueMapping.purs b/src/Data/ArrayBuffer/ValueMapping.purs new file mode 100644 index 0000000..c17d677 --- /dev/null +++ b/src/Data/ArrayBuffer/ValueMapping.purs @@ -0,0 +1,37 @@ +-- | This module represents type-level mappings between `ArrayViewType`s +-- | and meaningful data. + +module Data.ArrayBuffer.ValueMapping where + +import Data.ArrayBuffer.Types (kind ArrayViewType, Float64, Float32, Uint8Clamped, Uint32, Uint16, Uint8, Int32, Int16, Int8) +import Data.Typelevel.Num (D1, D2, D4, D8) +import Data.UInt (UInt) +import Data.Float32 (Float32) as F + + +-- | Maps a `TypedArray`'s binary casted value, to the space occupied by that value, in bytes. +class BytesPerValue (a :: ArrayViewType) (b :: Type) | a -> b + +instance bytesPerValueUint8Clamped :: BytesPerValue Uint8Clamped D1 +instance bytesPerValueUint32 :: BytesPerValue Uint32 D4 +instance bytesPerValueUint16 :: BytesPerValue Uint16 D2 +instance bytesPerValueUint8 :: BytesPerValue Uint8 D1 +instance bytesPerValueInt32 :: BytesPerValue Int32 D4 +instance bytesPerValueInt16 :: BytesPerValue Int16 D2 +instance bytesPerValueInt8 :: BytesPerValue Int8 D1 +instance bytesPerValueFloat32 :: BytesPerValue Float32 D4 +instance bytesPerValueFloat64 :: BytesPerValue Float64 D8 + + +-- | Maps a `TypedArray`'s binary casted value, to its computable representation in JavaScript. +class BinaryValue (a :: ArrayViewType) (t :: Type) | a -> t + +instance binaryValueUint8Clamped :: BinaryValue Uint8Clamped UInt +instance binaryValueUint32 :: BinaryValue Uint32 UInt +instance binaryValueUint16 :: BinaryValue Uint16 UInt +instance binaryValueUint8 :: BinaryValue Uint8 UInt +instance binaryValueInt32 :: BinaryValue Int32 Int +instance binaryValueInt16 :: BinaryValue Int16 Int +instance binaryValueInt8 :: BinaryValue Int8 Int +instance binaryValueFloat32 :: BinaryValue Float32 F.Float32 +instance binaryValueFloat64 :: BinaryValue Float64 Number diff --git a/test/Main.purs b/test/Main.purs index 017bc05..40066b3 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -3,75 +3,11 @@ module Test.Main where import Prelude import Effect (Effect) -import Data.ArrayBuffer.ArrayBuffer as AB -import Data.ArrayBuffer.DataView as DV -import Data.ArrayBuffer.Typed as TA -import Data.Maybe (Maybe(..), isNothing) -import Data.UInt (fromInt, pow) -import Test.QuickCheck (quickCheck', ()) - - -assertEffEquals :: forall a. Eq a => Show a => a -> Effect a -> Effect Unit -assertEffEquals expectedValue computation = do - actualValue <- computation - let msg = show expectedValue <> " /= " <> show actualValue - quickCheck' 1 $ actualValue == expectedValue msg - -assertEquals :: forall a. Eq a => Show a => a -> a -> Effect Unit -assertEquals expected actual = do - let msg = show expected <> " /= " <> show actual - quickCheck' 1 $ expected == actual msg +import Effect.Console (log) +import Test.Properties (propertiesTests) main :: Effect Unit main = do - ab4 <- AB.create 4 - ab8 <- AB.create 8 - assertEquals 4 $ AB.byteLength ab4 - assertEffEquals 2 $ pure <<< AB.byteLength =<< AB.slice 0 2 ab4 - assertEffEquals 2 $ pure <<< AB.byteLength =<< AB.slice 2 4 ab4 - assertEffEquals 0 $ pure <<< AB.byteLength =<< AB.slice (-2) (-2) ab4 - assertEffEquals 1 $ pure <<< AB.byteLength =<< (AB.slice (-2) (-1) ab4) - assertEquals Nothing $ DV.byteLength <$> DV.slice 0 200 ab4 - assertEquals (Just 2) $ DV.byteLength <$> DV.slice 0 2 ab4 - assertEquals (Just 2) $ DV.byteLength <$> DV.slice 2 2 ab4 - assertEquals 4 $ AB.byteLength $ AB.fromArray [1.0, 2.0, 3.0, 4.0] - assertEquals 4 $ AB.byteLength $ AB.fromIntArray [1, 2, 3, 4] - assertEquals 8 $ AB.byteLength $ DV.buffer $ DV.whole ab8 - assertEquals 8 $ AB.byteLength $ DV.buffer $ TA.dataView $ TA.asInt8Array $ DV.whole ab8 - - assertEquals (Just 8) $ DV.byteLength <$> DV.slice 0 8 ab8 - assertEquals true $ isNothing $ DV.slice 0 40 ab8 - - fourElementInt8Array <- pure <<< TA.asInt8Array <<< DV.whole $ AB.fromIntArray [1, 2, 3, 4] - assertEffEquals (Just 2.0) $ TA.at fourElementInt8Array 1 - assertEffEquals Nothing $ TA.at fourElementInt8Array 4 - assertEffEquals Nothing $ TA.at fourElementInt8Array (-1) - - assertEquals [1.0, 2.0, 3.0] $ TA.toArray <<< TA.asInt8Array <<< DV.whole $ AB.fromArray [1.0, 2.0, 3.0] - - twoElementDataView <- do - ab' <- AB.create 2 - let dv = DV.whole ab' - DV.setUint8 dv (fromInt 123) 0 - DV.setUint8 dv (fromInt 0) 1 - pure dv - assertEffEquals (Just $ fromInt 123) $ DV.getUint16le twoElementDataView 0 - assertEffEquals (Just $ fromInt 31488) $ DV.getUint16be twoElementDataView 0 - assertEffEquals (Just $ fromInt 2 `pow` fromInt 32 - fromInt 1) $ do - ab' <- AB.create 4 - let dv = DV.whole ab' - t = fromInt 255 - DV.setUint8 dv t 0 - DV.setUint8 dv t 1 - DV.setUint8 dv t 2 - DV.setUint8 dv t 3 - DV.getUint32be dv 0 - - let arr = DV.whole (AB.fromIntArray [0x4, 0x3, 0x2, 0x1]) - assertEffEquals (Just 0x04) (DV.getInt8 arr 0) - assertEffEquals (Just 0x04) (DV.getInt8 (TA.dataView (TA.asInt8Array arr)) 0) - assertEffEquals (Just 0x0304) (DV.getInt16le arr 0) - assertEffEquals (Just 0x0304) (DV.getInt16le (TA.dataView (TA.asInt16Array arr)) 0) - assertEffEquals (Just 0x01020304) (DV.getInt32le arr 0) - assertEffEquals (Just 0x01020304) (DV.getInt32le (TA.dataView (TA.asInt32Array arr)) 0) + log "Starting tests..." + propertiesTests diff --git a/test/Properties.purs b/test/Properties.purs new file mode 100644 index 0000000..77f0d14 --- /dev/null +++ b/test/Properties.purs @@ -0,0 +1,25 @@ +module Test.Properties where + +import Effect (Effect) +import Effect.Console (log) +import Effect.Ref (new, read) as Ref +import Prelude (Unit, bind, discard, ($), (<>), (*), show) +import Test.Properties.DataView (dataViewTests) +import Test.Properties.TypedArray (typedArrayTests) + + +propertiesTests :: Effect Unit +propertiesTests = do + do + count <- Ref.new 0 + log " - TypedArray Tests:" + typedArrayTests count + c <- Ref.read count + log $ " - Verified " <> show c <> " properties, generating " <> show (c * 9 * 100) <> " test cases." + + do + count <- Ref.new 0 + log " - DataView Tests:" + dataViewTests count + c <- Ref.read count + log $ " - Verified " <> show c <> " properties, generating " <> show (c * 16 * 100) <> " test cases." diff --git a/test/Properties/ArrayBuffer.purs b/test/Properties/ArrayBuffer.purs new file mode 100644 index 0000000..3be4160 --- /dev/null +++ b/test/Properties/ArrayBuffer.purs @@ -0,0 +1,2 @@ +module Test.Properties.ArrayBuffer where + diff --git a/test/Properties/DataView.purs b/test/Properties/DataView.purs new file mode 100644 index 0000000..3a13210 --- /dev/null +++ b/test/Properties/DataView.purs @@ -0,0 +1,110 @@ +module Test.Properties.DataView where + + +import Prelude + +import Data.ArrayBuffer.DataView as DV +import Data.ArrayBuffer.DataView.Gen (genDataView, genWithOffsetAndValue, WithOffsetAndValue(..)) +import Data.ArrayBuffer.Typed.Gen (genFloat32, genFloat64, genInt16, genInt32, genInt8, genUint16, genUint32, genUint8) +import Data.ArrayBuffer.Types (Float32, Float64, Int16, Int32, Int8, Uint16, Uint32, Uint8) +import Data.ArrayBuffer.ValueMapping (class BytesPerValue) +import Data.Maybe (Maybe(..)) +import Data.Typelevel.Num (class Nat, D1, D2, D4, D8) +import Data.UInt (UInt) +import Data.Float32 (Float32) as F +import Data.Vec (head) as Vec +import Data.Symbol (class IsSymbol) +import Effect (Effect) +import Effect.Console (log) +import Effect.Ref (Ref) +import Effect.Ref as Ref +import Effect.Unsafe (unsafePerformEffect) +import Test.QuickCheck (class Testable, quickCheckGen, Result, (===)) + + + +dataViewTests :: Ref Int -> Effect Unit +dataViewTests count = do + log " - setBE x o => getBE o === Just x" + placingAValueIsThereTests DV.BE count + log " - setLE x o => getLE o === Just x" + placingAValueIsThereTests DV.LE count + + +type TestableViewF a name b n t q = + Show t + => Eq t + => Ord t + => Semiring t + => BytesPerValue a b + => DV.ShowArrayViewType a name + => IsSymbol name + => Nat b + => DV.DataView a t + => WithOffsetAndValue n a t + -> q + + +overAll :: forall q n. Testable q => Nat n => Ref Int -> (forall a name b t. TestableViewF a name b n t q) -> Effect Unit +overAll count f = do + void (Ref.modify (\x -> x + 1) count) + log " - Uint32" + quickCheckGen $ + let f' :: TestableViewF Uint32 "Uint32" D4 n UInt q + f' = f + in f' <$> genWithOffsetAndValue genDataView genUint32 + + log " - Uint16" + quickCheckGen $ + let f' :: TestableViewF Uint16 "Uint16" D2 n UInt q + f' = f + in f' <$> genWithOffsetAndValue genDataView genUint16 + + log " - Uint8" + quickCheckGen $ + let f' :: TestableViewF Uint8 "Uint8" D1 n UInt q + f' = f + in f' <$> genWithOffsetAndValue genDataView genUint8 + + log " - Int32" + quickCheckGen $ + let f' :: TestableViewF Int32 "Int32" D4 n Int q + f' = f + in f' <$> genWithOffsetAndValue genDataView genInt32 + + log " - Int16" + quickCheckGen $ + let f' :: TestableViewF Int16 "Int16" D2 n Int q + f' = f + in f' <$> genWithOffsetAndValue genDataView genInt16 + + log " - Int8" + quickCheckGen $ + let f' :: TestableViewF Int8 "Int8" D1 n Int q + f' = f + in f' <$> genWithOffsetAndValue genDataView genInt8 + + log " - Float32" + quickCheckGen $ + let f' :: TestableViewF Float32 "Float32" D4 n F.Float32 q + f' = f + in f' <$> genWithOffsetAndValue genDataView genFloat32 + + log " - Float64" + quickCheckGen $ + let f' :: TestableViewF Float64 "Float64" D8 n Number q + f' = f + in f' <$> genWithOffsetAndValue genDataView genFloat64 + + +placingAValueIsThereTests :: DV.Endian -> Ref Int -> Effect Unit +placingAValueIsThereTests endian count = overAll count placingAValueIsThere + where + placingAValueIsThere :: forall a name b t. TestableViewF a name b D1 t Result + placingAValueIsThere (WithOffsetAndValue os t xs) = + let o = Vec.head os + prx = DV.AProxy :: DV.AProxy a + in unsafePerformEffect do + _ <- DV.set endian prx xs o t + my <- DV.get endian prx xs o + pure (my === Just t) diff --git a/test/Properties/TypedArray.purs b/test/Properties/TypedArray.purs new file mode 100644 index 0000000..e570952 --- /dev/null +++ b/test/Properties/TypedArray.purs @@ -0,0 +1,644 @@ +module Test.Properties.TypedArray where + + +import Prelude + +import Control.Monad.Gen (suchThat) +import Data.Array (drop, take) +import Data.Array as Array +import Data.ArrayBuffer.Typed (class TypedArray) +import Data.ArrayBuffer.Typed as TA +import Data.ArrayBuffer.Typed.Gen (WithOffset(..), genFloat32, genFloat64, genInt16, genInt32, genInt8, genTypedArray, genUint16, genUint32, genUint8, genWithOffset) +import Data.ArrayBuffer.Types (ArrayView, Float32Array, Float64Array, Int16Array, Int32Array, Int8Array, Uint16Array, Uint8Array, Uint8ClampedArray, Uint32Array) +import Data.ArrayBuffer.ValueMapping (class BytesPerValue) +import Data.Maybe (Maybe(..)) +import Data.Tuple (Tuple(..)) +import Data.Typelevel.Num (class Nat, D0, D1, D5, toInt') +import Data.Vec (head) as Vec +import Effect (Effect) +import Effect.Console (log) +import Effect.Ref (Ref) +import Effect.Ref as Ref +import Effect.Unsafe (unsafePerformEffect) +import Partial.Unsafe (unsafePartial) +import Test.QuickCheck (class Testable, Result(..), quickCheckGen, (/==), (), (===)) +import Test.QuickCheck.Combinators ((==>), (|=|)) +import Test.QuickCheck.Gen (Gen) +import Type.Proxy (Proxy(..)) + + +typedArrayTests :: Ref Int -> Effect Unit +typedArrayTests count = do + log " - partBehavesLikeTakeDrop" + partBehavesLikeTakeDrop count + log " - byteLength x / bytesPerValue === length x" + byteLengthDivBytesPerValueTests count + log " - fromArray (toArray x) === x" + fromArrayToArrayIsoTests count + log " - fill y x => all (== y) x" + allAreFilledTests count + log " - set x [y] o => (at x o == Just y)" + setSingletonIsEqTests count + log " - all p x => any p x" + allImpliesAnyTests count + log " - all p (filter p x)" + filterImpliesAllTests count + log " - filter (not . p) (filter p x) == []" + filterIsTotalTests count + log " - filter p (filter p x) == filter p x" + filterIsIdempotentTests count + log " - forall os `in` xs. all (\\o -> hasIndex o xs)" + withOffsetHasIndexTests count + log " - forall os `in` xs. all (\\o -> elem (at o xs) xs)" + withOffsetElemTests count + log " - any p x => p (find p x)" + anyImpliesFindTests count + log " - p (at x (findIndex p x))" + findIndexImpliesAtTests count + log " - at x (indexOf y x) == y" + indexOfImpliesAtTests count + log " - at x (lastIndexOf y x) == y" + lastIndexOfImpliesAtTests count + log " - foldr cons [] x == toArray x" + foldrConsIsToArrayTests count + log " - foldl snoc [] x == toArray x" + foldlSnocIsToArrayTests count + log " - map identity x == x" + mapIdentityIsIdentityTests count + log " - traverse snoc x == toArray x" + traverseSnocIsToArrayTests count + log " - reverse (reverse x) == x" + doubleReverseIsIdentityTests count + log " - toArray (reverse x) == Array.reverse (toArray x)" + reverseIsArrayReverseTests count + log " - sort (sort x) == sort x" + sortIsIdempotentTests count + log " - toArray (sort x) == Array.sort (toArray x)" + sortIsArraySortTests count + log " - toString' \",\" x == toString x" + toStringIsJoinWithCommaTests count + log " - setTyped x (subArray x) == x" + setTypedOfSubArrayIsIdentityTests count + log " - let z' = subArray x; q = toArray z'; mutate x; pure q /= toArray z'" + modifyingOriginalMutatesSubArrayTests count + log " - let z' = subArray x; q = toArray x; mutate z'; pure q /= toArray x" + modifyingSubArrayMutatesOriginalTests count + log " - let z' = subArray 0 x; q = toArray z'; mutate x; pure q /= toArray z'" + modifyingOriginalMutatesSubArrayZeroTests count + log " - let z' = subArray 0 x; q = toArray x; mutate z'; pure q /= toArray x" + modifyingSubArrayMutatesOriginalZeroTests count + log " - let z' = subArray 0 (length x) x; q = toArray z'; mutate x; pure q /= toArray z'" + modifyingOriginalMutatesSubArrayAllTests count + log " - let z' = subArray 0 (length x) x; q = toArray x; mutate z'; pure q /= toArray x" + modifyingSubArrayMutatesOriginalAllTests count + log " - let z' = subArray o x; q = toArray z'; mutate x; pure q == toArray z'" + modifyingOriginalDoesntMutateSubArrayPartTests count + log " - let z' = subArray 0 o x; q = toArray z'; mutate x; pure q == toArray z'" + modifyingOriginalDoesntMutateSubArrayPart2Tests count + log " - let z' = slice x; q = toArray z'; mutate x; pure q == toArray z'" + modifyingOriginalDoesntMutateSliceTests count + log " - let z' = slice o x; q = toArray z'; mutate x; pure q == toArray z'" + modifyingOriginalDoesntMutateSlicePartTests count + log " - let z' = slice 0 o x; q = toArray z'; mutate x; pure q == toArray z'" + modifyingOriginalDoesntMutateSlicePart2Tests count + log " - copyWithin x 0 0 (length x) == x" + copyWithinSelfIsIdentityTests count + log " - take (o + 1) (copyWithin o x) == slice o x" + copyWithinIsSliceTests count + log " - copyWithin o x == setTyped x (slice o x)" + copyWithinViaSetTypedTests count + + + +type TestableArrayF a b n t q = + Show t + => Eq t + => Ord t + => Semiring t + => TypedArray a t + => BytesPerValue a b + => Nat b + => WithOffset n a + -> q + + +overAll' :: forall q n. Testable q => Nat n => Int -> Ref Int -> (forall a b t. TestableArrayF a b n t q) -> Effect Unit +overAll' mn count f = do + void (Ref.modify (\x -> x + 1) count) + + let chk :: forall a b t. Show t => Eq t => Ord t => Semiring t => Nat b => BytesPerValue a b => TypedArray a t => String -> Proxy (ArrayView a) -> Gen t -> Effect Unit + chk s _ gen = do + log $ " - " <> s + quickCheckGen $ f <$> genWithOffset arr + where arr :: Gen (ArrayView a) + arr = genTypedArray gen `suchThat` \xs -> mn <= TA.length xs + + chk "Uint8ClampedArray" (Proxy :: Proxy Uint8ClampedArray) genUint8 + chk "Uint32Array" (Proxy :: Proxy Uint32Array) genUint32 + chk "Uint16Array" (Proxy :: Proxy Uint16Array) genUint16 + chk "Uint8Array" (Proxy :: Proxy Uint8Array) genUint8 + chk "Int32Array" (Proxy :: Proxy Int32Array) genInt32 + chk "Int16Array" (Proxy :: Proxy Int16Array) genInt16 + chk "Int8Array" (Proxy :: Proxy Int8Array) genInt8 + chk "Float32Array" (Proxy :: Proxy Float32Array) genFloat32 + chk "Float64Array" (Proxy :: Proxy Float64Array) genFloat64 + + +overAll :: forall q n. Testable q => Nat n => Ref Int -> (forall a b t. TestableArrayF a b n t q) -> Effect Unit +overAll count f = overAll' 0 count f + +overAll1 :: forall q n. Testable q => Nat n => Ref Int -> (forall a b t. TestableArrayF a b n t q) -> Effect Unit +overAll1 count f = overAll' 1 count f + +partBehavesLikeTakeDrop :: Ref Int -> Effect Unit +partBehavesLikeTakeDrop count = overAll count f + where + f :: forall a b t. TestableArrayF a b D0 t Result + f (WithOffset _ a) = + let n = 2 + na = TA.toArray a + ba = TA.buffer a + pa :: ArrayView a + pa = TA.part ba n n + in take n (drop n na) === TA.toArray pa + +byteLengthDivBytesPerValueTests :: Ref Int -> Effect Unit +byteLengthDivBytesPerValueTests count = overAll count byteLengthDivBytesPerValueEqLength + where + byteLengthDivBytesPerValueEqLength :: forall a b t. TestableArrayF a b D0 t Result + byteLengthDivBytesPerValueEqLength (WithOffset _ a) = + let b = toInt' (Proxy :: Proxy b) + in TA.length a === (TA.byteLength a `div` b) + +fromArrayToArrayIsoTests :: Ref Int -> Effect Unit +fromArrayToArrayIsoTests count = overAll count fromArrayToArrayIso + where + fromArrayToArrayIso :: forall a b t. TestableArrayF a b D0 t Result + fromArrayToArrayIso (WithOffset _ x) = + TA.toArray (TA.fromArray (TA.toArray x) :: ArrayView a) === TA.toArray x + + +allAreFilledTests :: Ref Int -> Effect Unit +allAreFilledTests count = overAll count allAreFilled + where + allAreFilled :: forall a b t. TestableArrayF a b D0 t Result + allAreFilled (WithOffset _ xs) = unsafePerformEffect do + let x = case TA.at xs 0 of + Nothing -> zero + Just y -> y + TA.fill xs x Nothing + let b = TA.all (\y -> y == x) xs + pure (b "All aren't the filled value") + + +setSingletonIsEqTests :: Ref Int -> Effect Unit +setSingletonIsEqTests count = overAll count setSingletonIsEq + where + setSingletonIsEq :: forall a b t. TestableArrayF a b D1 t Result + setSingletonIsEq (WithOffset os xs) = unsafePerformEffect do + case TA.at xs 0 of + Nothing -> pure Success + Just x -> do + _ <- TA.set xs (Just (Vec.head os)) [x] + pure (TA.at xs (Vec.head os) === Just x) + + +-- | Should work with any arbitrary predicate, but we can't generate them +allImpliesAnyTests :: Ref Int -> Effect Unit +allImpliesAnyTests count = overAll count allImpliesAny + where + allImpliesAny :: forall a b t. TestableArrayF a b D0 t Result + allImpliesAny (WithOffset _ xs) = + let pred x = x /= zero + all' = TA.all pred xs "All don't satisfy the predicate" + any' = TA.any pred xs "None satisfy the predicate" + in (TA.length xs === zero) |=| all' ==> any' + + +-- | Should work with any arbitrary predicate, but we can't generate them +filterImpliesAllTests :: Ref Int -> Effect Unit +filterImpliesAllTests count = overAll count filterImpliesAll + where + filterImpliesAll :: forall a b t. TestableArrayF a b D0 t Result + filterImpliesAll (WithOffset _ xs) = + let pred x = x /= zero + ys = TA.filter pred xs + all' = TA.all pred ys + in all' "Filter doesn't imply all" + + +-- | Should work with any arbitrary predicate, but we can't generate them +filterIsTotalTests :: Ref Int -> Effect Unit +filterIsTotalTests count = overAll count filterIsTotal + where + filterIsTotal :: forall a b t. TestableArrayF a b D0 t Result + filterIsTotal (WithOffset _ xs) = + let pred x = x /= zero + ys = TA.filter pred xs + zs = TA.filter (\x -> not pred x) ys + in TA.toArray zs === [] + + +-- | Should work with any arbitrary predicate, but we can't generate them +filterIsIdempotentTests :: Ref Int -> Effect Unit +filterIsIdempotentTests count = overAll count filterIsIdempotent + where + filterIsIdempotent :: forall a b t. TestableArrayF a b D0 t Result + filterIsIdempotent (WithOffset _ xs) = + let pred x = x /= zero + ys = TA.filter pred xs + zs = TA.filter pred ys + in TA.toArray zs === TA.toArray ys + + +withOffsetHasIndexTests :: Ref Int -> Effect Unit +withOffsetHasIndexTests count = overAll1 count withOffsetHasIndex + where + withOffsetHasIndex :: forall a b t. TestableArrayF a b D5 t Result + withOffsetHasIndex (WithOffset os xs) = + Array.all (\o -> TA.hasIndex xs o) os "All doesn't have index of itself" + + +withOffsetElemTests :: Ref Int -> Effect Unit +withOffsetElemTests count = overAll1 count withOffsetElem + where + withOffsetElem :: forall a b t. TestableArrayF a b D5 t Result + withOffsetElem (WithOffset os xs) = + Array.all (\o -> TA.elem (unsafePartial (TA.unsafeAt xs o)) Nothing xs) os + "All doesn't have an elem of itself" + + +-- | Should work with any arbitrary predicate, but we can't generate them +anyImpliesFindTests :: Ref Int -> Effect Unit +anyImpliesFindTests count = overAll count anyImpliesFind + where + anyImpliesFind :: forall a b t. TestableArrayF a b D0 t Result + anyImpliesFind (WithOffset _ xs) = + let pred x = x /= zero + p = TA.any pred xs "All don't satisfy the predicate" + q = + case TA.find pred xs of + Nothing -> Failed "Doesn't have a value satisfying the predicate" + Just z -> if pred z + then Success + else Failed "Found value doesn't satisfy the predicate" + in p ==> q + + +-- | Should work with any arbitrary predicate, but we can't generate them +findIndexImpliesAtTests :: Ref Int -> Effect Unit +findIndexImpliesAtTests count = overAll count findIndexImpliesAt + where + findIndexImpliesAt :: forall a b t. TestableArrayF a b D0 t Result + findIndexImpliesAt (WithOffset _ xs) = + let pred x o = x /= zero + mo = TA.findIndex pred xs + in case mo of + Nothing -> Success + Just o -> case TA.at xs o of + Nothing -> Failed "No value at found index" + Just x -> pred x o "Find index implies at" + + + +indexOfImpliesAtTests :: Ref Int -> Effect Unit +indexOfImpliesAtTests count = overAll count indexOfImpliesAt + where + indexOfImpliesAt :: forall a b t. TestableArrayF a b D0 t Result + indexOfImpliesAt (WithOffset _ xs) = + case TA.at xs 0 of + Nothing -> Success + Just y -> case TA.indexOf y Nothing xs of + Nothing -> Failed "no index of" + Just o -> TA.at xs o === Just y + + +lastIndexOfImpliesAtTests :: Ref Int -> Effect Unit +lastIndexOfImpliesAtTests count = overAll count lastIndexOfImpliesAt + where + lastIndexOfImpliesAt :: forall a b t. TestableArrayF a b D0 t Result + lastIndexOfImpliesAt (WithOffset _ xs) = + case TA.at xs 0 of + Nothing -> Success + Just y -> case TA.lastIndexOf y Nothing xs of + Nothing -> Failed "no lastIndex of" + Just o -> TA.at xs o === Just y + + +foldrConsIsToArrayTests :: Ref Int -> Effect Unit +foldrConsIsToArrayTests count = overAll count foldrConsIsToArray + where + foldrConsIsToArray :: forall a b t. TestableArrayF a b D0 t Result + foldrConsIsToArray (WithOffset _ xs) = + TA.foldr (\x acc -> Array.cons x acc) [] xs === TA.toArray xs + + +foldlSnocIsToArrayTests :: Ref Int -> Effect Unit +foldlSnocIsToArrayTests count = overAll count foldlSnocIsToArray + where + foldlSnocIsToArray :: forall a b t. TestableArrayF a b D0 t Result + foldlSnocIsToArray (WithOffset _ xs) = + TA.foldl (\acc x -> Array.snoc acc x) [] xs === TA.toArray xs + + +mapIdentityIsIdentityTests :: Ref Int -> Effect Unit +mapIdentityIsIdentityTests count = overAll count mapIdentityIsIdentity + where + mapIdentityIsIdentity :: forall a b t. TestableArrayF a b D0 t Result + mapIdentityIsIdentity (WithOffset _ xs) = + TA.toArray (TA.map identity xs) === TA.toArray xs + + +traverseSnocIsToArrayTests :: Ref Int -> Effect Unit +traverseSnocIsToArrayTests count = overAll count traverseSnocIsToArray + where + traverseSnocIsToArray :: forall a b t. TestableArrayF a b D0 t Result + traverseSnocIsToArray (WithOffset _ xs) = + let ys = unsafePerformEffect do + ref <- Ref.new [] + TA.traverse_ (\x -> void (Ref.modify (\xs' -> Array.snoc xs' x) ref)) xs + Ref.read ref + in TA.toArray xs === ys + + +doubleReverseIsIdentityTests :: Ref Int -> Effect Unit +doubleReverseIsIdentityTests count = overAll count doubleReverseIsIdentity + where + doubleReverseIsIdentity :: forall a b t. TestableArrayF a b D0 t Result + doubleReverseIsIdentity (WithOffset _ xs) = + let ys = TA.toArray xs + _ = unsafePerformEffect do + TA.reverse xs + TA.reverse xs + in TA.toArray xs === ys + + +reverseIsArrayReverseTests :: Ref Int -> Effect Unit +reverseIsArrayReverseTests count = overAll count reverseIsArrayReverse + where + reverseIsArrayReverse :: forall a b t. TestableArrayF a b D0 t Result + reverseIsArrayReverse (WithOffset _ xs) = + let ys = Array.reverse (TA.toArray xs) + _ = unsafePerformEffect do + TA.reverse xs + in TA.toArray xs === ys + + +sortIsIdempotentTests :: Ref Int -> Effect Unit +sortIsIdempotentTests count = overAll count sortIsIdempotent + where + sortIsIdempotent :: forall a b t. TestableArrayF a b D0 t Result + sortIsIdempotent (WithOffset _ xs) = + let ys = unsafePerformEffect do + TA.sort xs + pure (TA.toArray xs) + zs = unsafePerformEffect do + TA.sort xs + pure (TA.toArray xs) + in zs === ys + + +sortIsArraySortTests :: Ref Int -> Effect Unit +sortIsArraySortTests count = overAll count sortIsArraySort + where + sortIsArraySort :: forall a b t. TestableArrayF a b D0 t Result + sortIsArraySort (WithOffset _ xs) = + let ys = Array.sort (TA.toArray xs) + _ = unsafePerformEffect do + TA.sort xs + in TA.toArray xs === ys + + +toStringIsJoinWithCommaTests :: Ref Int -> Effect Unit +toStringIsJoinWithCommaTests count = overAll count toStringIsJoinWithComma + where + toStringIsJoinWithComma :: forall a b t. TestableArrayF a b D0 t Result + toStringIsJoinWithComma (WithOffset _ xs) = + TA.toString' xs "," === TA.toString xs + + +setTypedOfSubArrayIsIdentityTests :: Ref Int -> Effect Unit +setTypedOfSubArrayIsIdentityTests count = overAll count setTypedOfSubArrayIsIdentity + where + setTypedOfSubArrayIsIdentity :: forall a b t. TestableArrayF a b D0 t Result + setTypedOfSubArrayIsIdentity (WithOffset _ xs) = + let ys = TA.toArray xs + zsSub = TA.subArray xs Nothing + zs = unsafePerformEffect do + _ <- TA.setTyped xs Nothing zsSub + pure (TA.toArray xs) + in zs === ys + + +modifyingOriginalMutatesSubArrayTests :: Ref Int -> Effect Unit +modifyingOriginalMutatesSubArrayTests count = overAll count modifyingOriginalMutatesSubArray + where + modifyingOriginalMutatesSubArray :: forall a b t. TestableArrayF a b D0 t Result + modifyingOriginalMutatesSubArray (WithOffset _ xs) + | Array.all (eq zero) (TA.toArray xs) = Success + | otherwise = + let zsSub = TA.subArray xs Nothing + zs = TA.toArray zsSub + ys = unsafePerformEffect do + TA.fill xs zero Nothing + pure (TA.toArray zsSub) + in zs /== ys + + +modifyingSubArrayMutatesOriginalTests :: Ref Int -> Effect Unit +modifyingSubArrayMutatesOriginalTests count = overAll count modifyingOriginalMutatesSubArray + where + modifyingOriginalMutatesSubArray :: forall a b t. TestableArrayF a b D0 t Result + modifyingOriginalMutatesSubArray (WithOffset _ xs) + | Array.all (eq zero) (TA.toArray xs) = Success + | otherwise = + let zsSub = TA.subArray xs Nothing + zs = TA.toArray xs + ys = unsafePerformEffect do + TA.fill zsSub zero Nothing + pure (TA.toArray xs) + in zs /== ys + + +modifyingOriginalMutatesSubArrayZeroTests :: Ref Int -> Effect Unit +modifyingOriginalMutatesSubArrayZeroTests count = overAll count modifyingOriginalMutatesSubArrayZero + where + modifyingOriginalMutatesSubArrayZero :: forall a b t. TestableArrayF a b D0 t Result + modifyingOriginalMutatesSubArrayZero (WithOffset _ xs) + | Array.all (eq zero) (TA.toArray xs) = Success + | otherwise = + let zsSub = TA.subArray xs (Just (Tuple 0 Nothing)) + zs = TA.toArray zsSub + ys = unsafePerformEffect do + TA.fill xs zero Nothing + pure (TA.toArray zsSub) + in zs /== ys + + +modifyingSubArrayMutatesOriginalZeroTests :: Ref Int -> Effect Unit +modifyingSubArrayMutatesOriginalZeroTests count = overAll count modifyingSubArrayMutatesOriginalZero + where + modifyingSubArrayMutatesOriginalZero :: forall a b t. TestableArrayF a b D0 t Result + modifyingSubArrayMutatesOriginalZero (WithOffset _ xs) + | Array.all (eq zero) (TA.toArray xs) = Success + | otherwise = + let zsSub = TA.subArray xs (Just (Tuple 0 Nothing)) + zs = TA.toArray xs + ys = unsafePerformEffect do + TA.fill zsSub zero Nothing + pure (TA.toArray xs) + in zs /== ys + + +modifyingOriginalMutatesSubArrayAllTests :: Ref Int -> Effect Unit +modifyingOriginalMutatesSubArrayAllTests count = overAll count modifyingOriginalMutatesSubArrayAll + where + modifyingOriginalMutatesSubArrayAll :: forall a b t. TestableArrayF a b D0 t Result + modifyingOriginalMutatesSubArrayAll (WithOffset _ xs) + | Array.all (eq zero) (TA.toArray xs) = Success + | otherwise = + let zsSub = TA.subArray xs (Just (Tuple 0 (Just (TA.length xs)))) + zs = TA.toArray zsSub + ys = unsafePerformEffect do + TA.fill xs zero Nothing + pure (TA.toArray zsSub) + in zs /== ys + + +modifyingSubArrayMutatesOriginalAllTests :: Ref Int -> Effect Unit +modifyingSubArrayMutatesOriginalAllTests count = overAll count modifyingSubArrayMutatesOriginalAll + where + modifyingSubArrayMutatesOriginalAll :: forall a b t. TestableArrayF a b D0 t Result + modifyingSubArrayMutatesOriginalAll (WithOffset _ xs) + | Array.all (eq zero) (TA.toArray xs) = Success + | otherwise = + let zsSub = TA.subArray xs (Just (Tuple 0 (Just (TA.length xs)))) + zs = TA.toArray xs + ys = unsafePerformEffect do + TA.fill zsSub zero Nothing + pure (TA.toArray xs) + in zs /== ys + + +modifyingOriginalDoesntMutateSubArrayPartTests :: Ref Int -> Effect Unit +modifyingOriginalDoesntMutateSubArrayPartTests count = overAll count modifyingOriginalMutatesSubArrayPart + where + modifyingOriginalMutatesSubArrayPart :: forall a b t. TestableArrayF a b D1 t Result + modifyingOriginalMutatesSubArrayPart (WithOffset os xs) + | Vec.head os == 0 = Success + | Array.all (eq zero) (TA.toArray (TA.subArray xs Nothing)) = Success + | TA.at xs (Vec.head os) == Just zero = Success + | otherwise = + let o = Vec.head os + zsSub = TA.subArray xs (Just (Tuple o Nothing)) + zs = TA.toArray zsSub + ys = unsafePerformEffect do + TA.fill xs zero Nothing + pure (TA.toArray zsSub) + in zs === ys + + +modifyingOriginalDoesntMutateSubArrayPart2Tests :: Ref Int -> Effect Unit +modifyingOriginalDoesntMutateSubArrayPart2Tests count = overAll count modifyingOriginalMutatesSubArrayPart2 + where + modifyingOriginalMutatesSubArrayPart2 :: forall a b t. TestableArrayF a b D1 t Result + modifyingOriginalMutatesSubArrayPart2 (WithOffset os xs) + | Vec.head os == 0 = Success + | Array.all (eq zero) (TA.toArray (TA.subArray xs Nothing)) = Success + | TA.at xs (Vec.head os) == Just zero = Success + | otherwise = + let o = Vec.head os + zsSub = TA.subArray xs (Just (Tuple 0 (Just o))) + zs = TA.toArray zsSub + ys = unsafePerformEffect do + TA.fill xs zero Nothing + pure (TA.toArray zsSub) + in zs === ys + + +modifyingOriginalDoesntMutateSliceTests :: Ref Int -> Effect Unit +modifyingOriginalDoesntMutateSliceTests count = overAll count modifyingOriginalDoesntMutateSlice + where + modifyingOriginalDoesntMutateSlice :: forall a b t. TestableArrayF a b D0 t Result + modifyingOriginalDoesntMutateSlice (WithOffset _ xs) + | Array.all (eq zero) (TA.toArray xs) = Success + | otherwise = + let zsSub = TA.slice xs Nothing + zs = TA.toArray zsSub + ys = unsafePerformEffect do + TA.fill xs zero Nothing + pure (TA.toArray zsSub) + in zs === ys + + +modifyingOriginalDoesntMutateSlicePartTests :: Ref Int -> Effect Unit +modifyingOriginalDoesntMutateSlicePartTests count = overAll count modifyingOriginalDoesntMutateSlicePart + where + modifyingOriginalDoesntMutateSlicePart :: forall a b t. TestableArrayF a b D1 t Result + modifyingOriginalDoesntMutateSlicePart (WithOffset os xs) + | Array.all (eq zero) (TA.toArray (TA.slice xs (Just (Tuple (Vec.head os) Nothing)))) = Success + | TA.at xs (Vec.head os) == Just zero = Success + | otherwise = + let o = Vec.head os + zsSub = TA.slice xs (Just (Tuple o Nothing)) + zs = TA.toArray zsSub + ys = unsafePerformEffect do + TA.fill xs zero Nothing + pure (TA.toArray zsSub) + in zs === ys + + +modifyingOriginalDoesntMutateSlicePart2Tests :: Ref Int -> Effect Unit +modifyingOriginalDoesntMutateSlicePart2Tests count = overAll count modifyingOriginalDoesntMutateSlicePart2 + where + modifyingOriginalDoesntMutateSlicePart2 :: forall a b t. TestableArrayF a b D1 t Result + modifyingOriginalDoesntMutateSlicePart2 (WithOffset os xs) + | Array.all (eq zero) (TA.toArray (TA.slice xs (Just (Tuple (Vec.head os) Nothing)))) = Success + | TA.at xs (Vec.head os) == Just zero = Success + | otherwise = + let o = Vec.head os + zsSub = TA.slice xs (Just (Tuple 0 (Just o))) + zs = TA.toArray zsSub + ys = unsafePerformEffect do + TA.fill xs zero Nothing + pure (TA.toArray zsSub) + in zs === ys + + +copyWithinSelfIsIdentityTests :: Ref Int -> Effect Unit +copyWithinSelfIsIdentityTests count = overAll count copyWithinSelfIsIdentity + where + copyWithinSelfIsIdentity :: forall a b t. TestableArrayF a b D0 t Result + copyWithinSelfIsIdentity (WithOffset _ xs) = + let ys = TA.toArray xs + zs = unsafePerformEffect do + TA.copyWithin xs 0 0 (Just (TA.length xs)) + pure (TA.toArray xs) + in zs === ys + + +copyWithinIsSliceTests :: Ref Int -> Effect Unit +copyWithinIsSliceTests count = overAll count copyWithinIsSlice + where + copyWithinIsSlice :: forall a b t. TestableArrayF a b D1 t Result + copyWithinIsSlice (WithOffset os xs) = + let o = Vec.head os + ys = TA.toArray (TA.slice xs (Just (Tuple o Nothing))) + zs = unsafePerformEffect do + TA.copyWithin xs 0 o Nothing + pure $ Array.drop (Array.length ys) $ TA.toArray xs + in TA.toArray xs === ys <> zs + + +copyWithinViaSetTypedTests :: Ref Int -> Effect Unit +copyWithinViaSetTypedTests count = overAll count copyWithinViaSetTyped + where + copyWithinViaSetTyped :: forall a b t. TestableArrayF a b D1 t Result + copyWithinViaSetTyped (WithOffset os xs) = + let o = Vec.head os + xs' = TA.fromArray (TA.toArray xs) :: ArrayView a + _ = unsafePerformEffect do + let ys = TA.slice xs' (Just (Tuple o Nothing)) + _ <- TA.setTyped xs' Nothing ys + TA.copyWithin xs 0 o Nothing + in TA.toArray xs === TA.toArray xs'